如何在不同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
相关产品推荐
相关产品推荐

