Swift中非KVC兼容对象通信方案重构:自定义视图监听UIControl子类属性变化
嘿,我来帮你搞定这个问题!你之前用协议代理的方案踩坑,大概率是因为不小心把代理逻辑和KVC混在一起导致了冲突,咱们换几个更靠谱的思路来实现需求:
方案一:使用闭包(Closure)实现一对一回调
这个方案轻量直接,适合单个自定义视图监听单个UIControl的场景,完全避开KVC的坑:
// 你的UIControl子类 class CustomControl: UIControl { // 定义闭包,用来传递属性变更/操作触发的通知 var onStateChanged: (() -> Void)? // 假设这是你要监听的自定义属性 var customToggle: Bool = false { didSet { // 属性变更时触发闭包 onStateChanged?() } } // 也可以在UIControl的原生操作(比如点击)时触发 override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesEnded(touches, with: event) onStateChanged?() } } // 你的自定义展示视图 class CustomDisplayView: UIView { func bind(to control: CustomControl) { // 绑定闭包,注意用[weak self]避免循环引用 control.onStateChanged = { [weak self] in // 在这里处理视图展示逻辑,比如显示自己 self?.isHidden = false } } }
方案二:使用现代KVO(键值观察)监听属性变化
如果必须监听UIControl的属性(包括系统原生属性比如isSelected、enabled),可以用Swift的现代KVO,它比旧版API更安全,能避免“不兼容KVC”的错误——前提是你的自定义属性要正确暴露给KVC:
// UIControl子类:自定义属性必须加@objc dynamic修饰,才能被KVO监听 class CustomControl: UIControl { @objc dynamic var customProperty: Bool = false } // 自定义展示视图 class CustomDisplayView: UIView { // 存储KVO监听对象,避免被提前释放 private var propertyObservation: NSKeyValueObservation? func setupObserver(for control: CustomControl) { // 监听自定义属性的变化 propertyObservation = control.observe(\.customProperty, options: [.new]) { [weak self] _, change in if let newValue = change.newValue, newValue { self?.isHidden = false } } // 也可以监听UIControl的原生属性,比如isSelected // propertyObservation = control.observe(\.isSelected, options: [.new]) { ... } } deinit { // 手动销毁监听(现代KVO会自动处理,但显式调用更稳妥) propertyObservation?.invalidate() } }
方案三:使用NotificationCenter实现一对多监听
如果需要多个视图同时监听同一个UIControl的变化,通知中心是更合适的选择:
// 定义专属通知名称,避免和其他通知冲突 extension Notification.Name { static let controlStateUpdated = Notification.Name("ControlStateUpdated") } // UIControl子类 class CustomControl: UIControl { var customProperty: Bool = false { didSet { // 发送通知,还可以通过userInfo传递额外数据 NotificationCenter.default.post( name: .controlStateUpdated, object: self, userInfo: ["newValue": customProperty] ) } } } // 自定义展示视图 class CustomDisplayView: UIView { func registerNotification(for targetControl: CustomControl?) { // 注册通知,如果只想监听特定Control,把object参数传进去 NotificationCenter.default.addObserver( self, selector: #selector(handleControlUpdate(_:)), name: .controlStateUpdated, object: targetControl ) } @objc private func handleControlUpdate(_ notification: Notification) { if let newValue = notification.userInfo?["newValue"] as? Bool, newValue { self.isHidden = false } } deinit { // 移除所有监听,避免内存泄漏 NotificationCenter.default.removeObserver(self) } }
方案四:使用Combine框架(iOS13+)实现响应式监听
如果你的项目支持iOS13及以上,Combine是最现代的响应式方案,代码简洁且自动管理生命周期:
import Combine // UIControl子类:用@Published包装需要监听的属性 class CustomControl: UIControl { @Published var customProperty: Bool = false } // 自定义展示视图 class CustomDisplayView: UIView { // 存储订阅对象,避免被提前释放 private var cancellables = Set<AnyCancellable>() func bind(to control: CustomControl) { // 订阅属性变化,确保在主线程更新UI control.$customProperty .receive(on: DispatchQueue.main) .sink { [weak self] newValue in self?.isHidden = !newValue } .store(in: &cancellables) } }
方案选择建议
- 一对一简单场景:优先闭包或Combine(iOS13+)
- 监听系统UIControl属性:现代KVO或Combine
- 一对多场景:NotificationCenter
内容的提问来源于stack exchange,提问作者sefiroth974
相关产品推荐
相关产品推荐

