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

如何在不同ViewController间同步关联自定义视图的外观状态

跨ViewController同步自定义视图状态方案

核心思路

用全局状态单例统一管理50个自定义视图的状态(图片、颜色),两个ViewController的自定义视图通过索引绑定到同一状态项,状态更新时通知所有绑定视图同步UI。


1. 实现状态管理单例

创建单例类存储所有视图的状态,提供更新/获取状态的方法,同时发送状态更新通知:

import UIKit

class CustomViewStateManager {
    static let shared = CustomViewStateManager()
    private init() {}
    
    // 存储50个视图的状态,索引0-49对应每个视图
    private var viewStates: [ViewState] = Array(repeating: ViewState(), count: 50)
    
    struct ViewState {
        var image: UIImage? = UIImage(named: "default")
        var tintColor: UIColor = .gray
    }
    
    // 更新指定索引的状态
    func updateState(at index: Int, image: UIImage?, color: UIColor) {
        guard index >= 0 && index < viewStates.count else { return }
        viewStates[index].image = image
        viewStates[index].tintColor = color
        NotificationCenter.default.post(
            name: NSNotification.Name("CustomViewStateUpdated"),
            object: index
        )
    }
    
    // 获取指定索引的状态
    func getState(at index: Int) -> ViewState? {
        guard index >= 0 && index < viewStates.count else { return nil }
        return viewStates[index]
    }
}

2. 封装自定义视图

自定义视图从xib加载,内部处理UI渲染、状态绑定和点击事件:

import UIKit

class BaseCustomView: UIView {
    var bindIndex: Int = -1 {
        didSet {
            if let state = CustomViewStateManager.shared.getState(at: bindIndex) {
                updateUI(with: state)
            }
            NotificationCenter.default.addObserver(
                self,
                selector: #selector(onStateUpdated(_:)),
                name: NSNotification.Name("CustomViewStateUpdated"),
                object: nil
            )
        }
    }
    
    @IBOutlet weak var iconImageView: UIImageView!
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        loadXib()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        loadXib()
    }
    
    // 子类重写此方法加载对应布局的xib
    func loadXib() {}
    
    @IBAction func onViewTapped(_ sender: UITapGestureRecognizer) {
        guard bindIndex != -1 else { return }
        // 替换为你的业务逻辑:切换图片/颜色
        let newImage = UIImage(named: "selected")
        let newColor = UIColor.systemBlue
        CustomViewStateManager.shared.updateState(at: bindIndex, image: newImage, color: newColor)
    }
    
    @objc private func onStateUpdated(_ notification: Notification) {
        guard let updatedIndex = notification.object as? Int, updatedIndex == bindIndex else { return }
        if let state = CustomViewStateManager.shared.getState(at: updatedIndex) {
            updateUI(with: state)
        }
    }
    
    private func updateUI(with state: CustomViewStateManager.ViewState) {
        iconImageView.image = state.image
        iconImageView.tintColor = state.tintColor
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

// 布局A的自定义视图
class CustomViewLayoutA: BaseCustomView {
    override func loadXib() {
        let nib = UINib(nibName: "CustomViewLayoutA", bundle: nil)
        let contentView = nib.instantiate(withOwner: self, options: nil).first as! UIView
        contentView.frame = bounds
        addSubview(contentView)
    }
}

// 布局B的自定义视图
class CustomViewLayoutB: BaseCustomView {
    override func loadXib() {
        let nib = UINib(nibName: "CustomViewLayoutB", bundle: nil)
        let contentView = nib.instantiate(withOwner: self, options: nil).first as! UIView
        contentView.frame = bounds
        addSubview(contentView)
    }
}

3. ViewController加载自定义视图

两个ViewController逻辑一致,仅加载不同布局的自定义视图,给每个视图绑定对应索引:

import UIKit

class ViewControllerA: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        loadCustomViews()
    }
    
    private func loadCustomViews() {
        let spacing: CGFloat = 10
        let viewSize = CGSize(width: (view.bounds.width - 5*spacing)/4, height: 80)
        for i in 0..<50 {
            let customView = CustomViewLayoutA(frame: .zero)
            customView.bindIndex = i
            customView.addGestureRecognizer(UITapGestureRecognizer(target: customView, action: #selector(customView.onViewTapped(_:))))
            // 网格布局位置计算
            let row = i / 4
            let col = i % 4
            let x = spacing + CGFloat(col)*(viewSize.width + spacing)
            let y = spacing + CGFloat(row)*(viewSize.height + spacing)
            customView.frame = CGRect(x: x, y: y, width: viewSize.width, height: viewSize.height)
            view.addSubview(customView)
        }
    }
}

class ViewControllerB: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        loadCustomViews()
    }
    
    private func loadCustomViews() {
        let spacing: CGFloat = 10
        let viewSize = CGSize(width: (view.bounds.width - 5*spacing)/4, height: 80)
        for i in 0..<50 {
            let customView = CustomViewLayoutB(frame: .zero)
            customView.bindIndex = i
            customView.addGestureRecognizer(UITapGestureRecognizer(target: customView, action: #selector(customView.onViewTapped(_:))))
            let row = i / 4
            let col = i % 4
            let x = spacing + CGFloat(col)*(viewSize.width + spacing)
            let y = spacing + CGFloat(row)*(viewSize.height + spacing)
            customView.frame = CGRect(x: x, y: y, width: viewSize.width, height: viewSize.height)
            view.addSubview(customView)
        }
    }
}

关键注意点

  • 两个布局的自定义视图继承同一基类,复用所有逻辑,仅xib布局不同
  • 状态单例确保全局状态唯一,避免多数据源冲突
  • 可改用Combine框架的发布者-订阅者模式替代NotificationCenter(适合iOS 13+)
  • 自定义视图销毁时必须移除通知监听,防止内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 21:54:14