Chapters

Hide chapters

Catalyst by Tutorials

First Edition · iOS 13 · Swift 5.1 · Xcode 11

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

2. Migrating to Split View Controller
Written by Andy Pereira

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

The split view controller provides a way to manage two view controllers at the same time. The split view controller takes advantage of the iPad’s significantly larger screen size, making it easy to display a master-detail style interface. This also makes it easy to adapt to the screen size of the device someone may be using. In this chapter, you’ll convert the iPhone “master-detail” pattern found in Journalyst to a split view controller, then update the UI to take advantage of the view.

Getting started

Open the starter project for the chapter. Select an iPad Pro simulator in the active scheme, then build and run the app. In its current state, the iPad version of Journalyst requires you to select an entry before you can see the details.

Integrating Split View Controller

Open Main.storyboard and select the Entry Table View Controller scene. From the menu bar, select Editor ▸ Embed In ▸ Navigation Controller.

// MARK: - Properties
var entryTableViewController: EntryTableViewController? = nil
@IBSegueAction func entryViewController(coder: NSCoder,
  sender: Any?, segueIdentifier: String?) ->
  UINavigationController? {
    guard let cell = sender as? EntryTableViewCell,
      let indexPath = tableView.indexPath(for: cell),
      let navigationController 
        = UINavigationController(coder: coder),
      let entryTableViewController
        = navigationController.topViewController as?
        EntryTableViewController else { return nil }
    entryTableViewController.entry 
      = dataSource?.itemIdentifier(for: indexPath)
    self.entryTableViewController = entryTableViewController
    return navigationController
}
// MARK: - Split view
extension AppDelegate: UISplitViewControllerDelegate {
  func splitViewController(
    _ splitViewController: UISplitViewController,
    collapseSecondary secondaryViewController: UIViewController,
    onto primaryViewController: UIViewController) -> Bool {
    guard let secondaryNavigationController
      = secondaryViewController as? UINavigationController,
      let entryTableViewController 
        = secondaryNavigationController.topViewController
        as? EntryTableViewController else {
          return false
    }
    if entryTableViewController.entry == nil {
      return true
    }
    return false
  }
}
func application(_ application: UIApplication,
  didFinishLaunchingWithOptions launchOptions:
  [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    if let window = window,
      let splitViewController 
        = window.rootViewController as? UISplitViewController,
      let navigationController 
        = splitViewController.viewControllers.last
        as? UINavigationController {
          navigationController.topViewController?
            .navigationItem.leftBarButtonItem 
            = splitViewController.displayModeButtonItem
          splitViewController.preferredDisplayMode = .allVisible
          splitViewController.delegate = self
    }
    return true
}

Updating the UI

Even though you have a working split view controller, you’ll notice that not everything is working smoothly. The first entry in the list does not automatically get shown in the EntryTableViewController. To get that working, open MainTableViewController.swift and add this code to viewDidLoad():

if let splitViewController = splitViewController,
  let splitNavigationController 
    = splitViewController.viewControllers.last
    as? UINavigationController,
  let topViewController 
    = splitNavigationController.topViewController
    as? EntryTableViewController {
  entryTableViewController = topViewController
}
private func populateMockData() {
  reloadSnapshot(animated: false)
  if let entryTableViewController = entryTableViewController,
    let entry = entries.first,
    entryTableViewController.entry == nil {
    tableView.selectRow(at: IndexPath(row: 0, section: 0),
                        animated: false,
                        scrollPosition: .top)
    entryTableViewController.entry = entry
  }
}

func textViewDidEndEditing(_ textView: UITextView) {
  entry?.log = textView.text
}
if let sender = sender,
  let popoverController =
  actionSheet.popoverPresentationController {
  popoverController.sourceRect = CGRect(x: sender.frame.midX,
                                        y: sender.frame.midY,
                                        width: 0,
                                        height: 0)
  popoverController.sourceView = sender
}
if let popoverController 
  = activityController.popoverPresentationController {
    popoverController.barButtonItem 
      = navigationItem.rightBarButtonItem
}

macOS split views

In traditional macOS apps, split views have long been the standard way of presenting a UI in a similar way to the split view controller. Catalyst makes it easy to take a split view controller and turn it into something that looks right at home on a Mac.

splitViewController.primaryBackgroundStyle = .sidebar

Key points

  • Split view controllers make it easy to create an iOS and macOS app from the same codebase.
  • Popovers on iPadOS and macOS are handled identically.
  • There is minimal work to make a split view controller look proper on macOS.

Where to go from here?

This chapter got you from an iPhone-only app to a working iPad and macOS app that feels right at home on each platform. You learned what changes you need to make to handle the different platforms, and how much Catalyst will handle for you.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now