自定义NSStoryboard不会将NSViewController添加到Responder Chain

Zel*_*lko 3 xcode nsviewcontroller nsresponder swift macos-sierra

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

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

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

如果我在macOS故事板中使用标准segue,则目标视图控制器的行为与预期一致 - 焦点,键等效,响应者链等.

但是,如果我使用自定义segue,目标视图控制器不会按预期运行:(具体而言,密钥等效项神秘地无法工作 - 但是,在测试中,来自视图控制器的按钮上的等效键继续工作(这不是' t help/desired)

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

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)

}
}
Run Code Online (Sandbox Code Playgroud)

Zio*_*rez 8

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

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

解决方案演示

我有以下示例与您的自定义segue类,关键等效示例,响应者链示例和焦点示例(假设您的意思是TextField焦点)正确运行.

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

在此输入图像描述

PushSegue

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

viewController.view.layer?.backgroundColor = CGColor.init(red: 1, green: 0, blue: 0, alpha: 1)
Run Code Online (Sandbox Code Playgroud)

关键等价物

Segue公司按钮分配的keyEquivalent NSRightArrowFunctionKey辞退按钮分配NSLeftArrowFunctionKey.您可以点击向右和向左箭头键在两个视图之间进行切换.要设置右箭头,我在MainViewController viewDidLoad()函数中添加了以下内容:

let array = [unichar(NSRightArrowFunctionKey)]
mainViewSegueButton.keyEquivalent = String(utf16CodeUnits: array, count: 1)
Run Code Online (Sandbox Code Playgroud)

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

let array = [unichar(NSLeftArrowFunctionKey)]
dismissButton.keyEquivalent = String(utf16CodeUnits: array, count: 1)
Run Code Online (Sandbox Code Playgroud)

响应链

既然你没有描述你对响应者链的具体问题,我只是实现了一个简单的测试,看看它是否有效.我将设置SheetControllerdismissButton用的nextResponder MainViewController.我有以下中SheetControllerviewDidLoad():

// 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
Run Code Online (Sandbox Code Playgroud)

为了解释上述内容:我首先dismissButton使用辅助功能标签进行设置,以便MainViewController稍后识别该按钮.将按钮的目标设置为nil意味着它将查看其nextResponder以处理它检测到的任何事件.我将按钮的动作设置为dismissAction方法,该方法在其中声明MainViewController.最后,我将dismissButtonnextResponder 设置为MainViewController.

回到后面MainViewController,我设置了dismissAction下面的方法.它只是增加一个计数器变量,并将该计数器与按钮的accessibilityLabel(我们之前设置的相同访问标签)一起显示到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!)
}
Run Code Online (Sandbox Code Playgroud)

焦点

为此,我假设您指的是textField焦点.在SheetController,我的sheetFocusTextField重点是viewDidAppear:

override func viewDidAppear() {
    super.viewDidAppear()
    sheetFocusTextField.becomeFirstResponder()
}
Run Code Online (Sandbox Code Playgroud)

MainViewController,我mainFocusTextField关注的dismissAction方法:

mainFocusTextField.becomeFirstResponder()
Run Code Online (Sandbox Code Playgroud)

Github Link

https://github.com/starkindustries/MacOSSegueTest

参考