如何在iOS 13上实现被UIHostingController包裹的SwiftUI View自定义返回按钮并弹出导航栈?
针对iOS 13的自定义返回按钮解决方案
针对iOS 13的场景,你遇到的问题其实是因为SwiftUI View被UIHostingController包裹后,和UIKit导航栈的交互逻辑需要结合UIKit的API来处理——直接用纯SwiftUI的导航弹出方法会因为两层导航容器的兼容问题失效。下面给你两个可靠的解决方案:
方案一:通过闭包让SwiftUI触发UIKit的弹出动作
这种方式让SwiftUI视图持有一个来自UIHostingController的闭包,点击自定义按钮时直接调用闭包完成弹出,逻辑清晰且避免强引用问题:
1. 定义带弹出闭包的SwiftUI视图
struct TransactionDetailsView: View { let transactionRecord: YourRecordType // 替换成你的记录类型 let onPop: () -> Void var body: some View { // 你的页面内容 ScrollView { Text("交易详情:\(transactionRecord.description)") .padding() } // 设置自定义返回按钮 .navigationBarItems(leading: Button(action: onPop) { HStack(spacing: 4) { Image(systemName: "chevron.left") // iOS13支持SF Symbols,若不支持可替换为自定义图片 Text("返回") } }) .navigationBarTitle("交易详情", displayMode: .inline) } }
2. 修改UIHostingController的初始化逻辑
class TransactionDetailsHostingController: UIHostingController<TransactionDetailsView> { init(transactionRecord: YourRecordType) { // 传递弹出闭包,用weak self避免循环引用 let rootView = TransactionDetailsView(transactionRecord: transactionRecord) { [weak self] in self?.navigationController?.popViewController(animated: true) } super.init(rootView: rootView) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() // 隐藏系统默认返回按钮,避免和自定义按钮冲突 navigationItem.hidesBackButton = true } }
方案二:直接在UIHostingController中设置UIKit风格的导航按钮
如果你不想在SwiftUI视图中处理导航逻辑,可以直接在UIHostingController层面配置自定义返回按钮,完全用UIKit的API实现:
class TransactionDetailsHostingController: UIHostingController<TransactionDetailsView> { init(transactionRecord: YourRecordType) { super.init(rootView: TransactionDetailsView(transactionRecord: transactionRecord)) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() // 隐藏系统默认返回按钮 navigationItem.hidesBackButton = true // 创建自定义返回按钮 let backButton = UIBarButtonItem( image: UIImage(systemName: "chevron.left"), style: .plain, target: self, action: #selector(popViewController) ) // 可选:给按钮添加文字 // backButton.title = "返回" navigationItem.leftBarButtonItem = backButton } @objc private func popViewController() { navigationController?.popViewController(animated: true) } }
关键注意事项
- 必须调用
navigationItem.hidesBackButton = true隐藏系统默认返回按钮,否则会出现两个返回按钮 - 闭包方案中一定要用
[weak self]避免循环引用,防止内存泄漏 - iOS13中使用
UIImage(systemName:)需要确保项目支持SF Symbols(Xcode 11+默认支持),如果需要兼容更早的环境,替换为自定义图片即可
内容的提问来源于stack exchange,提问作者Aaron Bratcher
相关产品推荐
相关产品推荐

