SwiftUI按钮点击展示Objective-C UIViewController的实现方案
问题背景
刚接触SwiftUI,在现有Objective-C项目中引入SwiftUI模块,通过UIHostingController在SwiftViewControllerUsedToPresentSwiftUI中初始化SwiftUI视图(struct ContentView: View),代码如下,这部分运行正常:
let hostingController = UIHostingController.init(rootView: ContentView())
需求是在SwiftUI视图的按钮点击时,调用项目中带关联xib的现有MyExistingUIViewController,理想实现代码如下:
let myExistingUIViewController = MyExistingUIViewController(nibName: "MyExistingUIViewController", bundle: nil) myExistingUIViewController.modalPresentationStyle = .formSheet present(myExistingUIViewController, animated: true, completion: nil)
尝试直接通过宿主VC调用时,出现以下错误:
[Presentation] Attempt to present <MyExistingUIViewController: 0x7f7a67827c00> on <SwiftViewControllerUsedToPresentSwiftUI: 0x7f7a64720da0> (from <SwiftViewControllerUsedToPresentSwiftUI: 0x7f7a64720da0>) whose view is not in the window hierarchy.
后续按照建议用UIViewControllerRepresentable包装现有VC:
import Foundation import SwiftUI struct MyExistingUIViewControllerWrapper: UIViewControllerRepresentable{ func makeUIViewController(context: Context) -> MyExistingUIViewController { let vc = MyExistingUIViewController(nibName: "MyExistingView", bundle: nil) return vc } func updateUIViewController(_ uiViewController: MyExistingUIViewController, context: Context) { // 相关代码 } }
通过SwiftUI的.sheet调用时:
.sheet(isPresented: $showingWrapperView){ MyExistingUIViewControllerWrapper() }
但无法以formSheet样式展示,而现有xib对应的VC本身是formSheet样式。
解决方案
页面流转路径为:Objective-C View Controller → SwiftUI → Objective-C Form Sheet,通过从初始的FirstObjCViewController向SwiftUI视图传递两个闭包Handler,实现需求:
- 一个Handler用于关闭当前SwiftUI视图
- 另一个Handler用于关闭SwiftUI视图,并从
FirstObjCViewController打开目标FormSheet类型的MyExistingUIViewController
代码实现
- Objective-C侧创建SwiftUI视图的代码:
UIViewController *newSwiftUIView = [NewSwiftUIView makeNewSwiftUIView WithSomeFirstArg:someFirstArg closeHandler:^{ [[self presentedViewController] dismissViewControllerAnimated:YES completion:nil]; } closeAndOpenMyExistingUIViewControllerHandler:^{ [[self presentedViewController] dismissViewControllerAnimated:YES completion:nil]; [self OpenMyExistingUIViewController]; // 此方法在FirstObjCViewController中定义,负责以FormSheet样式打开目标VC }];
- Swift侧接收Handler的方法定义:
@objc static func makeNewSwiftUIView(someFirstArg: NSArray, closeHandler: @escaping (() -> Void), closeAndOpenMyExistingUIViewControllerHandler: @escaping (() -> Void)) -> UIViewController
这种实现方式运行流畅、代码简洁,可直接复用现有Objective-C侧的VC展示逻辑,保证FormSheet样式正常生效。
内容的提问来源于stack exchange,提问作者user1664018

