-
Notifications
You must be signed in to change notification settings - Fork 5
Open
Labels
enhancementNew feature or requestNew feature or request
Description
For common master/detail setups. A SwiftUI NavigationView really is a UI/NSSplitView already :-) But the semantics wrt to show and showDetail would be different.
Implementation shouldn't be too hard, depending on how many features are to be replicated.
A first attempt, to be finished:
/**
* Type erased version of the ``SplitViewController``. Check that for more
* information.
*/
public protocol _SplitViewController: _ViewController {
typealias Style = SplitViewControllerStyle
typealias Column = SplitViewControllerColumn
}
public enum SplitViewControllerStyle: Equatable {
case doubleColumn
case tripleColumn
}
public enum SplitViewControllerColumn: Equatable {
case primary
case supplementary
case secondary
}
/**
* A simple wrapper around SwiftUI's `NavigationView`.
*
* Should be used as a root only.
*
* This adds a few `UISplitViewController` like behaviour, but in the end just
* hooks into `NavigationView`
* (which is a SplitViewController in wider layouts).
*
* Unlike `UISplitViewController`, this does not wrap the children in
* `NavigationController`s (this is handled by SwiftUI itself).
*
* Example:
* ```swift
* struct ContentView: View { // the "scene view"
*
* var body: some View {
* MainViewController(SplitViewController(style: .doubleColumn))
* }
* }
* ```
*
* Note that this works quite differently to a `UISplitViewController`.
*
* 2022-04-25: Note that programmatic navigation in SwiftUI is still a mess,
* i.e. popping in a 3-pane controller may fail.
*/
open class SplitViewController: ViewController, _SplitViewController {
// TBD: We could probably make this more typesafe if we tie it to three
// columns?
@Published public var style : SplitViewControllerStyle
@Published public var viewControllers : [ AnyViewController ]
init(style: SplitViewControllerStyle = .doubleColumn,
viewControllers: [ AnyViewController ] = [])
{
self.style = style
self.viewControllers = viewControllers
}
convenience
public init<PrimaryVC, SupplementaryVC, SecondaryVC>(
_ primary : PrimaryVC,
_ supplementary : SupplementaryVC,
_ secondary : SecondaryVC
) where PrimaryVC : ViewController,
SupplementaryVC : ViewController,
SecondaryVC : ViewController
{
self.init(style: .tripleColumn, viewControllers: [
AnyViewController(primary),
AnyViewController(supplementary),
AnyViewController(secondary)
])
addChild(primary)
addChild(supplementary)
addChild(secondary)
}
convenience
public init<PrimaryVC, SecondaryVC>(_ primary : PrimaryVC,
_ secondary : SecondaryVC)
where PrimaryVC: ViewController, SecondaryVC: ViewController
{
self.init(style: .doubleColumn, viewControllers: [
AnyViewController(primary),
AnyViewController(secondary)
])
addChild(primary)
addChild(secondary)
}
// MARK: - View
public struct ContentView: View {
@EnvironmentObject private var viewController : SplitViewController
public init() {}
struct EmbedChild: SwiftUI.View {
let vc : _ViewController?
var body: some View {
if let vc = vc {
vc.anyControlledContentView
}
}
}
public var body: some View {
// SwiftUI switches the mode based on the _static_ style of the View
switch viewController.style {
case .doubleColumn:
NavigationView {
EmbedChild(vc: viewController.children.first)
EmbedChild(vc: viewController.children.count > 1
? viewController.children.dropFirst().first
: nil)
}
case .tripleColumn:
NavigationView {
EmbedChild(vc: viewController.children.first)
EmbedChild(vc: viewController.children.count > 1
? viewController.children.dropFirst().first
: nil)
EmbedChild(vc: viewController.children.count > 2
? viewController.children.dropFirst(2).first
: nil)
}
}
}
}
}
public extension AnyViewController {
@inlinable // Note: not a protocol requirement, i.e. dynamic!
var splitViewController : _SplitViewController? {
viewController.splitViewController
}
}
public extension _ViewController {
/**
* Return the ``SplitViewController`` presenting/wrapping this controller.
*/
var splitViewController : _SplitViewController? {
/// Is this VC itself being presented?
if let presentingVC = presentingViewController {
if let nvc = presentingVC as? _SplitViewController { return nvc }
return presentingVC.splitViewController
}
if let parent = parent as? _SplitViewController {
return parent
}
return parent?.splitViewController
}
}Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request