用于处理 Swift 应用程序中动作的抽象概念。
请注意,目前的 API 仍在变动中。
尽管我使用了语义化版本号,但在一切稳定下来之前,我会滥用它们 - 因此,新的维护版本可能包含破坏性更改。这仅仅是为了避免过早地到达 10.x 版本!
动作是执行某些工作的离散片段。这可能是修改模型,或执行用户界面动作,实际上并不重要。
动作使用标识符向 ActionManager 注册。然后通过 ActionManager 使用相同的标识符来调用它们。
动作彼此之间以及与其不需要执行其特定任务的任何事物之间解耦。
当执行一个动作时,它会传递一个上下文。其中包含它执行其动作所需的所有信息,并且是确保耦合松散和动态的主要机制。
提供给动作的上下文由响应链中的项目填充。 这样,它实际上取决于用户界面上下文 - 哪个窗口位于前面,哪个项目具有焦点等等。 只要响应链中的某些内容提供正确的上下文,就可以在许多不同的情况下调用相同的操作。 Swift 的类型安全在这里有所帮助,可以轻松地从上下文中提取相关参数,确保它们是正确的类型。
Actions 模块不依赖于 AppKit/UIKit。 它可用于实现命令行应用程序的动作,或在非 Apple 平台上实现动作。
另一方面,ActionsKit 模块构建在 Actions 之上,并将其集成到 AppKit 或 UIKit 的响应链中。
这允许您将 UI 按钮、菜单等绑定到将 performAction
选择器发送到响应链,并让动作管理器选择它们,推断要执行的动作并执行它。它还实现了一些验证支持。
尚未提供撤销支持,但将会添加。
创建一个 ActionManager
,将其附加到全局对象(例如,您的应用程序委托),并向其注册一些动作。
如果要将 UI 项目绑定到它,请使用 ActionManagerMac
或 ActionManagerMobile
子类之一,并通过调用 installResponder
将其挂钩到响应链中。
class Application: NSObject, NSApplicationDelegate {
let actionManager = ActionManagerMac()
func applicationWillFinishLaunching(_ notification: Notification) {
actionManager.register([
MyAction(identifier: "MyAction"),
AnotherAction(identifier: "AnotherAction")
])
actionManager.installResponder()
}
将用户界面对象的动作设置为 performAction(_ sender: Any)
,并将目标设置为第一响应者。将 UI 项目的标识符设置为要调用的动作的标识符。
或者,使用 actionManager.perform("MyAction")
直接调用动作。
动作是类。
要定义一个动作,从 Action
继承,并实现 perform
。
class MyAction: PersonAction {
override func perform(context: ActionContext) {
// do stuff here
}
}
传递给 perform
的 context
包含原始的 sender。
它还包含一个其他信息的字典。 响应链中的项目可以在执行动作时向此字典添加项目,方法是实现 ActionContextProvider
协议。
这使视图控制器或窗口控制器可以将基本信息传递给动作,同时保持它们完全解耦。
要调用的动作可以显式传递,也可以从用户界面项目的标识符中解析出来。
用户界面标识符可以采用以下形式:{prefix.}action{("key": "value", "key2": "value2")}
。
可选的前缀(将被丢弃)可以是任何字符串,并且可以包含句点。
这允许您将多个用户界面项目绑定到同一个动作,而无需为它们提供完全相同的标识符字符串(如果它们不是唯一的,Xcode 会报错)。 以下所有标识符都将调用 MyAction
动作:MyAction
、button.MyAction
、menu.MyAction
、some.other.thing.MyAction
。
如果动作后存在括号,则其内容被解释为要添加到上下文的键值对列表。 例如,两个按钮可能具有标识符 MyAction("color": "red")
和 MyAction("color": "blue")
。 两者都会调用 MyAction
动作,但 context["color"]
的值将分别设置为 red
或 blue
。
目前,这些参数的解析方式就像它们是一个 JSON 字典一样,因此键和值都需要用引号引起来。 Xcode 实际上抱怨标识符中存在 "
和 :
字符,因此未来的版本可能会取消此限制,并允许您简单地指定 (key: value, key2: value2)
。
动作通常仅在某些情况下有效 - 例如,当选择了一些文本时 - 或者它们希望根据上下文更改其名称或可见性。
要执行验证,请覆盖 validate(context: ActionContext) -> Validation
并检查传入的上下文。
使用 Mac 或 iOS 动作管理器时,会自动为某些用户界面动作调用验证。 在其他情况下,您可以根据需要手动调用它。 自动验证几乎总是合适的,但是如果您想跳过它的情况,可以通过安排上下文包含 skipValidation
的 true 值来实现。
如果您的用户界面想知道何时执行了某些动作,则此模式可能很有用。
为您的动作的观察者定义一个协议。 这可以包含您需要的任何东西。
在要观察的用户界面控制器中实现您的协议。 还要实现 ActionContextProvider
协议,并将控制器附加到动作将读取的键。
protocol MyActionObserver {
func myMethod(myArgument: String)
}
extension MyViewController: ActionContextProvider, MyActionObserver {
func provide(context: ActionContext) {
context.append(key: "MyActionObserver", value: self)
}
在该动作中,除了执行实际工作之外,还要枚举观察者键。 对于每个观察者,调用协议中的一个方法,传递任何相关的参数或上下文。
class MyAction: PersonAction {
override func perform(context: ActionContext) {
// do some stuff here
// notify observers
context.forEach(key: "MyActionObserver") { (observer: MyActionObserver) in
observer.myMethod(myArgument: "myValue")
}
}
}