You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:13:49