Swift macOS SegmentedControl 操作未被调用
Swift macOS SegmentedControl Action not getting called
描述
我正在尝试使用 NSSegmentedControls
在子视图控制器之间进行转换。 ParentViewController
位于Main.storyboard
,ChildViewControllers
位于Assistant.storyboard
。每个 ChildViewController 都有一个 SegmentedControl,分为 2 个 Segment,它们的主要用途是在 ChildViewController 之间导航。所以他们被设置为 momentaryPushIn
而不是 selectOne。每个 ChildViewController 使用 Delegate 与 ParentViewController 通信。
所以在 ParentViewController 中我添加了 ChildViewControllers 如下:
/// The View of the ParentViewController configured as NSVisualEffectView
@IBOutlet var visualEffectView: NSVisualEffectView!
var assistantChilds: [NSViewController] {
get { return [NSViewController]() }
set(newValue) {
for child in newValue { self.addChild(child) }
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
addAssistantViewControllersToChildrenArray()
}
override func viewWillAppear() {
visualEffectView.addSubview(self.children[0].view)
self.children[0].view.frame = self.view.bounds
}
private func addAssistantViewControllersToChildrenArray() -> Void {
let storyboard = NSStoryboard.init(name: "Assistant", bundle: nil)
let exampleChild = storyboard.instantiateController(withIdentifier: "ExampleChild") as! ExampleChildViewController
let exampleSibling = storyboard.instantiateController(withIdentifier: "ExampleSibling") as! ExampleSiblingViewController
exampleChild.navigationDelegate = self
exampleSibling.navigationDelegate = self
assistantChilds = [exampleChild, exampleSibling]
}
到目前为止一切顺利。 ExampleChildViewController
有一个 NSTextField
实例。当我在 TextField 的范围内时,我可以触发 SegmentedControls 的动作。它应该向前和向后导航。但是一旦我离开 TextField 的范围,我仍然可以单击 Segments,但它们不会触发任何操作。即使 TextField 不是应用程序的当前 "First Responder",它们也应该能够向前和向后导航。我想我在这里遗漏了一些东西,我希望任何人都可以帮助我。我知道问题不在于 NSSegmentedControl
,因为我在 SiblingViewController 中看到与配置为 Switch/Checkbox 的 NSButton
相同的行为。我只是不知道我做错了什么。
这是我第一次在这里问自己问题,所以我希望我正在做的事情能够在解决方案上取得进展。让我知道我是否可以做某事 better/different 或者我是否需要提供有关某事的更多信息。
提前致谢!
Additional Information
For the sake of completeness:
The ParentViewController
itself is embedded in a ContainerView
,
which is owned by the RootViewController
. I can't imagine this does
matter in any way, but this way we are not missing something out.
I am actually not showing the navigation action, because I want to
keep it as simple as possible. Furthermore the action is not problem,
it does what I want it to do. Correct me if I am wrong with this.
我在研究时发现的可能解决方案, 对我不起作用:
- Setting window.delegate of the ChildViewControllers to NSApp.windows.first?.delegate
- Setting the ChildViewController to
becomeFirstResponder
in its func viewWillAppear()
visualEffectView.addSubview(self.children[0].view, positioned: NSWindow.OrderingMode.above, relativeTo: nil
)
相关problems/topics我在研究中发现:
- Adding and Removing Child View Controllers
- How to use Child View Controllers in Swift 4.0 programmatically
- Container View Controllers
- Control a NSTabViewController from parent View
- How to detect when NSTextField has the focus or is it`s content selected cocoa
解决方案
let parentViewControllerInstance = self.parent as! ParentViewController
segmentedControl.target = parentViewControllerInstance
在我的例子中,我只需要将委托设置为 sendAction
方法的目标。
背景
好的,在阅读 AppKit 文档数小时后,我现在可以回答我自己的问题了。
首先调试UI发现问题肯定不在ViewHierarchy
所以我试着去思考NSButton和NSSegmentedControl的本质。在某些时候,我注意到两者都是 NSControl 的子类。
class NSSegmentedControl : NSControl
class NSButton : NSControl
AppKit 文档说:
Discussion
Buttons are a standard control used to initiate actions within your app. You can configure buttons with many different visual styles, but the behavior is the same. When clicked, a button calls the action method of its associated target object. (...) You use the action method to perform your app-specific tasks.
粗体文本指向解决方案的关键 - 其关联目标对象。通常我这样定义控制项的动作:
button.action = #selector(someFunc(_:))
这导致 NSControl 实例调用这个:
func sendAction(_ action: Selector?, to target: Any?) -> Bool
文档中的参数说明:
Parameters
theAction
The selector to invoke on the target. If the selector is NULL, no message is sent.
theTarget
The target object to receive the message. If the object is nil, the application searches the responder chain for an object capable of handling the message. For more information on dispatching actions, see the class description for NSActionCell.
总之,触发动作方法(在我的例子中是 NSSegmentedControl)的 NSControl 实例没有发送动作的目标。所以它只能通过响应链发送它的动作方法——当第一个响应者位于另一个视图时显然是 nil。
描述
我正在尝试使用 NSSegmentedControls
在子视图控制器之间进行转换。 ParentViewController
位于Main.storyboard
,ChildViewControllers
位于Assistant.storyboard
。每个 ChildViewController 都有一个 SegmentedControl,分为 2 个 Segment,它们的主要用途是在 ChildViewController 之间导航。所以他们被设置为 momentaryPushIn
而不是 selectOne。每个 ChildViewController 使用 Delegate 与 ParentViewController 通信。
所以在 ParentViewController 中我添加了 ChildViewControllers 如下:
/// The View of the ParentViewController configured as NSVisualEffectView
@IBOutlet var visualEffectView: NSVisualEffectView!
var assistantChilds: [NSViewController] {
get { return [NSViewController]() }
set(newValue) {
for child in newValue { self.addChild(child) }
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
addAssistantViewControllersToChildrenArray()
}
override func viewWillAppear() {
visualEffectView.addSubview(self.children[0].view)
self.children[0].view.frame = self.view.bounds
}
private func addAssistantViewControllersToChildrenArray() -> Void {
let storyboard = NSStoryboard.init(name: "Assistant", bundle: nil)
let exampleChild = storyboard.instantiateController(withIdentifier: "ExampleChild") as! ExampleChildViewController
let exampleSibling = storyboard.instantiateController(withIdentifier: "ExampleSibling") as! ExampleSiblingViewController
exampleChild.navigationDelegate = self
exampleSibling.navigationDelegate = self
assistantChilds = [exampleChild, exampleSibling]
}
到目前为止一切顺利。 ExampleChildViewController
有一个 NSTextField
实例。当我在 TextField 的范围内时,我可以触发 SegmentedControls 的动作。它应该向前和向后导航。但是一旦我离开 TextField 的范围,我仍然可以单击 Segments,但它们不会触发任何操作。即使 TextField 不是应用程序的当前 "First Responder",它们也应该能够向前和向后导航。我想我在这里遗漏了一些东西,我希望任何人都可以帮助我。我知道问题不在于 NSSegmentedControl
,因为我在 SiblingViewController 中看到与配置为 Switch/Checkbox 的 NSButton
相同的行为。我只是不知道我做错了什么。
这是我第一次在这里问自己问题,所以我希望我正在做的事情能够在解决方案上取得进展。让我知道我是否可以做某事 better/different 或者我是否需要提供有关某事的更多信息。
提前致谢!
Additional Information
For the sake of completeness:
The
ParentViewController
itself is embedded in aContainerView
, which is owned by theRootViewController
. I can't imagine this does matter in any way, but this way we are not missing something out.
I am actually not showing the navigation action, because I want to keep it as simple as possible. Furthermore the action is not problem, it does what I want it to do. Correct me if I am wrong with this.
我在研究时发现的可能解决方案, 对我不起作用:
- Setting window.delegate of the ChildViewControllers to NSApp.windows.first?.delegate
- Setting the ChildViewController to
becomeFirstResponder
in itsfunc viewWillAppear()
visualEffectView.addSubview(self.children[0].view, positioned: NSWindow.OrderingMode.above, relativeTo: nil
)
相关problems/topics我在研究中发现:
- Adding and Removing Child View Controllers
- How to use Child View Controllers in Swift 4.0 programmatically
- Container View Controllers
- Control a NSTabViewController from parent View
- How to detect when NSTextField has the focus or is it`s content selected cocoa
解决方案
let parentViewControllerInstance = self.parent as! ParentViewController
segmentedControl.target = parentViewControllerInstance
在我的例子中,我只需要将委托设置为 sendAction
方法的目标。
背景
好的,在阅读 AppKit 文档数小时后,我现在可以回答我自己的问题了。
首先调试UI发现问题肯定不在ViewHierarchy
所以我试着去思考NSButton和NSSegmentedControl的本质。在某些时候,我注意到两者都是 NSControl 的子类。
class NSSegmentedControl : NSControl
class NSButton : NSControl
AppKit 文档说:
Discussion
Buttons are a standard control used to initiate actions within your app. You can configure buttons with many different visual styles, but the behavior is the same. When clicked, a button calls the action method of its associated target object. (...) You use the action method to perform your app-specific tasks.
粗体文本指向解决方案的关键 - 其关联目标对象。通常我这样定义控制项的动作:
button.action = #selector(someFunc(_:))
这导致 NSControl 实例调用这个:
func sendAction(_ action: Selector?, to target: Any?) -> Bool
文档中的参数说明:
Parameters
theAction
The selector to invoke on the target. If the selector is NULL, no message is sent.
theTarget
The target object to receive the message. If the object is nil, the application searches the responder chain for an object capable of handling the message. For more information on dispatching actions, see the class description for NSActionCell.
总之,触发动作方法(在我的例子中是 NSSegmentedControl)的 NSControl 实例没有发送动作的目标。所以它只能通过响应链发送它的动作方法——当第一个响应者位于另一个视图时显然是 nil。