自定义NSStoryboard不要将NSViewController添加到响应者链

时间:2017-04-13 20:40:05

标签: swift xcode macos-sierra nsviewcontroller nsresponder

总体目标:在macOS上使用自定义故事板segue

手头的问题是响应者链。根据Apple的说法:

  

此外,在macOS 10.10及更高版本中,视图控制器参与   在响应者链中。 NSViewController

如果我在macOS情节提要中使用标准segue ,则目标视图控制器的行为与预期相同 - 焦点,等效键,响应者链等。

但是,如果我使用自定义segue ,则目标视图控制器的行为不符合预期:(具体而言,密钥等效项无法正常工作 - 但是,在测试中,来自来源视图控制器的按钮上的等效键继续工作(这不是有用/不需要的)

在macOS 10.12上,我使用了以下自定义信号......我做错了什么?

class PushSegue: NSStoryboardSegue {

override func perform() {
    (sourceController as AnyObject).presentViewController(destinationController as! NSViewController, animator: PushAnimator())
}
}


class PushAnimator:  NSObject, NSViewControllerPresentationAnimator  {

func animatePresentation(of viewController: NSViewController, from fromViewController: NSViewController) {
    viewController.view.wantsLayer = true
    viewController.view.frame = CGRect(x: fromViewController.view.frame.size.width, y: 0,
                                       width: fromViewController.view.frame.size.width, height: fromViewController.view.frame.size.height)
    fromViewController.addChildViewController(viewController)
    fromViewController.view.addSubview(viewController.view)

    let futureFrame = CGRect(x: 0, y: 0, width: viewController.view.frame.size.width,
                             height: viewController.view.frame.size.height)

    NSAnimationContext.runAnimationGroup({ context in
        context.duration = 0.75
        viewController.view.animator().frame = futureFrame

    }, completionHandler:nil)
}

func animateDismissal(of viewController: NSViewController, from fromViewController: NSViewController) {
    let futureFrame = CGRect(x: viewController.view.frame.size.width, y: 0,
                             width: viewController.view.frame.size.width, height: viewController.view.frame.size.height)

    NSAnimationContext.runAnimationGroup({ context in
        context.duration = 0.75
        context.completionHandler = {
            viewController.view.removeFromSuperview()
            viewController.removeFromParentViewController()
        }

        viewController.view.animator().frame = futureFrame
    }, completionHandler: nil)

}
}

1 个答案:

答案 0 :(得分:8)

从我理解的方式来看,你的问题有四个部分:

  1. 如何使用自定义segue类
  2. KeyEquivalents
  3. 的问题
  4. 响应者链
  5. 的问题
  6. 焦点
  7. 的问题

    解决方案演示

    我让以下示例正确运行自定义segue类等效键示例,响应者链示例和< strong>焦点示例(假设您的意思是TextField焦点)。

    正如您在下面的gif中所看到的,您可以点击 Segue 按钮,使用您的PushSegue segue进行转换。然后点击关闭按钮返回。右箭头和左箭头分别是 Segue Dismiss 按钮的等效键。响应者链成功地将数据从SheetController(红色屏幕)传递到MainViewController(灰色屏幕)。此外,textFields在segueing时正确聚焦。我将进一步详细解释每个功能。

    enter image description here

    PushSegue

    我带了您发布的PushAnimator子类,只是在视图中添加了红色背景,以便我可以直观地看到它。我是通过在animatePresentation类的PushAnimator方法中添加以下行来完成此操作的:

    viewController.view.layer?.backgroundColor = CGColor.init(red: 1, green: 0, blue: 0, alpha: 1)
    

    关键等价物

    Segue 按钮被指定为NSRightArrowFunctionKey的等效键,关闭按钮被指定为NSLeftArrowFunctionKey。您可以点击向右和向左箭头键在两个视图之间进行切换。要设置右箭头,我在MainViewController viewDidLoad()函数中添加了以下内容:

    let array = [unichar(NSRightArrowFunctionKey)]
    mainViewSegueButton.keyEquivalent = String(utf16CodeUnits: array, count: 1)
    

    要设置左箭头,我在SheetController viewDidLoad()函数中添加了以下内容:

    let array = [unichar(NSLeftArrowFunctionKey)]
    dismissButton.keyEquivalent = String(utf16CodeUnits: array, count: 1)
    

    响应者链

    由于您没有描述您对响应者链的具体问题,我只是实施了一个简单的测试,看看它是否有效。我将SheetController的{​​{1}}设置为dismissButton的nextResponder。我在MainViewController的{​​{1}}:

    中有以下内容
    SheetController

    要解释上述内容:我首先使用辅助功能标签设置viewDidLoad(),以便// SheetController viewDidLoad() // Set Accessibility Label dismissButton.setAccessibilityLabel("DismissButton") // Set button target and action dismissButton.target = nil dismissButton.action = #selector(MainViewController.dismissAction(_:)) // Set nextResponder dismissButton.nextResponder = self.parent 稍后可以识别该按钮。将按钮的目标设置为nil意味着它将查看其nextResponder以处理它检测到的任何事件。我将按钮的操作设置为dismissButton方法,该方法在MainViewController内声明。最后,我将dismissAction的nextResponder设置为MainViewController

    回到dismissButton,我在下面设置了MainViewController方法。它只是增加一个计数器变量,并将该计数器与按钮的accessibilityLabel(我们之前设置的相同访问标签)一起显示到textField。最后一行驳回了MainViewController

    dismissAction

    焦点

    为此,我假设您指的是textField焦点。在SheetController中,我// MainViewController func dismissAction(_ sender: AnyObject) { counter += 1 let buttonAccessLabel = (sender as! NSButton).accessibilityLabel() helloWorldTextField.stringValue = "Action #" + counter.description + " from: " + buttonAccessLabel! self.dismissViewController(self.presentedViewControllers!.first!) } 焦点在SheetController

    sheetFocusTextField

    viewDidAppear中,override func viewDidAppear() { super.viewDidAppear() sheetFocusTextField.becomeFirstResponder() } 方法有MainViewController个焦点:

    mainFocusTextField

    Github Link

    https://github.com/starkindustries/MacOSSegueTest

    参考