Two column navigation on iPad
This commit is contained in:
parent
efb96eddf3
commit
5b70c713b2
|
@ -291,6 +291,7 @@
|
||||||
D6E77D09286D25FA00D8B732 /* TrendingHashtagCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E77D08286D25FA00D8B732 /* TrendingHashtagCollectionViewCell.swift */; };
|
D6E77D09286D25FA00D8B732 /* TrendingHashtagCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E77D08286D25FA00D8B732 /* TrendingHashtagCollectionViewCell.swift */; };
|
||||||
D6E77D0B286D426E00D8B732 /* TrendingLinkCardCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E77D0A286D426E00D8B732 /* TrendingLinkCardCollectionViewCell.swift */; };
|
D6E77D0B286D426E00D8B732 /* TrendingLinkCardCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E77D0A286D426E00D8B732 /* TrendingLinkCardCollectionViewCell.swift */; };
|
||||||
D6E77D0D286E6B7300D8B732 /* TrendingLinkCardCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6E77D0C286E6B7300D8B732 /* TrendingLinkCardCollectionViewCell.xib */; };
|
D6E77D0D286E6B7300D8B732 /* TrendingLinkCardCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6E77D0C286E6B7300D8B732 /* TrendingLinkCardCollectionViewCell.xib */; };
|
||||||
|
D6E77D0F286F773900D8B732 /* SplitNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E77D0E286F773900D8B732 /* SplitNavigationController.swift */; };
|
||||||
D6E9CDA8281A427800BBC98E /* PostService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E9CDA7281A427800BBC98E /* PostService.swift */; };
|
D6E9CDA8281A427800BBC98E /* PostService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E9CDA7281A427800BBC98E /* PostService.swift */; };
|
||||||
D6EAE0DB2550CC8A002DB0AC /* FocusableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */; };
|
D6EAE0DB2550CC8A002DB0AC /* FocusableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */; };
|
||||||
D6EBF01523C55C0900AE061B /* UIApplication+Scenes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EBF01423C55C0900AE061B /* UIApplication+Scenes.swift */; };
|
D6EBF01523C55C0900AE061B /* UIApplication+Scenes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EBF01423C55C0900AE061B /* UIApplication+Scenes.swift */; };
|
||||||
|
@ -645,6 +646,7 @@
|
||||||
D6E77D08286D25FA00D8B732 /* TrendingHashtagCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingHashtagCollectionViewCell.swift; sourceTree = "<group>"; };
|
D6E77D08286D25FA00D8B732 /* TrendingHashtagCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingHashtagCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
D6E77D0A286D426E00D8B732 /* TrendingLinkCardCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingLinkCardCollectionViewCell.swift; sourceTree = "<group>"; };
|
D6E77D0A286D426E00D8B732 /* TrendingLinkCardCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingLinkCardCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
D6E77D0C286E6B7300D8B732 /* TrendingLinkCardCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TrendingLinkCardCollectionViewCell.xib; sourceTree = "<group>"; };
|
D6E77D0C286E6B7300D8B732 /* TrendingLinkCardCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TrendingLinkCardCollectionViewCell.xib; sourceTree = "<group>"; };
|
||||||
|
D6E77D0E286F773900D8B732 /* SplitNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitNavigationController.swift; sourceTree = "<group>"; };
|
||||||
D6E9CDA7281A427800BBC98E /* PostService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostService.swift; sourceTree = "<group>"; };
|
D6E9CDA7281A427800BBC98E /* PostService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostService.swift; sourceTree = "<group>"; };
|
||||||
D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusableTextField.swift; sourceTree = "<group>"; };
|
D6EAE0DA2550CC8A002DB0AC /* FocusableTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusableTextField.swift; sourceTree = "<group>"; };
|
||||||
D6EBF01423C55C0900AE061B /* UIApplication+Scenes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Scenes.swift"; sourceTree = "<group>"; };
|
D6EBF01423C55C0900AE061B /* UIApplication+Scenes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Scenes.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -1298,6 +1300,7 @@
|
||||||
D653F410267D1E32004E32B1 /* DiffableTimelineLikeTableViewController.swift */,
|
D653F410267D1E32004E32B1 /* DiffableTimelineLikeTableViewController.swift */,
|
||||||
D625E4812588262A0074BB2B /* DraggableTableViewCell.swift */,
|
D625E4812588262A0074BB2B /* DraggableTableViewCell.swift */,
|
||||||
D693DE5623FE1A6A0061E07D /* EnhancedNavigationViewController.swift */,
|
D693DE5623FE1A6A0061E07D /* EnhancedNavigationViewController.swift */,
|
||||||
|
D6E77D0E286F773900D8B732 /* SplitNavigationController.swift */,
|
||||||
D6BC8747219738E1006163F1 /* EnhancedTableViewController.swift */,
|
D6BC8747219738E1006163F1 /* EnhancedTableViewController.swift */,
|
||||||
D693DE5823FE24300061E07D /* InteractivePushTransition.swift */,
|
D693DE5823FE24300061E07D /* InteractivePushTransition.swift */,
|
||||||
D6C693FB2162FE6F007D6A6D /* LoadingViewController.swift */,
|
D6C693FB2162FE6F007D6A6D /* LoadingViewController.swift */,
|
||||||
|
@ -1804,6 +1807,7 @@
|
||||||
D662AEF2263A4BE10082A153 /* ComposePollView.swift in Sources */,
|
D662AEF2263A4BE10082A153 /* ComposePollView.swift in Sources */,
|
||||||
D677284A24ECBDF400C732D3 /* ComposeCurrentAccount.swift in Sources */,
|
D677284A24ECBDF400C732D3 /* ComposeCurrentAccount.swift in Sources */,
|
||||||
D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */,
|
D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */,
|
||||||
|
D6E77D0F286F773900D8B732 /* SplitNavigationController.swift in Sources */,
|
||||||
D6B30E09254BAF63009CAEE5 /* ImageGrayscalifier.swift in Sources */,
|
D6B30E09254BAF63009CAEE5 /* ImageGrayscalifier.swift in Sources */,
|
||||||
D6A6C10F25B62D2400298D0F /* DiskCache.swift in Sources */,
|
D6A6C10F25B62D2400298D0F /* DiskCache.swift in Sources */,
|
||||||
D6B81F3C2560365300F6E31D /* RefreshableViewController.swift in Sources */,
|
D6B81F3C2560365300F6E31D /* RefreshableViewController.swift in Sources */,
|
||||||
|
|
|
@ -20,8 +20,11 @@ class MainSplitViewController: UISplitViewController {
|
||||||
|
|
||||||
private var tabBarViewController: MainTabBarViewController!
|
private var tabBarViewController: MainTabBarViewController!
|
||||||
|
|
||||||
private var secondaryNavController: UINavigationController! {
|
// private var secondaryNavController: UINavigationController! {
|
||||||
viewController(for: .secondary) as? UINavigationController
|
// viewController(for: .secondary) as? UINavigationController
|
||||||
|
// }
|
||||||
|
private var secondaryNavController: SplitNavigationController! {
|
||||||
|
viewController(for: .secondary) as? SplitNavigationController
|
||||||
}
|
}
|
||||||
|
|
||||||
init(mastodonController: MastodonController) {
|
init(mastodonController: MastodonController) {
|
||||||
|
@ -46,9 +49,10 @@ class MainSplitViewController: UISplitViewController {
|
||||||
setViewController(sidebar, for: .primary)
|
setViewController(sidebar, for: .primary)
|
||||||
primaryBackgroundStyle = .sidebar
|
primaryBackgroundStyle = .sidebar
|
||||||
|
|
||||||
let secondaryNav = EnhancedNavigationViewController()
|
// let secondaryNav = EnhancedNavigationViewController()
|
||||||
secondaryNav.useBrowserStyleNavigation = true
|
// secondaryNav.useBrowserStyleNavigation = true
|
||||||
setViewController(secondaryNav, for: .secondary)
|
let splitNav = SplitNavigationController()
|
||||||
|
setViewController(splitNav, for: .secondary)
|
||||||
// don't unnecesarily construct a content VC unless the we're in actually split mode
|
// don't unnecesarily construct a content VC unless the we're in actually split mode
|
||||||
// when we change from compact -> split for the first time, the VC will be transferred anyways
|
// when we change from compact -> split for the first time, the VC will be transferred anyways
|
||||||
if traitCollection.horizontalSizeClass != .compact {
|
if traitCollection.horizontalSizeClass != .compact {
|
||||||
|
|
|
@ -142,7 +142,7 @@ class MainTabBarViewController: UITabBarController, UITabBarControllerDelegate {
|
||||||
return vc
|
return vc
|
||||||
} else {
|
} else {
|
||||||
let nav = EnhancedNavigationViewController(rootViewController: vc)
|
let nav = EnhancedNavigationViewController(rootViewController: vc)
|
||||||
nav.useBrowserStyleNavigation = true
|
// nav.useBrowserStyleNavigation = true
|
||||||
return nav
|
return nav
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,262 @@
|
||||||
|
//
|
||||||
|
// SplitNavigationController.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 7/1/22.
|
||||||
|
// Copyright © 2022 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SplitNavigationController: UIViewController {
|
||||||
|
|
||||||
|
private let rootNav = SplitRootNavigationController()
|
||||||
|
private let secondaryNav = SplitSecondaryNavigationController()
|
||||||
|
private let separatorView = UIView()
|
||||||
|
|
||||||
|
private var constraints: [NSLayoutConstraint] = []
|
||||||
|
|
||||||
|
var viewControllers: [UIViewController] {
|
||||||
|
get {
|
||||||
|
return rootNav.viewControllers + secondaryNav.viewControllers
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if newValue.isEmpty {
|
||||||
|
rootNav.viewControllers = []
|
||||||
|
secondaryNav.viewControllers = []
|
||||||
|
} else if canShowSecondaryNav {
|
||||||
|
var newValue = newValue
|
||||||
|
rootNav.viewControllers = [newValue.removeFirst()]
|
||||||
|
secondaryNav.viewControllers = newValue
|
||||||
|
} else {
|
||||||
|
rootNav.viewControllers = newValue
|
||||||
|
secondaryNav.viewControllers = []
|
||||||
|
}
|
||||||
|
updateSecondaryNavVisibility()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This property is only valid after the view has been laid out.
|
||||||
|
private var canShowSecondaryNav: Bool {
|
||||||
|
// minimum of 360pt for each column
|
||||||
|
// this allows split navigation on all ipads in portrait w/ sidebar hidden and in landscape (regardless of sidebar)
|
||||||
|
(viewIfLoaded?.bounds.width ?? 0) >= 720
|
||||||
|
}
|
||||||
|
|
||||||
|
init(rootViewController: UIViewController? = nil) {
|
||||||
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
|
||||||
|
rootNav.showImpl = { [unowned self] vc, sender in
|
||||||
|
if self.canShowSecondaryNav {
|
||||||
|
self.setSecondaryViewControllers([vc], animated: true)
|
||||||
|
|
||||||
|
// the split nav shouldn't really be reaching down into the inner VCs like this,
|
||||||
|
// but I can't think of a cleaner way
|
||||||
|
if let tableVC = sender as? UITableViewController,
|
||||||
|
let selectedIndexPath = tableVC.tableView.indexPathForSelectedRow {
|
||||||
|
tableVC.tableView.deselectRow(at: selectedIndexPath, animated: true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.rootNav.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
secondaryNav.closeSecondaryImpl = { [unowned self] in
|
||||||
|
self.popToRootViewController(animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let rootViewController {
|
||||||
|
rootNav.viewControllers = [rootViewController]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
embedChild(rootNav, layout: false)
|
||||||
|
embedChild(secondaryNav, layout: false)
|
||||||
|
rootNav.view.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
secondaryNav.view.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
|
separatorView.backgroundColor = .separator
|
||||||
|
separatorView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
view.addSubview(separatorView)
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
rootNav.view.topAnchor.constraint(equalTo: view.topAnchor),
|
||||||
|
rootNav.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||||
|
rootNav.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||||
|
|
||||||
|
separatorView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||||
|
separatorView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||||
|
separatorView.leadingAnchor.constraint(equalTo: rootNav.view.trailingAnchor),
|
||||||
|
separatorView.widthAnchor.constraint(equalToConstant: 0.5),
|
||||||
|
|
||||||
|
secondaryNav.view.topAnchor.constraint(equalTo: view.topAnchor),
|
||||||
|
secondaryNav.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||||
|
secondaryNav.view.leadingAnchor.constraint(equalTo: separatorView.trailingAnchor),
|
||||||
|
])
|
||||||
|
|
||||||
|
updateSecondaryNavVisibility()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func show(_ vc: UIViewController, sender: Any?) {
|
||||||
|
if !canShowSecondaryNav {
|
||||||
|
rootNav.pushViewController(vc, animated: true)
|
||||||
|
} else if rootNav.viewControllers.isEmpty {
|
||||||
|
rootNav.pushViewController(vc, animated: false)
|
||||||
|
} else {
|
||||||
|
secondaryNav.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
|
updateSecondaryNavVisibility()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||||
|
super.traitCollectionDidChange(previousTraitCollection)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillLayoutSubviews() {
|
||||||
|
super.viewWillLayoutSubviews()
|
||||||
|
|
||||||
|
if !isLayingOutForAnimation {
|
||||||
|
updateSecondaryNavVisibility()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateSecondaryNavVisibility() {
|
||||||
|
guard isViewLoaded else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if canShowSecondaryNav {
|
||||||
|
if rootNav.viewControllers.count > 1 {
|
||||||
|
var vcs = rootNav.viewControllers
|
||||||
|
let root = vcs.removeFirst()
|
||||||
|
rootNav.viewControllers = [root]
|
||||||
|
// this shouldn't be necessary since the vcs are removed from their parent vc by setting rootNav.viewControllers
|
||||||
|
// but it doesn't remove the views from their superview (until the next runloop iteration?)
|
||||||
|
// so we need to do that ourselves before we can set them on the secondary nav (otherwise it raises an exception)
|
||||||
|
vcs.forEach { $0.removeViewAndController() }
|
||||||
|
secondaryNav.viewControllers = vcs
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !secondaryNav.viewControllers.isEmpty {
|
||||||
|
let firstSecondary = secondaryNav.viewControllers.first!
|
||||||
|
// remove the left bar button item so that the builtin Back item shows
|
||||||
|
if firstSecondary.navigationItem.leftBarButtonItem?.tag == ViewTags.splitNavCloseSecondaryButton {
|
||||||
|
firstSecondary.navigationItem.leftBarButtonItem = nil
|
||||||
|
}
|
||||||
|
rootNav.viewControllers.append(contentsOf: secondaryNav.viewControllers)
|
||||||
|
secondaryNav.viewControllers = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSecondaryVisible(canShowSecondaryNav && !secondaryNav.viewControllers.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setSecondaryVisible(_ visible: Bool) {
|
||||||
|
guard isViewLoaded else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
NSLayoutConstraint.deactivate(constraints)
|
||||||
|
if visible {
|
||||||
|
constraints = [
|
||||||
|
rootNav.view.trailingAnchor.constraint(equalTo: view.centerXAnchor),
|
||||||
|
secondaryNav.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
constraints = [
|
||||||
|
rootNav.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||||
|
secondaryNav.view.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
NSLayoutConstraint.activate(constraints)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setSecondaryViewControllers(_ vcs: [UIViewController], animated: Bool) {
|
||||||
|
if animated {
|
||||||
|
if vcs.isEmpty {
|
||||||
|
popToRootViewController(animated: true)
|
||||||
|
} else {
|
||||||
|
let wasVisible = !secondaryNav.viewControllers.isEmpty
|
||||||
|
secondaryNav.viewControllers = vcs
|
||||||
|
secondaryNav.view.frame = CGRect(x: view.bounds.width, y: 0, width: view.bounds.width / 2, height: view.bounds.height)
|
||||||
|
secondaryNav.view.layoutIfNeeded()
|
||||||
|
if !wasVisible {
|
||||||
|
let animator = UIViewPropertyAnimator(duration: 0.35, curve: .easeInOut) {
|
||||||
|
self.updateSecondaryNavVisibility()
|
||||||
|
self.view.layoutIfNeeded()
|
||||||
|
}
|
||||||
|
animator.startAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
secondaryNav.viewControllers = vcs
|
||||||
|
updateSecondaryNavVisibility()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var isLayingOutForAnimation = false
|
||||||
|
|
||||||
|
func popToRootViewController(animated: Bool) {
|
||||||
|
if animated {
|
||||||
|
// we don't update secondaryNav.viewControllers until after the animation is completed
|
||||||
|
// otherwise the secondary nav's contents disappear immediately, rather than sliding off-screen
|
||||||
|
let animator = UIViewPropertyAnimator(duration: 0.35, curve: .easeInOut) {
|
||||||
|
self.isLayingOutForAnimation = true
|
||||||
|
self.setSecondaryVisible(false)
|
||||||
|
self.view.layoutIfNeeded()
|
||||||
|
}
|
||||||
|
animator.addCompletion { _ in
|
||||||
|
self.secondaryNav.viewControllers = []
|
||||||
|
self.isLayingOutForAnimation = false
|
||||||
|
// self.updateSecondaryNavVisibility()
|
||||||
|
}
|
||||||
|
animator.startAnimation()
|
||||||
|
} else {
|
||||||
|
self.secondaryNav.viewControllers = []
|
||||||
|
self.updateSecondaryNavVisibility()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SplitRootNavigationController: UINavigationController {
|
||||||
|
fileprivate var showImpl: ((UIViewController, Any?) -> Void)!
|
||||||
|
|
||||||
|
override func show(_ vc: UIViewController, sender: Any?) {
|
||||||
|
showImpl(vc, sender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SplitSecondaryNavigationController: EnhancedNavigationViewController {
|
||||||
|
fileprivate var closeSecondaryImpl: (() -> Void)!
|
||||||
|
|
||||||
|
override var viewControllers: [UIViewController] {
|
||||||
|
didSet {
|
||||||
|
if let first = viewControllers.first {
|
||||||
|
configureSecondarySplitCloseButton(for: first)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func configureSecondarySplitCloseButton(for viewController: UIViewController) {
|
||||||
|
guard viewController.navigationItem.leftBarButtonItem?.tag != ViewTags.splitNavCloseSecondaryButton else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let item = UIBarButtonItem(title: "Close", style: .done, target: self, action: #selector(closeSecondary))
|
||||||
|
item.tag = ViewTags.splitNavCloseSecondaryButton
|
||||||
|
viewController.navigationItem.leftBarButtonItem = item
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func closeSecondary() {
|
||||||
|
closeSecondaryImpl()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ import UIKit
|
||||||
|
|
||||||
// Based on MVCTodo by Dave DeLong: https://github.com/davedelong/MVCTodo/blob/841649dd6aa31bacda3ad7ef9a9a836f66281e50/MVCTodo/Extensions/UIViewController.swift
|
// Based on MVCTodo by Dave DeLong: https://github.com/davedelong/MVCTodo/blob/841649dd6aa31bacda3ad7ef9a9a836f66281e50/MVCTodo/Extensions/UIViewController.swift
|
||||||
extension UIViewController {
|
extension UIViewController {
|
||||||
func embedChild(_ newChild: UIViewController, in container: UIView? = nil) {
|
func embedChild(_ newChild: UIViewController, in container: UIView? = nil, layout: Bool = true) {
|
||||||
// if the view controller is already a child of something else, remove it
|
// if the view controller is already a child of something else, remove it
|
||||||
if let oldParent = newChild.parent, oldParent != self {
|
if let oldParent = newChild.parent, oldParent != self {
|
||||||
newChild.beginAppearanceTransition(false, animated: false)
|
newChild.beginAppearanceTransition(false, animated: false)
|
||||||
|
@ -36,7 +36,7 @@ extension UIViewController {
|
||||||
newChild.beginAppearanceTransition(true, animated: false)
|
newChild.beginAppearanceTransition(true, animated: false)
|
||||||
addChild(newChild)
|
addChild(newChild)
|
||||||
newChild.didMove(toParent: self)
|
newChild.didMove(toParent: self)
|
||||||
targetContainer.embedSubview(newChild.view)
|
targetContainer.embedSubview(newChild.view, layout: layout)
|
||||||
newChild.endAppearanceTransition()
|
newChild.endAppearanceTransition()
|
||||||
} else {
|
} else {
|
||||||
// the view controller is already a child
|
// the view controller is already a child
|
||||||
|
@ -45,7 +45,7 @@ extension UIViewController {
|
||||||
// we don't do the appearance transition stuff here,
|
// we don't do the appearance transition stuff here,
|
||||||
// because the vc is already a child, so *presumably*
|
// because the vc is already a child, so *presumably*
|
||||||
// that transition stuff has already appened
|
// that transition stuff has already appened
|
||||||
targetContainer.embedSubview(newChild.view)
|
targetContainer.embedSubview(newChild.view, layout: layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,22 +57,25 @@ extension UIViewController {
|
||||||
|
|
||||||
// Based on MVCTodo by Dave DeLong: https://github.com/davedelong/MVCTodo/blob/841649dd6aa31bacda3ad7ef9a9a836f66281e50/MVCTodo/Extensions/UIView.swift
|
// Based on MVCTodo by Dave DeLong: https://github.com/davedelong/MVCTodo/blob/841649dd6aa31bacda3ad7ef9a9a836f66281e50/MVCTodo/Extensions/UIView.swift
|
||||||
extension UIView {
|
extension UIView {
|
||||||
func embedSubview(_ subview: UIView) {
|
func embedSubview(_ subview: UIView, layout: Bool = true) {
|
||||||
if subview.superview == self { return }
|
if subview.superview == self { return }
|
||||||
|
|
||||||
if subview.superview != nil {
|
if subview.superview != nil {
|
||||||
subview.removeFromSuperview()
|
subview.removeFromSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
subview.frame = bounds
|
|
||||||
addSubview(subview)
|
addSubview(subview)
|
||||||
|
|
||||||
NSLayoutConstraint.activate([
|
if layout {
|
||||||
subview.leadingAnchor.constraint(equalTo: leadingAnchor),
|
subview.frame = bounds
|
||||||
subview.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
||||||
subview.topAnchor.constraint(equalTo: topAnchor),
|
NSLayoutConstraint.activate([
|
||||||
subview.bottomAnchor.constraint(equalTo: bottomAnchor)
|
subview.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||||
|
subview.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||||
|
subview.topAnchor.constraint(equalTo: topAnchor),
|
||||||
|
subview.bottomAnchor.constraint(equalTo: bottomAnchor)
|
||||||
])
|
])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isContainedWithin(_ other: UIView) -> Bool {
|
func isContainedWithin(_ other: UIView) -> Bool {
|
||||||
|
|
|
@ -16,4 +16,5 @@ struct ViewTags {
|
||||||
static let navBackBarButton = 42003
|
static let navBackBarButton = 42003
|
||||||
static let navForwardBarButton = 42004
|
static let navForwardBarButton = 42004
|
||||||
static let navEmptyTitleView = 42005
|
static let navEmptyTitleView = 42005
|
||||||
|
static let splitNavCloseSecondaryButton = 42006
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue