How to display missing back button in Master-Detail1-Detail2 view in SwiftUI in landscape? - ios

I'm using a NavigationView and NavigationLinks to move from a MasterView to a Detail1View and then further to a Detail2View.
On an iPhone in portrait mode, the back button is displayed for all detail views and allows to move back from Detail2View to Detail1View, and then further to MasterView.
But on an iPhone in landscape mode or on an iPad, when moving from Detail1View to Detail2View, there is no back button. And it is thus impossible to go back to Detail1View.
Adding a 2nd NavigationView allows to have a back button, but it's not really a desirable workaround, as the 2nd UINavigationViewController is shown below the 1st one.
struct MyMasterView: View {
private let names = ["Homer", "Marge", "Bart", "Lisa"]
var body: some View {
List() {
Section(header: Text("The Simpsons")) {
ForEach(names, id: \.self) { name in
NavigationLink(destination: MyDetail1View(name: name)) {
Text(name)
}
}
}
}
.navigationBarTitle(Text("My App"))
}
}
struct MyDetail1View: View {
var name: String
var body: some View {
//NavigationView {
List {
NavigationLink(destination: MyDetail2View(name: name)) {
Text("Hello \(name)")
}
}
.navigationBarTitle(Text("Details 1"))
//}
}
}
struct MyDetail2View: View {
var name: String
var body: some View {
HStack {
Text("\(name), I'm sorry but in Landscape there is no back button...")
}
.navigationBarTitle(Text("Details 2"))
}
}
struct ContentView : View {
var body: some View {
NavigationView {
MyMasterView()
Text("In Landscape, swipe to start...")
}
}
}

The answer turns out to be as simple as using isDetailLink()!
NavigationLink(destination: MyDetail2View(name: name)) {
Text("Hello \(name)")
}.isDetailLink(false)
Credits to https://stackoverflow.com/a/57400873/2893408

Related

Multiple back buttons created when navigates to root screen in SwiftUI

say I create 3 screens in SwiftUI which contains a NavigationLink to the next screen. like, first screen navigates to 2nd screen. 2nd screen navigates to third. and the third screen navigates to the first screen. In this case even if I use NavigationView only once(in the first screen). I encountered that there's a back button forming when I navigate to 1st screen from the third screen. And it keeps adding up when I start to navigate from then on. I have tried to use .navigationBarBackButtonHidden(true). It hides it but the space taken by back button was still there.
My code is similar to this:
struct FirstScreen: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: SecondScreen()) {
Text("Go to Second Screen")
}
}
}
}
}
struct SecondScreen: View {
var body: some View {
VStack {
NavigationLink(destination: ThirdScreen()) {
Text("Go to Third Screen")
}
}
}
}
struct ThirdScreen: View {
var body: some View {
VStack {
NavigationLink(destination: FirstScreen()) {
Text("Go to First Screen")
}
}
}
}
this is the image
You're pushing the FirstScreen onto your navigation stack, but FirstScreen contains its own NavigationView. If you really want to keep pushing them on the stack, then move the NavigationView outside of FirstScreen.
struct ContentView: View {
var body: some View {
NavigationStack { // Use NavigationStack for iOS 16
FirstScreen()
}
}
}
struct FirstScreen: View {
var body: some View {
VStack {
NavigationLink(destination: SecondScreen()) {
Text("Go to Second Screen")
}
}
}
}
If you actually want to pop all the views off the stack and go back to FirstScreen you should use
init(path: Binding<NavigationPath>, etc)
Here's a simple example passing the path down the stack and resetting it to pop back to the root…
enum Screen {
case two, three
}
struct ContentView: View {
#State var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
VStack {
// value gets appended to path
NavigationLink("Go to Second Screen", value: Screen.two)
}
// Decides which screen to show for values in path
.navigationDestination(for: Screen.self) { index in
switch index {
case .two:
SecondScreen(path: $path)
case .three:
ThirdScreen(path: $path)
}
}
}
}
}
struct SecondScreen: View {
#Binding var path: NavigationPath
var body: some View {
VStack {
NavigationLink("Go to Third Screen", value: Screen.three)
}
}
}
struct ThirdScreen: View {
#Binding var path: NavigationPath
var body: some View {
VStack {
Button("Go to First Screen") {
// reset the path to pop to root
path = NavigationPath()
}
}
}
}

SwiftUI - Navigation Link pops out on iPhone, but not in Simulator

