NEW: Subscribe to Hacking with Swift+ and accelerate your learning! >>

Working with two side by side views in SwiftUI

Paul Hudson    @twostraws   

One of the most important UI components in UIKit is UISplitViewController, which you’ll see in many of Apple’s apps such as Notes, Mail, and more. On iPad split views show two views side by side, usually with some a primary list on the left and a detail view on the right. On iPhone the split view automatically collapses the two views into one, so you get navigation view push-and-pop behavior instead.

SwiftUI doesn’t have a direct equivalent for split view controllers, but instead makes the same functionality available through a creative use of NavigationView. You’re already familiar with the basic usage of NavigationView, which allows us to create views like this one:

struct ContentView: View {
    var body: some View {
        NavigationView {
            Text("Hello, World!")
                .navigationBarTitle("Primary")
        }
    }
}

Previously you’ve seen how that works great in portrait mode, but in landscape mode it results in a blank white screen. This is because of the split view behavior of NavigationView: it’s designed to work with two views rather than one, and right now SwiftUI doesn’t seem to care if you only provided one.

However, if we do provide two views then we get some really useful behavior out of the box. Try changing your view to this:

NavigationView {
    Text("Hello, World!")
        .navigationBarTitle("Primary")

    Text("Secondary")
}

When you launch the app now, what you say depends on your device and orientation:

  1. On portrait iPhones you’ll see “Hello, World!”
  2. On large landscape iPhones (such as iPhone 11 Pro Max) you’ll see “Secondary”.
  3. On portrait iPads you’ll also see “Secondary”
  4. On landscape iPads you’ll see both “Hello, World!” and “Secondary” side by side.

On the second and third of those combinations you’ll find you can swipe from the left edge of the device to bring up the other view – “Hello, World!” will partly slide over the top of “Secondary”, and can be dismissed by tapping anywhere in the “Secondary” view. Having a split view like this is a great way to take advantage of the extra screen space of iPads, while also giving users a faster way to navigate through your content.

SwiftUI automatically links the primary and secondary views, which means if you have a NavigationLink in the primary view it will automatically load its content in the secondary view:

NavigationView {
    NavigationLink(destination: Text("New secondary")) {
        Text("Hello, World!")
    }
    .navigationBarTitle("Primary")

    Text("Secondary")
}

However, right now at least, all this magic has a few drawbacks that I hope are likely to be fixed in a future SwiftUI update:

  1. Your initial secondary view isn’t given a navigation bar at the top, so even though you’re able to set a title nothing will appear.
  2. Subsequent detail views always get a navigation bar whether you want it or not, so you need to use navigationBarHidden(true) to hide it.
  3. There’s no way of making the primary view stay visible on iPad even when there is more than enough space.
  4. There’s no way of having a Menu button appear in the navigation bar of the secondary view, to make the primary view more discoverable.
  5. You can’t make the primary view shown in landscape by default; SwiftUI always chooses the detail.

The reason I feel confident these will be fixed in the future is because they are all possible in UIKit with its UISplitViewController, so hopefully it’s only a matter of time before the same functionality is enabled in SwiftUI.

Tip: NavigationView supports either one or two child views. Although you can place more in there, the third and subsequent views will be ignored.

Hacking with Swift is sponsored by Instabug

SPONSORED Are you tired of wasting time debugging your Swift app? Instabug’s SDK is here to help you minimize debugging time by providing you with complete device details, network logs, and reproduction steps with every bug report. All data is attached automatically, and it only takes a line of code to setup. Start your free trial now and get 3 months off exclusively for the Hacking with Swift Community.

Start your free trial!

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.4/5

Link copied to your pasteboard.