iOS开发:Storyboard与Segue场景下如何跨UIViewController传递数据
UITabBarController 跨视图控制器数据传递实现方案
适配场景
当前项目架构为UITabBarController承载两个独立的UIViewController,基于Storyboard搭建界面、依赖Segue完成跳转,需要在第一个VC完成Firebase数据拉取后,将数据同步到第二个VC的UILabel控件完成文本更新。
常见实现误区
- 手动初始化目标
dietVC实例进行赋值:手动创建的控制器实例和Storyboard上挂载的运行时实例不是同一个对象,赋值操作不会在实际显示的页面生效 - 直接在异步回调中修改目标VC的内部UILabel属性:如果目标VC的视图还未完成加载,label控件为nil,赋值操作不会触发界面更新
具体实现步骤
1. 为目标VC定义带UI同步逻辑的公开属性
在接收数据的DietViewController内部,不直接暴露私有UILabel控件,定义可被外部赋值的公开数据属性,通过属性监听自动完成UI更新:
class DietViewController: UIViewController { // 外部可赋值的公开数据入口 var burnedCaloriesText: String? { didSet { // 视图已加载时立刻更新UI,未加载时先缓存数据 guard isViewLoaded else { return } burnedLabel.text = burnedCaloriesText } } // 按iOS命名规范定义的UILabel控件关联 @IBOutlet weak var burnedLabel: UILabel! // 视图加载完成时,同步已缓存的数据,解决数据先到、视图后加载的场景 override func viewDidLoad() { super.viewDidLoad() burnedLabel.text = burnedCaloriesText } }
2. 从运行时层级获取真实的目标VC实例
在拉取Firebase数据的回调中,通过当前VC所属的UITabBarController的子控制器数组,匹配获取Storyboard上挂载的真实dietVC实例,再完成数据赋值:
func fetchCeloriesData() { refWorkout = workout!.title Database.database().reference().child("programs").child(refWorkout).child("calories").observeSingleEvent(of: .value) { [weak self] (snapshot) in // 安全解包快照值,避免空值导致强制解包崩溃 guard let caloriesData = snapshot.value as? String else { return } print(caloriesData) // 获取tab架构下真实运行的DietViewController实例 guard let tabBarVC = self?.tabBarController, let dietVC = tabBarVC.viewControllers?.first(where: { $0 is DietViewController }) as? DietViewController else { return } // 赋值触发UI更新 dietVC.burnedCaloriesText = caloriesData } }
场景覆盖说明
- 当用户已经停留在第二个tab页面时:数据赋值后会立刻通过属性监听触发UILabel文本更新
- 当用户还在第一个tab、未切换到第二个页面时:数据会先缓存在
DietViewController的属性中,等用户切换tab触发viewDidLoad时自动完成文本赋值,不会出现数据丢失 - 如果目标VC被包裹在
UINavigationController中,仅需要在实例匹配逻辑里,先取出导航控制器的topViewController做类型判断即可,核心数据传递逻辑不需要修改
内容的提问来源于stack exchange,提问作者Rozgen
相关产品推荐
相关产品推荐

