我想装饰 UIViewController,使其能够在从另一个类(例如网络状态管理器)调用
setInteractionEnabled
方法时调整其界面。所有更改(如果有)应通过覆盖 onInteractionChanged
在具体控制器中提供。这是我的代码:
import Foundation
typealias InteractionClosure = ((enabled: Bool) -> Void)
protocol Interaction: class {
var onInteractionChanged: InteractionClosure? { get set }
func setInteractionEnabled(enabled: Bool)
}
extension Interaction where Self: UIViewController {
// Default: Do nothing
// Throws: - Extensions may not contain stored properties
var onInteractionChanged: InteractionClosure? = nil
func setInteractionEnabled(enabled: Bool) {
onInteractionChanged?(enabled: enabled)
}
}
extension UIViewController : Interaction {}
如何添加默认实现
onInteractionChanged
?
回答我自己的问题通常是我不做的事情,但这是我的解决方案:
typealias InteractionClosure = (enabled: Bool) -> Void
protocol Interaction: class {
func addOnInteractionChanged(closure: InteractionClosure)
func setInteractionEnabled(enabled: Bool)
}
extension Interaction where Self: UIViewController {
func addOnInteractionChanged(closure: InteractionClosure) {
onInteractionChanged = closure
}
func setInteractionEnabled(enabled: Bool) {
onInteractionChanged?(enabled: enabled)
}
// MARK: - Private
private var onInteractionChanged: InteractionClosure? {
get {
let wrapper =
objc_getAssociatedObject(self, &icAssociationKey) as? ClosureWrapper
return wrapper?.closure
}
set(newValue) {
objc_setAssociatedObject(self,
&icAssociationKey,
ClosureWrapper(newValue),
.OBJC_ASSOCIATION_RETAIN)
}
}
}
extension UIViewController : Interaction {}
// Helpers
private var icAssociationKey: UInt8 = 0
private class ClosureWrapper {
var closure: InteractionClosure?
init(_ closure: InteractionClosure?) {
self.closure = closure
}
}
客户端类:
class LoginViewController: UIViewController {
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.setup()
}
// MARK: - Private
private func setup() {
// ...
addOnInteractionChanged { [unowned self] (enabled) in
self.signInButton.enabled = enabled
self.activityIndicatorView.hidden = !enabled
}
}
}
经理班:
visibleViewController?.setInteractionEnabled(true)
如果您希望属性仅具有
{ get }
能力,您可以使用:
protocol TestProtocol {
var testClosure: ((_ parameter: Bool) -> Void)? { get }
}
extension TestProtocol {
var testClosure: ((_ parameter: Bool) -> Void)? {
return { parameter in
print(parameter)
}
}
}
在协议中使用闭包的现代-Swift 紧凑方式如下
typealias InteractionClosure = (Bool) -> Void
protocol Interaction {
var interactionClosure: InteractionClosure? { get set }
}
class SomeViewController: UIViewController { }
extension SomeViewController: Interaction {
private struct AssociatedKeys {
static var interactionClosure: UInt8 = 0
}
var interactionClosure: InteractionClosure? {
get {
let wrapper =
objc_getAssociatedObject(self, &AssociatedKeys.interactionClosure) as? InteractionClosure
return wrapper
}
set(newValue) {
objc_setAssociatedObject(self, &AssociatedKeys.interactionClosure, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
这里我们没有任何全局变量和补充函数。
并且我们不使用默认实现的协议扩展,因为我们需要将此闭包设置为不同的
UIViewController
实例。
如果我们使用全局 var 作为关联值的键,那么我们将用第二个实例覆盖第一个实例的闭包设置器。
默认实现也不是那么明显,大多数时候必须与
self
上下文相关联(只有全局抽象默认值是好的)。