25

My app has simple navigation needs

  • List View (parent objects)
  • List View (child objects)
  • Detail View (child object)

I have this setup and working on iPhone, but when I run the app on iPad in portrait mode the master list is always hidden.

I'm using .isDetailLink(false) on the navigation link from the first list to the second, so both lists always stay in the master column. In iPad landscape everything works as expected but in portrait the detail view fills the screen. I can swipe in from the left side of the screen to show the list but I'd like to provide more clarity to the user.

I'd like to show or add the back button to show the master/list side (sort of like the Apple Notes app). On the iPhone I get the back button by default but on iPad in portrait mode there is nothing in its place.

This is what I see on iPhone enter image description here

But this is what I see on iPad enter image description here

Parent list

struct ParentList: View {

    let firstList = ["Sample data 01", "Sample data 02", "Sample data 03", "Sample data 04", "Sample data 05"]

    var body: some View {
        NavigationView {
            List{
                ForEach(firstList, id: \.self) { item in
                    NavigationLink(destination: ChildList()){

                        Text(item)
                    }
                    .isDetailLink(false)

                }
            }
        }
    }
}

Child list

struct ChildList: View {

    let secondList = ["More Sample data 01", "More Sample data 02", "More Sample data 03", "More Sample data 04", "More Sample data 05"]

    var body: some View {
        List{
            ForEach(secondList, id: \.self) { item in
                NavigationLink(destination: ChildDetail()){

                    Text(item)

                }
            }
        }
    }
}

Child detail

struct ChildDetail: View {

    var body: some View {
        Text("Child detail view")
    }

}

Update: As of Oct 17, 2019 I have not found a way to get this to work. I decided to use .navigationViewStyle(StackNavigationViewStyle()) for the time being. Interestingly, this needs to go outside of the navigation view like a normal modifier, not inside it with the navigation title.

radicalappdev
  • 1,298
  • 3
  • 13
  • 27
  • I don't know the answer, but I've had this same behavior on iPads also. Basically, a List automatically becomes a SplitView on the iPad, and in portrait it seems to be hiding the master view. I'm not near anything to help right now, but maybe if you drill into the properties you can set for a List you might find the answer. Search for things like" iPad", "master" and pore through the comments. Good luck! –  Sep 11 '19 at 14:26

5 Answers5

36

In portrait the default split view does not work. This may be fixed in future but it appears the current options are:
(a) change the navigation view style of your first list to .navigationViewStyle(StackNavigationViewStyle()) so the navigation will work like on iPhone and push each view.

(b) leave the style to default and only support landscape for iPad

(c) implement a UIKit split view controller

David
  • 3,285
  • 1
  • 37
  • 54
ptc
  • 734
  • 5
  • 11
8

There also is a quite hacky workaround (see https://stackoverflow.com/a/57215664/3187762)

By adding .padding() to the NavigationView it seems to achieve the behaviour of always display the Master.

NavigationView {
        MyMasterView()
        DetailsView()
 }.navigationViewStyle(DoubleColumnNavigationViewStyle())
  .padding()

Not sure if it is intended though. Might break in the future (works using Xcode 11.0, in simulator on iOS 13.0 and device with 13.1.2).

You should not rely on it. This comment seems to be the better answer: https://stackoverflow.com/a/57919024/3187762

simonnickel
  • 548
  • 6
  • 12
3

in iOS 13.4, a "back to master view" button has been added to the iPad layout. From the release notes:

When using a NavigationView with multiple columns, the navigation bar now shows a control to toggle the columns. (49074511)

For example:

struct MyNavView: View {
    var body: some View {
        NavigationView {
            NavigationLink(
                destination: Text("navigated"), 
                label: {Text("Go somwhere")}
            )
            .navigationBarTitle("Pick Item")
        }
    }
}

Has the following result: An iPad with a button at the top left saying "Pick Item" An iPad screenshot with a navigation column on the left side. The column has a header with the text "Pick Item"

Benjamin Kindle
  • 1,736
  • 12
  • 23
  • What exactly does this mean? I'm trying to do an iPhone/iPad app and iPad my app is going really *really* wonky on the iPad. I have multiple columns and with the app open going between all kinds of variants on the iPad, split views, landscape, portrait, that "hover" view it does in a column... I'm getting all sorts of weird things going on, I can even get the same column to show in BOTH places - side by side! I'm sure it's me, but it's ahrd to track this all down. Fun though! – Rillieux Apr 26 '20 at 20:46
  • 1
    @Poltavets It's not just you. SwiftUI appears to be about 60% complete. But yes, it is fun. :) – Tres May 03 '20 at 21:34
  • @Tres - that's good to know! Hopefully WWDC2020 will bring some good improvements. I really love SwiftUI. WWDC2020 is surely going to fix a lot of its woes. – Rillieux May 04 '20 at 05:38
0

Look this solution here

I hope this help

iGhost
  • 1,009
  • 11
  • 21
0

For my project I'm using this extension. They will always use StackNavigationViewStyle for iPhone, iPad in a vertical orientation, and if you provide forceStackedStyle: true. Otherwise DoubleColumnNavigationViewStyle will be used.


var body: some View {
  NavigationView {
    Text("Hello world")
  }
  .resolveNavigationViewStyle(forceStackedStyle: false)
}

extension View {
    func resolveNavigationViewStyle(forceStackedStyle: Bool) -> some View {
        
        if forceStackedStyle || UIDevice.current.userInterfaceIdiom == .phone {
            return self.navigationViewStyle(StackNavigationViewStyle())
                .eraseToAnyView()
        } else {
            return GeometryReader { p in
                if p.size.height > p.size.width { self.navigationViewStyle(StackNavigationViewStyle())
                } else {
                    self.navigationViewStyle(DoubleColumnNavigationViewStyle())
                }
            }
            .eraseToAnyView()
        }
    }
}
Alexander
  • 1
  • 1
  • 1