Giter Club home page Giter Club logo

viewcontroller's People

Contributors

helje5 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

viewcontroller's Issues

Type mismatch error when dismissing a sheet

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).

Support binds against `@Published` properties

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.

Add a `SplitViewController`

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
  }
}

Replace `PushLink` with a `NavigationLink` initialiser

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.