zeezide / viewcontroller Goto Github PK
View Code? Open in Web Editor NEWMVC for SwiftUI (yes, seriously)
License: Apache License 2.0
MVC for SwiftUI (yes, seriously)
License: Apache License 2.0
When a sheet is dismissed, the content of the sheet sometimes goes away too soon (and is replaced with the builtin, red, type mismatch error panel). Likely because the presented object is removed before the sheet gets dismissed.
Navigation doesn't seem to have the issue.
So need to find a way to hang on to the presented VC a little longer. Maybe keep it around as a "disappearingVC" and mark it gone once didDisappear
fires? (auto-sheet presentation only).
@DivineDominion says that:
I believe that the segfault is gone in Xcode 13.4 (at least I don't see any when compiling)
For this issue: https://github.com/ZeeZide/ViewController/blob/develop/Sources/ViewController/ViewControllerView.swift#L28
Should #ifdef
the release and enable the smarter debug default view.
Presumably only auto is going to be used going forward. They are kinda left-overs approaching the auto-goal.
We can still keep the .custom
presentation mode, and the user can manually bind the the presented VC and present however he likes.
This is a funny side effect when using the inline view
accessor like so:
class CowsOverview: ViewController {
@Published var search = ""
var view: some View {
TextField("Search it", $search) // <= doesn't work
}
This does work when using the ViewController via the environment, e.g.:
class CowsOverview: ViewController {
@Published var search = ""
struct ContentView: View {
@EnvironmentObject var viewController : CowsOverview
TextField("Search it", $viewController.search)
}
}
This is because the $
accesses the projectedValue
of the property wrapper - @Published
in the first case, and @EnvironmentObject
in the second. And while @EnvironmentObject
gives you a Binding (and then that goes on traversing keypathes), the @Published
gives you the Combine Publisher
for the property.
There are a few ways to solve this, either using a trampoline providing the Bindings, maybe like:
var `$`: Trampoline<Self, ... key path stuff>
or maybe just adding a .bind()
to @Published
, like:
extension Published {
var bind : Binding<Value> { ... }
}
Maybe someone has additional ideas. Generally it is not a huge deal, as one often ends up w/ real view structs anyways.
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
}
}
Would be nice if we could just keep using NavigationLink
, instead of
PushLink("Settings", to: SettingsPage())
a
NavigationLink("Settings", to: SettingsPage())
The tricky part is capturing the navigation state. I think it can be solved, but needs a little more work.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.