How to avoid the .searchable from appearing and disappearing? - swiftui

I am having a lot of buggy behavior on .searchable, on iOS 16.1 (Xcode 14.1). As you can see in the screenshot below. When entering a view with a .searchable component it will overlap with the view in transition and then disappears.
I am trying to make the code as basic as possible.
struct ContentView: View {
var body: some View {
NavigationStack {
List {
NavigationLink {
Mailbox().navigationTitle("Inkomend")
} label: { Label("Inkomend", systemImage: "tray") }
NavigationLink {
Mailbox().navigationTitle("Verstuurd")
} label: { Label("Verstuurd", systemImage: "paperplane") }
NavigationLink {
Mailbox().navigationTitle("Prullenmand")
} label: { Label("Prullenmand", systemImage: "trash") }
}
.navigationTitle("Postbussen")
.refreshable {}
}
}
}
struct Mailbox: View {
#State private var searchQuery: String = ""
var body: some View {
List {
NavigationLink {
Text("Detail")
} label: {
VStack(alignment: .leading) {
Text("Apple").font(.headline)
Text("Verify your account.")
Text("Fijn dat je deze belangrijke stap neemt om je account te verifiëren.").lineLimit(2).foregroundColor(.secondary)
}
}
}
.searchable(text: $searchQuery)
}
}

Use NavigationView instead of NavigationStack.

Like Latin Bhuva said, the new NavigationStack is not intended to be used in this way, a NavigationView would be more correct in this case.
struct ContentView: View {
var body: some View {
NavigationView {
List {
NavigationLink {
Mailbox().navigationTitle("Inkomend")
} label: { Label("Inkomend", systemImage: "tray") }
NavigationLink {
Mailbox().navigationTitle("Verstuurd")
} label: { Label("Verstuurd", systemImage: "paperplane") }
NavigationLink {
Mailbox().navigationTitle("Prullenmand")
} label: { Label("Prullenmand", systemImage: "trash") }
}
.navigationTitle("Postbussen")
.refreshable {}
}
}
}

Related

how to hide Tabview when using ToolbarItem, preserving ToolbarItem by disappearing

I have two views embedded in a TabView and a third view activated by a ToolbarItem in a navigationStack.
problem 1)
When I tap on plus button I navigate to my addView, but I still can see the tabs at the bottom.
problem 2)
after many test I found that if put the tabView code in MainView Inside a NavigationStack, I solve problem 1) but each time I dismiss from a detailView from a row in ContentView, the navigation Item disappears.
the main view for the tabview
struct MainView: View {
var body: some View {
TabView {
ContentView()
.tabItem {
Label("List", systemImage: "list.dash")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gearshape.fill")
}
}
}
}
the ContentView (a list of lessons, navigationDestination goes to a detail view)
struct ContentView: View {
#Environment(\.managedObjectContext) var moc
#FetchRequest (sortDescriptors: [
SortDescriptor(\.lessonNuber, order: .reverse)
], predicate: nil) var lessons: FetchedResults<Lesson>
#State var showAddView = false
var body: some View {
NavigationStack {
VStack {
List {
ForEach(lessons, id: \.self) { lesson in
NavigationLink {
DetailView(lesson: lesson)
} label: {
HStack {
Text("\(lesson.lessonNuber)")
.font(.title)
Text( "\(lesson.un_notion)")
.font(.body)
}
}
}
}
// .background(
// NavigationLink(destination: AddView(), isActive: $showAddView) {
// AddView()
// }
// )
.navigationDestination(isPresented: $showAddView) {
AddView()
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button {
showAddView = true
} label: {
Label("Add Lesson", systemImage: "plus")
}
}
}
}
.padding()
}
}
}

How to switch between tabBar and toolbar swiftUI?

In the files App after you pressed the Select button the tabBar switch to the toolbar.
How can I do this with swiftUI?(Switch between tabBar and toolbar)
struct tabBar: View {
var body: some View {
TabView {
ContentView().tabItem {
Image(systemName: "paperplane.fill")
Text("tabBar")
}
}
.toolbar {
ToolbarItem(placement: .bottomBar, content: {
Button(action: {
}){
Text("toolBar1")
}
})
ToolbarItem(placement: .bottomBar, content: {
Button() {
} label: {
Text("toolBar2")
}
})
}
}
}
For iOS 16 and above, you can use the toolbar(_:for:) method to toggle between visible and hidden states. Also, don't forget to wrap the view in a NavigationView, or else, setting the visibility of the bottom toolbar won't work.
import SwiftUI
struct ContentView: View {
#State var shouldShowTabBar = false
var body: some View {
NavigationView {
TabView {
Group {
Button("Switch Between Tab Bar and Toolbar") {
shouldShowTabBar.toggle()
}
.tabItem {
Label("Tab 1", systemImage: "list.dash")
}
Text("Tab 2")
.tabItem {
Label("Tab 2", systemImage: "square.and.pencil")
}
}
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button {
} label: {
Text("Toolbar Button")
}
}
}
.toolbar(shouldShowTabBar ? .visible : .hidden, for: .tabBar)
.toolbar(shouldShowTabBar ? .hidden : .visible, for: .bottomBar)
}
}
}
}
If you have to support iOS 14 and 15, you can check every item if it should be visible and hide/show them one by one.
import SwiftUI
struct ContentView: View {
#State var shouldShowTabBar = false
var body: some View {
NavigationView {
TabView {
Group {
Button("Switch Between Tab Bar and Toolbar") {
shouldShowTabBar.toggle()
}
.tabItem {
if shouldShowTabBar {
Label("Tab 1", systemImage: "list.dash")
}
}
Text("Tab 2")
.tabItem {
if shouldShowTabBar {
Label("Tab 2", systemImage: "square.and.pencil")
}
}
}
.toolbar {
ToolbarItem(placement: .bottomBar) {
if !shouldShowTabBar {
Button {
} label: {
Text("Toolbar Button")
}
}
}
}
}
}
}
}

