Previous to watchOS 9 you could present a sheet without any out of the box way to cancel or dismiss. However starting in watchOS 9 presenting a sheet also presents a cancel button in the top left of the navigation bar. How can I remove this and handle dismissing myself?
import SwiftUI
struct ContentView: View {
#State var isShowingSheet = false
var body: some View {
VStack {
Button("show sheet") {
isShowingSheet.toggle()
}
}
.sheet(isPresented: $isShowingSheet) {
Text("Sheet 1")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Same issue here, tried an empty view in a ToolBarItem but did not work.
I ended up using this:
NavigationView {
// Your stuff here
}
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
What you do is that you set an empty title, and you tell it to go at the same place as of the cancel button.
PS:it looks like #Kurt Lane was a bit faster in the comment. Credits to him.
Related
When trying to navigate back from a view using the environment Dismiss value while also focussing on an empty searchable modifier the view you navigated back to becomes unresponsive. This is due to an empty UIView blocking any interaction with the view as seen in this screenshot:
Empty UIView blocking view after navigating back
This only occurs when the searchbar is focussed and empty when trying to navigate back. When there's a value in the searchbar everything works:
GIF of the bug
Am I doing something wrong here?
Tested on Xcode 14.2 iPhone 14 Pro (iOS 16.0) simulator.
import SwiftUI
struct MainPage: View {
var body: some View {
if #available(iOS 16.0, *) {
NavigationStack {
Text("Main view")
NavigationLink(destination: DetailView()) {
Text("Click me")
}
}
}
}
}
struct DetailView: View {
#Environment(\.dismiss) private var dismiss
#State private var searchText = ""
var body: some View {
VStack {
Text("Detail view")
Button("Go back") {
dismiss()
}
}
.searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always))
}
}
This bug only seems to happen when using NavigationStack or NavigationView with a .navigationViewStyle(.stack). When using NavigationView without a navigationViewStyle it seems to work fine. Currently I can work around this using the latter but I would prefer to use NavigationStack as NavigationView has become deprecated since iOS 16.0.
Any help is appreciated.
[Xcode 14.1, iOS 16.1]
I have a NavigationStack with a navigationTitle and a TabView with 2 Views. Each View has a ScrollView (see image below):
NavigationStack and TabView problem image
When I tap on Tab1 (#1 in red on the image above), then swipe up, the behavior is as expected (#2), i.e. the big navigationTitle move to the center, and my view passes below and becomes blurry. Perfect.
However, when I tap ton Tab2 (#3) and then swipe up (#4), the big title stays big, and the view doesn't become blurry.
Then I tap on Tab1 again (#5) and it works as expected.
Please help!
Here is my code:
ContentView:
import SwiftUI
struct ContentView: View {
#State private var selection: Tab = .tab1
enum Tab {
case tab1
case tab2
}
#State private var mainTitle = "Tab1"
var body: some View {
NavigationStack {
TabView(selection: $selection) {
Tab1(mainTitle: $mainTitle)
.tabItem {
Label("Tab1", systemImage: "wrench.adjustable.fill")
}
.tag(Tab.tab1)
Tab2(mainTitle: $mainTitle)
.tabItem {
Label("Tab2", systemImage: "wrench.adjustable.fill")
}
.tag(Tab.tab2)
} .navigationTitle(mainTitle)
}
}
}
Tab1:
import SwiftUI
struct Tab1: View {
#Binding var mainTitle : String
var body: some View {
ScrollView {
Text("Text tab 1")
.padding(.all,100)
.background(.blue)
} .onAppear {
mainTitle = "Tab1"
}
}
}
Tab2:
import SwiftUI
struct Tab2: View {
#Binding var mainTitle : String
var body: some View {
ScrollView {
Text("Text tab 2")
.padding(.all,100)
.background(.green)
} .onAppear {
mainTitle = "Tab2"
}
}
}
I tried a hack that is supposed to fix the transparency bug for Tab bars, but it doesn't work.
.onAppear {
let tabBarAppearance = UITabBarAppearance()
tabBarAppearance.configureWithOpaqueBackground()
UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance
}
TabViews are designed to sit at the top of the navigation hierarchy. They're intended to allow users to switch between independent sections of your app at any time.
You would generally put a separate navigation stack within each tab that then handles pushing and popping of views. And then, you can use the navigationTitle modifier to manage the screen's title.
So your structure (which might be split over multiple custom views) should look something like:
TabView {
NavigationStack {
ScrollView {
}
.navigationTitle("Tab 1")
}
.tabItem { Label("Tab1", ...) }
NavigationStack {
ScrollView {
}
.navigationTitle("Tab 2")
}
.tabItem { Label("Tab2", ...) }
}
This structure is by design, to align with Apple's Human Interface Guidelines. It's worth reading the HIG to get a handle on where Apple are coming from, and how working on the same principles can really help your app feel like it belongs on your users' device.
I have an app which presents a sheet. It works for the first time but when I click on it again it does not work. I am making isPresented false when you dismiss a sheet but when I tap on the Filter button again, it does not show the sheet.
ContentView
struct ContentView: View {
#State private var isPresented: Bool = false
var body: some View {
NavigationView {
List(1...20, id: \.self) { index in
Text("\(index)")
}.listStyle(.plain)
.navigationTitle("Hotels")
.toolbar {
Button("Filters") {
isPresented = true
}
}
.sheet(isPresented: $isPresented) {
isPresented = false
} content: {
FilterView()
}
}
}
}
FilterView:
import SwiftUI
struct FilterView: View {
#Environment(\.presentationMode) private var presentationMode
var body: some View {
ZStack {
Text("FilterView")
Button {
// action
presentationMode.wrappedValue.dismiss()
} label: {
Text("Dismiss")
}
}
}
}
struct FilterView_Previews: PreviewProvider {
static var previews: some View {
FilterView()
}
}
A couple of things to note from my experience.
Firstly, when using the isPresented binding to show a sheet, you don't need to reset the bound value in a custom onDismiss handler to reset it to false - that's handled for you internally by SwiftUI as part of the dismiss action.
So your modifier can be simplified a little:
.sheet(isPresented: $isPresented) {
FilterView()
}
Secondly, when running an app in the Simulator I've noticed that when you come back to the main view after dismissing a sheet you have to interact with the app somehow before clicking on the toolbar button, or the action won't trigger.
In cases like this, just scrolling the list up or down a little would be enough, and then the toolbar button works as you'd expect.
I've not encountered the same thing while running apps on a physical device – whether that's because the bug isn't present, or just that it's a lot easier to interact with the app in some microscopic form of gesture, I couldn't say.
NavigationView nests TabView, I have a List, and push to the next page
When the application returns to the background and returns to the active state, the push page automatically pops up.
If TabView nests NavigationView, there will be no problem, but I want NavigationView to nest TabView, is there any way to solve it
struct ContentView: View {
var body: some View {
NavigationView {
TabView {
List {
ForEach(0..<30) { index in
RowView(index: index)
}
}
}
}
}
}
struct RowView: View {
var index: Int
#State var userViewActive: Int?
var body: some View {
NavigationLink {
Text("Hello, world!")
} label: {
Text("Hello, world!")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
In a tabview each tab should manage its own view state. Aka
Every tab should have it's own navigation view. If you add this, you will see that your problem is solved.
See attached:
Squished Toggle in SwiftUI
... The code for this centered toggle is simply:
#State var rememberMe: Bool = false
HStack(alignment: .center) {
Spacer()
Toggle(
"Remember Me",
isOn: $rememberMe
)
Spacer()
}
Obviously, there's no need for ellipsis here, but by eliminating the Spacer() on each side, it'll grow to fill the width.
How do you center the Toggle without squishing its text? 🤔
fixedSize() seems to fix the issue on my end. We don't even need a HStack:
import SwiftUI
struct ContentView: View {
#State var rememberMe: Bool = false
var body: some View {
Toggle(
"Remember Me",
isOn: $rememberMe
).fixedSize()
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
Result
From Apple:
fixedSize() Fixes this view at its ideal size.