UINavigationController-Alike for Desktop-Cocoa?

后端 未结 3 1102
终归单人心
终归单人心 2020-12-06 06:47

I\'m currently developing an application with an user interface much like Twitter for Mac (Pushing in/out of views like on iOS).

Has anyone implemented a UIViewCont

相关标签:
3条回答
  • 2020-12-06 06:58

    There isn't one in standard AppKit at this time. You'll have to write your own.

    This may help if you decide to go down that path: http://parsekit.com/umekit/

    UMEKit is a little framework for Cocoa that implements some equivalents to UIKit classes and UI components.

    0 讨论(0)
  • 2020-12-06 07:03

    I spent ages looking for this and started to write my own, then found https://github.com/bfolder/BFNavigationController

    For some reason Google doesn't know about it.

    0 讨论(0)
  • 2020-12-06 07:09

    Here how we did NavigationController, without navigation bar. Implementing navigation bar with transitions between navigation items similar to how transition between views made in example below.

    import AppKit
    
    public class NavigationController: NSViewController {
    
       public private (set) var viewControllers: [NSViewController] = []
    
       open override func loadView() {
          view = NSView()
          view.wantsLayer = true
       }
    
       public init(rootViewController: NSViewController) {
          super.init(nibName: nil, bundle: nil)
          pushViewController(rootViewController, animated: false)
       }
    
       public required init?(coder: NSCoder) {
          fatalError()
       }
    }
    
    extension NavigationController {
    
       public var topViewController: NSViewController? {
          return viewControllers.last
       }
    
       public func pushViewControllerAnimated(_ viewController: NSViewController) {
          pushViewController(viewController, animated: true)
       }
    
       public func pushViewController(_ viewController: NSViewController, animated: Bool) {
          viewController.navigationController = self
          viewController.view.wantsLayer = true
          if animated, let oldVC = topViewController {
             embedChildViewController(viewController)
             let endFrame = oldVC.view.frame
             let startFrame = endFrame.offsetBy(dx: endFrame.width, dy: 0)
             viewController.view.frame = startFrame
             viewController.view.alphaValue = 0.85
             viewControllers.append(viewController)
             NSAnimationContext.runAnimationGroup({ context in
                context.duration = 0.2
                context.allowsImplicitAnimation = true
                context.timingFunction = .easeOut
                viewController.view.animator().frame = endFrame
                viewController.view.animator().alphaValue = 1
                oldVC.view.animator().alphaValue = 0.25
             }) {
                oldVC.view.alphaValue = 1
                oldVC.view.removeFromSuperview()
             }
          } else {
             embedChildViewController(viewController)
             viewControllers.append(viewController)
          }
       }
    
       @discardableResult
       public func popViewControllerAnimated() -> NSViewController? {
          return popViewController(animated: true)
       }
    
       @discardableResult
       public func popViewController(animated: Bool) -> NSViewController? {
          guard let oldVC = viewControllers.popLast() else {
             return nil
          }
    
          if animated, let newVC = topViewController {
             let endFrame = oldVC.view.frame.offsetBy(dx: oldVC.view.frame.width, dy: 0)
             view.addSubview(newVC.view, positioned: .below, relativeTo: oldVC.view)
             NSAnimationContext.runAnimationGroup({ context in
                context.duration = 0.23
                context.allowsImplicitAnimation = true
                context.timingFunction = .easeIn
                oldVC.view.animator().frame = endFrame
                oldVC.view.animator().alphaValue = 0.85
             }) {
                self.unembedChildViewController(oldVC)
             }
          } else {
             unembedChildViewController(oldVC)
          }
          return oldVC
       }
    
    }
    

    Reusable extensions:

    extension NSViewController {
    
       private struct OBJCAssociationKey {
          static var navigationController = "com.mc.navigationController"
       }
    
       public var navigationController: NavigationController? {
          get {
             return ObjCAssociation.value(from: self, forKey: &OBJCAssociationKey.navigationController)
          } set {
             ObjCAssociation.setAssign(value: newValue, to: self, forKey: &OBJCAssociationKey.navigationController)
          }
       }
    }
    
    extension NSViewController {
    
       public func embedChildViewController(_ vc: NSViewController, container: NSView? = nil) {
          addChildViewController(vc)
          vc.view.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
          vc.view.autoresizingMask = [.height, .width]
          (container ?? view).addSubview(vc.view)
       }
    
       public func unembedChildViewController(_ vc: NSViewController) {
          vc.view.removeFromSuperview()
          vc.removeFromParentViewController()
       }
    }
    
    struct ObjCAssociation {
    
       static func value<T>(from object: AnyObject, forKey key: UnsafeRawPointer) -> T? {
          return objc_getAssociatedObject(object, key) as? T
       }
    
       static func setAssign<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
          objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_ASSIGN)
       }
    
       static func setRetainNonAtomic<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
          objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
       }
    
       static func setCopyNonAtomic<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
          objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_COPY_NONATOMIC)
       }
    
       static func setRetain<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
          objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_RETAIN)
       }
    
       static func setCopy<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
          objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_COPY)
       }
    }
    
    0 讨论(0)
提交回复
热议问题