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

如何为UIStepper实现观察者设计模式以同步更新多个自定义子视图

实现方案

核心逻辑是通过观察者模式,让VC持有可被观测的计数器属性,数值变更时自动通知所有订阅方,子视图接收到新值后自行刷新UI即可。以下是两种常用的落地实现:


方案1:使用Combine框架(推荐,iOS 13+支持)

类型安全,无需手动管理观察者释放,是目前iOS生态下观察者模式的首选实现。

步骤1:VC层配置可观测计数器与UIStepper绑定

import UIKit
import Combine

class MainViewController: UIViewController {
    // @Published 注解会自动在值变化时对外发布变更事件
    @Published var counter: Int = 0
    // 存储订阅关系,避免订阅被提前释放
    private var cancellables = Set<AnyCancellable>()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupStepper()
        setupChildViews()
    }
    
    private func setupStepper() {
        let stepper = UIStepper()
        stepper.minimumValue = 0
        stepper.maximumValue = 100
        stepper.stepValue = 1
        stepper.addTarget(self, action: #selector(stepperDidChange(_:)), for: .valueChanged)
        // 此处省略stepper的布局、添加到父视图代码
        view.addSubview(stepper)
    }
    
    @objc private func stepperDidChange(_ sender: UIStepper) {
        // 赋值操作会自动触发@Published的事件发布
        counter = Int(sender.value)
    }
    
    private func setupChildViews() {
        let firstView = FirstCustomView()
        let secondView = SecondCustomView()
        // 此处省略两个子视图的布局、添加到父视图代码
        view.addSubview(firstView)
        view.addSubview(secondView)
        
        // 给第一个子视图绑定计数器更新事件
        $counter
            .receive(on: DispatchQueue.main) // 确保UI更新在主线程执行
            .sink { [weak firstView] newValue in
                firstView?.refresh(with: newValue)
            }
            .store(in: &cancellables)
        
        // 给第二个子视图绑定计数器更新事件
        $counter
            .receive(on: DispatchQueue.main)
            .sink { [weak secondView] newValue in
                secondView?.refresh(with: newValue)
            }
            .store(in: &cancellables)
    }
}

步骤2:实现两个自定义子视图

// 第一个子视图:展示「My value is X」
class FirstCustomView: UIView {
    private let displayLabel = UILabel()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupUI() {
        addSubview(displayLabel)
        // 此处省略displayLabel的样式配置、布局代码
    }
    
    // 对外暴露的刷新方法
    func refresh(with value: Int) {
        displayLabel.text = "My value is \(value)"
    }
}

// 第二个子视图:展示「Counter: 'X'」
class SecondCustomView: UIView {
    private let displayLabel = UILabel()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupUI() {
        addSubview(displayLabel)
        // 此处省略displayLabel的样式配置、布局代码
    }
    
    func refresh(with value: Int) {
        displayLabel.text = "Counter: '\(value)'"
    }
}

方案2:使用NotificationCenter(兼容iOS 12及更低版本)

如果需要兼容旧版本系统,可以用系统通知中心实现广播能力。

步骤1:定义通知名称

extension Notification.Name {
    static let counterDidUpdate = Notification.Name("counterDidUpdate")
}

步骤2:VC数值变化时发送通知

@objc private func stepperDidChange(_ sender: UIStepper) {
    let newValue = Int(sender.value)
    NotificationCenter.default.post(
        name: .counterDidUpdate,
        object: nil,
        userInfo: ["value": newValue]
    )
}

步骤3:子视图内部监听通知

以第一个子视图为例:

class FirstCustomView: UIView {
    private let displayLabel = UILabel()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
        // 注册通知监听
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleCounterNoti(_:)),
            name: .counterDidUpdate,
            object: nil
        )
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    @objc private func handleCounterNoti(_ noti: Notification) {
        guard let value = noti.userInfo?["value"] as? Int else { return }
        displayLabel.text = "My value is \(value)"
    }
    
    // 销毁时移除观察者避免野指针
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    // 省略其他UI相关代码
}

第二个子视图的监听逻辑和上面一致,只需要修改refresh的文案即可。


内容的提问来源于stack exchange,提问作者akku

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 12:39:03