swift - 自定义 NSStoryboardSegue 未将 NSViewController 添加到响应链

标签 swift xcode macos-sierra nsviewcontroller nsresponder

总体目标:在 macOS 上使用自定义 Storyboard segue

手头的问题是响应者链。据 Apple 称:

In addition, in macOS 10.10 and later, a view controller participates in the responder chain. NSViewController

如果我在 macOS Storyboard中使用 standard segue,目标 View Controller 的行为符合预期——焦点、等效键、响应链等。

但是,如果我使用自定义 segue目标 View Controller 不会按预期运行 :( 具体来说,关键等效项神秘地无法工作 — 然而,在测试中,来自 source View Controller 的按钮上的等效键继续工作(这没有帮助/不需要)

在 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. 如何使用自定义 segue 类
  2. KeyEquivalents 的问题
  3. 响应链的问题
  4. 焦点的问题

解决方案演示

我有以下示例与您的自定义 segue 类、一个等效键示例、一个响应链示例和一个< strong>focus 示例(假设您指的是 TextField 焦点)。

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

enter image description here

推送Segue

我使用了您发布的 PushAnimator 子类,并向 View 添加了红色背景,以便我可以直观地看到它。为此,我在 PushAnimator 类的 animatePresentation 方法中添加了以下行:

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

等价键

Segue 按钮分配了 NSRightArrowFunctionKey 的 keyEquivalent,Dismiss 按钮分配了 NSLeftArrowFunctionKey。您可以点击向右和向左箭头键在两个 View 之间切换。为了设置右箭头,我在 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)

响应链

由于您没有描述您在响应链中遇到的具体问题,我只是实现了一个简单的测试以查看它是否有效。我将使用 MainViewController 的 nextResponder 设置 SheetControllerdismissButton。我在 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

为了解释以上内容:我首先设置了带有辅助功能标签的 dismissButton,以便 MainViewController 稍后可以识别该按钮。将按钮的目标设置为 nil 意味着它将寻找 nextResponder 来处理它检测到的任何事件。我将按钮的操作设置为 dismissAction 方法,该方法在 MainViewController 中声明。最后,我将 dismissButton 的 nextResponder 设置为 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!)
}

专注

为此,我假设您指的是 textField 焦点。在 SheetController 中,我将 sheetFocusTextField 焦点放在 viewDidAppear 中:

override func viewDidAppear() {
    super.viewDidAppear()
    sheetFocusTextField.becomeFirstResponder()
}

MainViewController 中,我在 dismissAction 方法中有 mainFocusTextField 焦点:

mainFocusTextField.becomeFirstResponder()

Github 链接

https://github.com/starkindustries/mac-os-segue-test

引用资料

关于swift - 自定义 NSStoryboardSegue 未将 NSViewController 添加到响应链,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43401324/

相关文章:

ios - SpriteKit SKView 显示字段内存使用情况

iphone - 将 UIImage 制作成圆形

ios - 在 iOS 上上传到服务器之前调整图像大小

macos - 在 mac 上使用自定义 vimrc 会导致语法高亮丢失

macos - 以编程方式访问网络浏览器选项卡 | swift 3

ios - CapsPageMenu 隐藏 TableView 上的 NavigationBar 从子 UIViewController 滚动不工作

swift - 如何检查某个位置是否位于多边形的 "accuracy"范围内。- 例如如果函数的精度是 30m

xcode - 快速 UIActivityIndi​​catorView 建议

swift - 无法对 Mac 应用程序进行代码签名

ios - 我可以从我的 iOS 应用程序启用/禁用移动数据吗?