Swift中NavigationController与ContainerView联动问题:点击按钮无日志输出
Hey,我来帮你排查下这个问题,以及给出可行的解决方案:
问题根源
你现在的代码逻辑有个关键误区:你调用navigationController?.performSegue(withIdentifier: "segue1", sender: self),是让导航控制器去执行一个segue,但你的TabBarViewController已经通过ContainerView嵌入在当前ViewController里了——它不是通过segue跳转出来的页面,所以TabBarViewController里的prepare(for:sender:)方法完全不会被触发,自然看不到日志输出。
下面给你三种常用的解决思路,你可以根据场景选择:
方法一:直接获取ContainerView中的TabBarViewController实例
既然TabBarViewController是当前控制器的子控制器(ContainerView嵌入的本质就是添加子控制器),你可以直接拿到它的实例,然后调用对应的操作:
首先在ViewController中添加属性存储这个子控制器:
class ViewController: UIViewController { // 存储ContainerView对应的TabBarViewController private var tabBarVC: TabBarViewController? override func viewDidLoad() { super.viewDidLoad() // 从当前控制器的子控制器列表中找到TabBarViewController tabBarVC = children.first(where: { $0 is TabBarViewController }) as? TabBarViewController } @IBAction func click1(_ sender: Any) { // 直接调用TabBarVC里的方法 tabBarVC?.printTestLog() } }
然后在TabBarViewController中添加对应的方法:
class TabBarViewController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() } func printTestLog() { print("this is test") // 后续你可以在这里添加切换Tab的逻辑,比如: // selectedIndex = 1 } }
方法二:使用代理模式(更解耦的规范写法)
如果你的项目逻辑比较复杂,想让控制器之间的依赖更清晰,推荐用代理模式:
首先定义一个协议,用来规范触发的操作:
protocol TabBarActionDelegate: AnyObject { func triggerTestAction() }
然后在ViewController中设置代理:
class ViewController: UIViewController { weak var tabBarDelegate: TabBarActionDelegate? override func viewDidLoad() { super.viewDidLoad() // 从子控制器中找到符合代理协议的实例 tabBarDelegate = children.first(where: { $0 is TabBarActionDelegate }) as? TabBarActionDelegate } @IBAction func click1(_ sender: Any) { // 通过代理触发操作 tabBarDelegate?.triggerTestAction() } }
让TabBarViewController遵守这个协议并实现方法:
class TabBarViewController: UITabBarController, TabBarActionDelegate { override func viewDidLoad() { super.viewDidLoad() } func triggerTestAction() { print("this is test") // 这里可以添加你需要的Tab切换逻辑 } }
方法三:使用NotificationCenter(适合跨多层控制器的场景)
如果你的操作需要跨多层控制器传递,或者不想直接依赖控制器实例,可以用通知中心:
在TabBarViewController中注册通知:
class TabBarViewController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() // 注册通知,监听触发事件 NotificationCenter.default.addObserver( self, selector: #selector(handleTestNotification), name: NSNotification.Name("TriggerTabBarTest"), object: nil ) } @objc private func handleTestNotification() { print("this is test") } // 记得在销毁时移除通知,避免内存泄漏 deinit { NotificationCenter.default.removeObserver(self) } }
然后在ViewController的按钮点击方法中发送通知:
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } @IBAction func click1(_ sender: Any) { // 发送通知触发操作 NotificationCenter.default.post( name: NSNotification.Name("TriggerTabBarTest"), object: nil ) } }
总结
如果只是简单的当前控制器和嵌入的TabBarVC交互,方法一最直接;如果追求代码解耦和可维护性,方法二更推荐;方法三适合复杂的跨层级场景,不过尽量少用,避免代码逻辑变得难以追踪。
内容的提问来源于stack exchange,提问作者GrzegorzB