How to close Half sheet in this situation?

I want to close the half sheet and back to the root view In swiftUI when with navigationLink we go to the another view. dismiss() doesn't work and turn us back to the previous view not the root view.
struct ContentView: View {
#State var showFirstSheetView = false
var body: some View {
NavigationView {
VStack {
Text("half sheet")
.onTapGesture {
showFirstSheetView.toggle()
}
}
.navigationTitle("Root view")
.sheet(isPresented: $showFirstSheetView) {
halfSheet()
}
}
}
}
struct halfSheet : View {
#State var showSecondView = false
var body: some View {
NavigationView {
VStack {
}
.background(
NavigationLink(isActive: $showSecondView, destination: {
SecondView()
}, label: {
EmptyView()
})
)
.navigationTitle("First view")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems( trailing: Button(action: {
showSecondView.toggle()
}) {
Text("Next")
}
)
}
}
}
struct SecondView : View {
#Environment(\.dismiss) var dismiss
var body: some View {
VStack {
}
.navigationTitle("Second view")
.navigationBarTitleDisplayMode(.inline)
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: Button(action: {
dismiss()
}) {
HStack(spacing : 5) {
Image(systemName: "chevron.backward")
Text("Back")
}
}, trailing: Button(action: {
// How back to the root View?
// below code works but not compatible with ios 15 and gives a warning
// UIApplication.shared.windows.first?.rootViewController?.dismiss(animated: true)
}) {
Text("Done")
})
}
}

.confirmationDialog inside of .swipeActions does not work, iOS 15

With regards to iOS 15, Xcode 13; I am wondering if this is a bug, not properly implemented, or a planned non-functional feature...
With a list that has a .swipeActions that calls a .confirmationDialog the confirmation dialog does not show.
See example:
import SwiftUI
struct ContentView: View {
#State private var confirmDelete = false
var body: some View {
NavigationView {
List{
ForEach(1..<10) {_ in
Cell()
}
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
confirmDelete.toggle()
} label: {
Label("Delete", systemImage: "trash")
}
.confirmationDialog("Remove this?", isPresented: $confirmDelete) {
Button(role: .destructive) {
print("Removed!")
} label: {
Text("Yes, Remove this")
}
}
}
}
}
}
}
struct Cell: View {
var body: some View {
Text("Hello")
.padding()
}
}
Misconfiguration:
The view modifier .confirmationDialog needs to be added to the view that is outside of the .swipeActions view modifier. It works when configured properly as shown below:
import SwiftUI
struct ContentView: View {
#State private var confirmDelete = false
var body: some View {
NavigationView {
List{
ForEach(1..<10) {_ in
Cell()
}
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
confirmDelete.toggle()
} label: {
Label("Delete", systemImage: "trash")
}
}
//move outside the scope of the .swipeActions view modifier:
.confirmationDialog("Remove this?", isPresented: $confirmDelete) {
Button(role: .destructive) {
print("Removed!")
} label: {
Text("Yes, Remove this")
}
}
}
}
}
}
struct Cell: View {
var body: some View {
Text("Hello")
.padding()
}
}

SwiftUI - Form "styling" being applied to navigation destination view

In SwiftUI, whenever a NavigationLink is inside a form, the destination view also seems to "styled" as if it's also in the form. This makes sense for pickers and such, but I'm just wanting to display a list of messages for an internal app that includes a simple email client. See recreated example below.
import SwiftUI
struct BasicRootView: View {
var body: some View {
NavigationView {
VStack(alignment: .leading) {
MailboxListView()
Spacer()
}
}
}
}
struct FormRootView: View {
var body: some View {
NavigationView {
Form {
MailboxListView()
}
}
}
}
struct MessageListView: View {
var body: some View {
List(0..<30) { i in
Text("Message \(i)")
}
.navigationBarTitle("Mailbox", displayMode: .inline)
}
}
struct MailboxListView: View {
var body: some View {
Section(header: Text("account#domain.com").font(.headline)) {
NavigationLink(destination: MessageListView()) {
HStack {
Image(systemName: "tray")
.font(.body)
Text("Inbox")
}
}
NavigationLink(destination: MessageListView()) {
HStack {
Image(systemName: "paperplane")
.font(.body)
Text("Sent")
}
}
NavigationLink(destination: MessageListView()) {
HStack {
Image(systemName: "bin.xmark")
.font(.body)
Text("Spam")
}
}
NavigationLink(destination: MessageListView()) {
HStack {
Image(systemName: "trash")
.font(.body)
Text("Trash")
}
}
}
.navigationBarTitle("Accounts")
}
}
The BasicRootView above doesn't look as I want it to, but the destination of its links looks like a normal list. So when I try to put it in a form to get the layout and look I'm going for, the destination list now itself looks like it's in a form as well, an unnecessary gap between the start of the list and the nav bar.
^^ No good
^^ Good
^^ Good
^^ No Good
Any way to "shed" the form in a destination view?
Just define the listStyle for both of your lists:
struct FormRootView: View {
var body: some View {
NavigationView {
List {
MailboxListView()
} .listStyle(GroupedListStyle())
}
}
}
struct MessageListView: View {
var body: some View {
List(0..<30) { i in
Text("Message \(i)")
} .listStyle(PlainListStyle())
.navigationBarTitle("Mailbox", displayMode: .inline)
}
}