在 SwiftUI 中呈现视图控制器

Posted

技术标签:

【中文标题】在 SwiftUI 中呈现视图控制器【英文标题】:Presenting View Controller in SwiftUI 【发布时间】:2020-06-25 08:15:50 【问题描述】:

如何使用 SwiftUI 实现以下 Objective-C 代码实现的功能?我无法牢牢把握所提出的想法。

    [self presentViewController:messageViewController animated:YES completion:nil];

【问题讨论】:

你想找UIViewRepresentable,有很多教程展示了如何在SwiftUI中使用UIKit视图和视图控制器。 我对这个问题的意义有点困惑。您想知道如何使用全屏显示任何视图吗? @Kyokook 你是对的。我想知道如何在超级视图之上的屏幕中间呈现模态视图。 【参考方案1】:

由于没有提供相关代码,所以伪代码如下所示

struct YourParentView: View 
   @State private var presented = false
   var body: some View 

      // some other code that activates `presented` state

      SomeUIElement()
         .sheet(isPresented: $presented) 
             YourMessageViewControllerRepresentable()
         
   

【讨论】:

感谢您的回答。经过一番修补,它确实提高了我的理解。但是,我有一个后续问题:我已经阅读了一些关于 Representables 的内容,但它们似乎被设计为使用 UIViews。这也是在工作表中使用一些旧的 Objective C 视图的方法吗?换句话说,Representables 是否用于包装除 UIView 之外的其他视图,以便在执行特定操作时在超级视图之上呈现视图? @Ali,可以表示精确的 UIView,当然包括所有子视图(通过 UIViewRepresentable)或整个 UIViewController(通过 UIViewControllerRepresentable)。如果您正确设置了 Objective-C/Swift 桥,那么您的 UIView/UIViewController 也可以正常工作 - 没有区别。【参考方案2】:

ios 13.x 之前,SwiftUI 没有提供任何方法。由于我有同样的需求,写了一个自定义的View修饰符来实现。

extension View 
    func uiKitFullPresent<V: View>(isPresented: Binding<Bool>, style: UIModalPresentationStyle = .fullScreen, content: @escaping (_ dismissHandler: @escaping () -> Void) -> V) -> some View 
        self.modifier(FullScreenPresent(isPresented: isPresented, style: style, contentView: content))
    


struct FullScreenPresent<V: View>: ViewModifier 
    @Binding var isPresented: Bool
    @State private var isAlreadyPresented: Bool = false
    
    let style: UIModalPresentationStyle
    let contentView: (_ dismissHandler: @escaping () -> Void) -> V
    
    @ViewBuilder
    func body(content: Content) -> some View 
        if isPresented 
            content
                .onAppear 
                    if self.isAlreadyPresented == false 
                        let hostingVC = UIHostingController(rootView: self.contentView(
                            self.isPresented = false
                            self.isAlreadyPresented = false
                            UIViewController.topMost?.dismiss(animated: true, completion: nil)
                        ))
                        hostingVC.modalPresentationStyle = self.style
                        UIViewController.topMost?.present(hostingVC, animated: true) 
                            self.isAlreadyPresented = true
                        
                    
                
         else 
            content
        
    

而且,你可以像下面这样使用它。

.uiKitFullPresent(isPresented: $isShowingPicker, content:  closeHandler in
    SomeFullScreenView()
        .onClose(closeHandler) // '.onClose' is a custom extension function written. you can invent your own way to call 'closeHandler'.
)

.uiKitFullPresentcontent 参数是一个以回调处理程序作为其参数的闭包。您可以使用此回调来关闭呈现的视图。

到目前为止,它运行良好。不过看起来有点棘手。

您可能知道,iOS 14 将为我们带来一种以您想要的方式呈现任何视图的方法。签出fullScreenCover()

关于呈现由Objective-C编写的UIViewController,正如Asperi在他的帖子中提到的那样。

更新 这是我目前使用的完整源代码。 https://gist.github.com/fullc0de/3d68b6b871f20630b981c7b4d51c8373

【讨论】:

【参考方案3】:

试试吧:

struct YourParentView: View 
   @State private var presented = false
   var body: some View 

      // some other code that activates `presented` state

      SomeUIElement()
         .sheet(isPresented: $showingSheet, onDismiss: 
             presented = false
         , content: 
             YourMessageViewControllerRepresentable() //Needs to be UIViewRepresentable()
         
   

【讨论】:

以上是关于在 SwiftUI 中呈现视图控制器的主要内容,如果未能解决你的问题,请参考以下文章

在视图外触发时如何绑定 SwiftUI.Alert 的呈现?

SwiftUI onTapGesture 仅调用一次

如何在 SwiftUI 的视图控制器中实现数据绑定?

如何知道 UIKit 视图控制器中的 swiftUI 事件? ||如何提供 swiftUI 和 uikit 之间的通信

如何在 SwiftUI 中设置视图中的控制器

如何在 SwiftUI 中动态推送视图或呈现视图?