I have an app that contains several views with NavigationLinks inside.
The main view looks like this, calling a Toolbar view I have created.
struct CountListView: View {
#StateObject var vm = CountListViewModel()
let navigationBar = HomePageNavigationBar()
var body: some View {
NavigationView {
List {
ForEach(vm.count, id: \.uid) { item in
NavigationLink(destination: CountView(count: item)) {
CountListItemView(name: item.name)
}
}
}
.toolbar {
navigationBar.rightSideOfBar()
navigationBar.leftSideOfBar()
}
.navigationBarTitle("Count")
The navigation bar function that is playing up looks like this
func leftSideOfBar() -> some ToolbarContent {
ToolbarItemGroup(placement: .navigationBarLeading) {
NavigationLink(destination: SettingsView()) {
Label("Settings", systemImage: "gear")
}
}
}
And the SettingsView is as follows:
struct SettingsView: View {
var body: some View {
List {
NavigationLink(destination: NameSettingView()) {
Text("Name")
}
.buttonStyle(PlainButtonStyle())
NavigationLink(destination: PrivacyPolicyView()) {
Text("Privacy Policy")
}
.buttonStyle(PlainButtonStyle())
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
When I open the Privacy Policy View on device, the returns to the SettingsView without any user intervention. But this problem doesn't exist in the simulator.

(SwiftUI) How to present a View in front of navigationBar?

First View:
when click on "Hello, World!1" , I set a NavigationLink that will direct to Second View.
Second View:
when click on "Hello, World!2", my PopUpView will appear.
But it is going under the navigationBar area. I need the PopUpView to be in front of navigationBar and this view has to be transparent so that we are able to see Second View.
Does anyone know how to solve this problem?
Please share your solution with me. Thank you SO much!
My code and screenshots are shown below for your reference.
enter image description here
enter image description here
enter image description here
struct MainView: View {
var body: some View {
NavigationView {
ZStack {
Color.yellow.ignoresSafeArea(.all)
VStack {
NavigationLink(
destination: SecondView(),
label: {
Text("Hello, World!1")
})
}
.navigationTitle("First")
.navigationBarTitleDisplayMode(.inline)
}
}
}
}
struct SecondView: View {
#State var showPop = false
var body: some View {
ZStack {
Color.blue.ignoresSafeArea(.all)
VStack {
Text("Hello, World!2")
.onTapGesture {
showPop = true
}
}
if showPop {
PopUpView(showPop: $showPop)
}
}
.navigationTitle("Second")
.navigationBarTitleDisplayMode(.inline)
}
}
struct PopUpView: View {
#Binding var showPop : Bool
var body: some View {
ZStack {
Color.black.opacity(0.5).ignoresSafeArea(.all)
.onTapGesture {
showPop.toggle()
}
Text("Hello, Pop UP!")
}
}
}

SwiftUI - Crash when presenting .sheet in minimal project that includes .toolbar

I've run into a weird crash when presenting a sheet under certain conditions.I've been able to reproduce it in a starter project and have had confirmation from multiple people that it crashes on their end as well. The strange thing is, that for some simulators / users, these steps don't crash the app.
I've filed a Bug Report with Apple (FB9064549), but thought I'd ask it here as well.
The steps to reproduce the crash are as follows:
Click the "Open Detail View" button
Go back
Click the "Open Detail View" button again
Click the "Present Modal"
It will crash
The basic content view has a toolbar with a Menu (or just a normal Text) in it and a NavigationLink to push to a new page. If I comment out the .toolbar, the crash does not happen.
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(
destination: DetailView(),
label: {
Text("Open Detail View")
})
}
// If you comment this out, it does not crash
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
ForEach(1...5, id: \.self) { index in
Button {
print("tap menu item")
} label: {
HStack {
Text("Menu Item \(index)")
}
}
}
} label: {
Text("Filter")
}
}
}
}
}
}
The presented DetailView can present a .sheet through a binding item. If I use the FullScreenCover instead, it does not crash.
struct DetailView: View {
#State var modalType: ModalType?
var body: some View {
Button(action: {
modalType = .modalWithTabView
}, label: {
Text("Present Modal")
})
.sheet(item: $modalType, content: { $0 })
}
}
enum ModalType: Identifiable, View {
case modalWithTabView
var id: String {
return "modalWithTabView"
}
var body: some View {
switch self {
case .modalWithTabView:
ModalWithTabView()
}
}
}
The sheet that is presented is a basic TabView pagination. If I don't add the PageTabViewStyle style, it does not crash.
struct ModalWithTabView: View {
#State var currentStep = 0
var body: some View {
TabView(selection: $currentStep) {
ForEach (0 ..< 10) { index in
Text("Page \(index)")
.tag(index)
}
}
// If you comment this out, it does not crash
.tabViewStyle(PageTabViewStyle())
}
}
If anyone has any pointers please let me know!

SwiftUi - Navigation View - How to show two views on iPad

I would like the iPad to show my first view1 list in the side bar and a second view alongside, populated by whatever is selected in view1. Then, when selecting something from the view2 list. go to the third view in full screen.
In my simple example, on loading I just get a blank screen with "< Back". On click, I get a side bar with my view1, but nothing else.
Obviously doing something wrong. Grateful for help.
My example:
struct firstView: View {
let myFirstArray = [
"First",
"Second",
"Third"
]
var body: some View {
NavigationView {
List(myFirstArray, id: \.self) { value in
NavigationLink (destination: SecondView()) {
Text("FirstView \(value)")
}
.navigationBarTitle("First View", displayMode: .inline)
}
}
}
}
struct SecondView: View {
let mySecondArray = [
"One",
"Two",
"Three"
]
var body: some View {
List (mySecondArray, id: \.self) { value in
NavigationLink (destination: ThirdView()) {
Text("Second View \(value)")
}
}
}
}
struct ThirdView: View {
var body: some View {
Text("Hello World")
}
}
You need to provide default detail view for double column navigation view style (which you see as default for iPad - different sidebar visibility for portrait/landscape orientation)
var body: some View {
NavigationView {
List(myFirstArray, id: \.self) { value in
NavigationLink (destination: SecondView()) {
Text("FirstView \(value)")
}
.navigationBarTitle("First View", displayMode: .inline)
}
SecondView() // << here !!
}
}

Resources