UINavigationController-Alike for Desktop-Cocoa?

后端 未结 3 1114
终归单人心
终归单人心 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 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(from object: AnyObject, forKey key: UnsafeRawPointer) -> T? {
          return objc_getAssociatedObject(object, key) as? T
       }
    
       static func setAssign(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
          objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_ASSIGN)
       }
    
       static func setRetainNonAtomic(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
          objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
       }
    
       static func setCopyNonAtomic(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
          objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_COPY_NONATOMIC)
       }
    
       static func setRetain(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
          objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_RETAIN)
       }
    
       static func setCopy(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
          objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_COPY)
       }
    }
    

提交回复
热议问题