使用 List/ScrollView 与 SFSafariViewController 全屏工作表发生冲突

Posted

技术标签:

【中文标题】使用 List/ScrollView 与 SFSafariViewController 全屏工作表发生冲突【英文标题】:Conflict using List/ScrollView with SFSafariViewController fullscreen sheet 【发布时间】:2020-09-06 06:38:08 【问题描述】:

我正在尝试制作一个项目列表,每个项目都应使用 SFSafariViewController 全屏打开一个工作表。为了全屏显示 SFSafariViewController,我使用了此链接中提供的代码:https://dev.to/uchcode/web-sheet-with-sfsafariviewcontroller-4nlc。控制器工作得很好。但是,当我将项目放入 List 或 ScrollView 时,它不会显示工作表。当我删除 List/ScrollView 时,它工作正常。

这里是代码。

import SwiftUI
import SafariServices

struct RootView: View, Hostable 
    @EnvironmentObject private var hostedObject: HostingObject<Self>
    let address: String
    let title: String
    
    func present() 
        let config = SFSafariViewController.Configuration()
        config.entersReaderIfAvailable = true
        
        let safari = SFSafariViewController(url: URL(string: address)!, configuration: config)
        hostedObject.viewController?.present(safari, animated: true)
    
    
    var body: some View 
        Button(title) 
            self.present()
        
    


struct ContentView: View 
    
    @State private var articlesList = [
    ArticlesList(id: 0, title: "Apple", link: "http://apple.com", lang: "en"),
    ArticlesList(id: 1, title: "Yahoo", link: "http://yahoo.com", lang: "en"),
    ArticlesList(id: 2, title: "microsoft", link: "http://microsoft.com", lang: "en"),
    ArticlesList(id: 3, title: "Google", link: "http://google.com", lang: "en")
    ]
    
    var body: some View 
        
        NavigationView
            //Here is the problem when I add RootView inside a List or ScrollView it does not show Safari.
            ScrollView(.vertical, showsIndicators: false) 
                VStack
                    ForEach(articlesList) article in
                        RootView(address: article.link, title: article.title).hosting()
                        Spacer(minLength: 10)
                    
                
            
            .navigationBarTitle("Articles")
        
    
    


struct ArticlesList: Identifiable, Codable 
    let id: Int
    let title: String
    let link: String
    let lang: String



struct UIViewControllerView: UIViewControllerRepresentable 
    final class ViewController: UIViewController 
        var didAppear: (UIViewController) -> Void =  _ in 
        override func viewDidAppear(_ animated: Bool) 
            super.viewDidAppear(animated)
            didAppear(self)
        
    
    
    var didAppear: (UIViewController) -> Void
    
    func makeUIViewController(context: Context) -> UIViewController 
        let viewController = ViewController()
        viewController.didAppear = didAppear
        return viewController
    
    
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) 
        //
    


struct UIViewControllerViewModifier: ViewModifier 
    var didAppear: (UIViewController) -> Void
    var viewControllerView: some View 
        UIViewControllerView(didAppear:didAppear).frame(width:0,height:0)
    
    func body(content: Content) -> some View 
        content.background(viewControllerView)
    


extension View 
    func uiViewController(didAppear: @escaping (UIViewController) -> ()) -> some View 
        modifier(UIViewControllerViewModifier(didAppear:didAppear))
    


class HostingObject<Content: View>: ObservableObject 
    @Published var viewController: UIViewController? = nil


struct HostingObjectView<Content: View>: View 
    var rootView: Content
    let hostedObject = HostingObject<Content>()
    func getHost(viewController: UIViewController) 
        hostedObject.viewController = viewController.parent
    
    var body: some View 
        rootView
            .uiViewController(didAppear: getHost(viewController:))
            .environmentObject(hostedObject)
    


protocol Hostable: View 
    associatedtype Content: View
    func hosting() -> Content


extension Hostable 
    func hosting() -> some View 
        HostingObjectView(rootView: self)
    

【问题讨论】:

如果您发布编译代码,您将获得更多/更好的答案。例如,删除 getArticles 函数和 LoadingView,添加一些测试数据,并确保所有内容都可以复制/粘贴到一个新的 SwiftUI 项目中,然后立即编译。 @BartvanKuik,我编辑了问题,为新的 SwiftUI 准备了一个代码。 【参考方案1】:

您的代码可以在 ios 14 模拟器中运行!

我包装了 SFSafariViewController,如果我用以下代码替换您的 RootView,它可以在我的 iOS 13 设备上运行。但是,它并不是真正的全屏,而是一张纸。

struct RootView: View 
    @State private var isPresenting = false
    let address: String
    let title: String
    
    var body: some View 
        Button(self.title) 
            self.isPresenting.toggle()
        
        .sheet(isPresented: self.$isPresenting) 
            SafariView(address: URL(string: self.address)!)
        
    


struct SafariView: UIViewControllerRepresentable 
    let address: URL
    
    func makeUIViewController(context: Context) -> SFSafariViewController 
        let config = SFSafariViewController.Configuration()
        config.entersReaderIfAvailable = true

        let safari = SFSafariViewController(url: self.address, configuration: config)
        return safari
    
    
    func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) 
        //
    

【讨论】:

如果它在 iOS 14 中工作,是否意味着它是以前版本(如 13.7)中的错误?我不确定该工作表是否适合用户。我会做一个测试。 老实说,我不知道。目前,我没有运行 iOS 14 测试版的可用设备。但在得出这是 iOS 13 错误的结论之前,我肯定想在那里对其进行测试,并且它确实适用于 iOS 14。 @BartvanKuik 嘿!你能找到解决办法吗? @KennyBatista 我希望下面的答案对您有所帮助。【参考方案2】:

下面是工作代码..

import SwiftUI
import SafariServices

struct ContentView: View 
    var body: some View
        TabView 
            HomeView()
                .tabItem 
                    VStack 
                        Image(systemName: "house")
                        Text("Home")
                    
            .tag(0)
            
            ArticlesView().hosting()
                .tabItem
                    VStack
                        Image(systemName: "quote.bubble")
                        Text("Articles")
                    
            .tag(1)
        
    


struct HomeView: View 
    var body: some View
        Text("This is home")
    



struct ShareView: View
    var body: some View
        Text("Here the share")
    


struct ArticlesView: View, Hostable 
    @EnvironmentObject private var hostedObject: HostingObject<Self>
    @State private var showShare = false
    
    @State private var articlesList = [
        ArticlesList(id: 0, title: "Apple", link: "http://apple.com", lang: "en"),
        ArticlesList(id: 1, title: "Yahoo", link: "http://yahoo.com", lang: "en"),
        ArticlesList(id: 2, title: "microsoft", link: "http://microsoft.com", lang: "en"),
        ArticlesList(id: 3, title: "Google", link: "http://google.com", lang: "en")
    ]
    
    
    func present(address: String) 
        let config = SFSafariViewController.Configuration()
        config.entersReaderIfAvailable = true
        
        let safari = SFSafariViewController(url: URL(string: address)!, configuration: config)
        hostedObject.viewController?.present(safari, animated: true)
    
    
    var body: some View 
        NavigationView
            ScrollView(.vertical, showsIndicators: false) 
                VStack(spacing: 40)
                    ForEach(articlesList) article in
                        Button(article.title) 
                            self.present(address: article.link)
                        
                    
                
                .sheet(isPresented: $showShare)
                    ShareView()
                
                .navigationBarTitle("Articles")
                .navigationBarItems(leading:
                    Button(action: 
                        self.showShare.toggle()
                    )
                    
                        Image(systemName: "plus")
                    
                )
            
        
    



struct ArticlesList: Identifiable, Codable 
    let id: Int
    let title: String
    let link: String
    let lang: String




struct UIViewControllerView: UIViewControllerRepresentable 
    final class ViewController: UIViewController 
        var didAppear: (UIViewController) -> Void =  _ in 
        override func viewDidAppear(_ animated: Bool) 
            super.viewDidAppear(animated)
            didAppear(self)
        
    
    
    var didAppear: (UIViewController) -> Void
    
    func makeUIViewController(context: Context) -> UIViewController 
        let viewController = ViewController()
        viewController.didAppear = didAppear
        return viewController
    
    
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) 
        //
    


struct UIViewControllerViewModifier: ViewModifier 
    var didAppear: (UIViewController) -> Void
    var viewControllerView: some View 
        UIViewControllerView(didAppear:didAppear).frame(width:0,height:0)
    
    func body(content: Content) -> some View 
        content.background(viewControllerView)
    


extension View 
    func uiViewController(didAppear: @escaping (UIViewController) -> ()) -> some View 
        modifier(UIViewControllerViewModifier(didAppear:didAppear))
    


class HostingObject<Content: View>: ObservableObject 
    @Published var viewController: UIViewController? = nil


struct HostingObjectView<Content: View>: View 
    var rootView: Content
    let hostedObject = HostingObject<Content>()
    func getHost(viewController: UIViewController) 
        hostedObject.viewController = viewController.parent
    
    var body: some View 
        rootView
            .uiViewController(didAppear: getHost(viewController:))
            .environmentObject(hostedObject)
    


protocol Hostable: View 
    associatedtype Content: View
    func hosting() -> Content


extension Hostable 
    func hosting() -> some View 
        HostingObjectView(rootView: self)
    

【讨论】:

以上是关于使用 List/ScrollView 与 SFSafariViewController 全屏工作表发生冲突的主要内容,如果未能解决你的问题,请参考以下文章

在 HStack 中有 2 个 List/ScrollView 时,NavigationView 不会折叠 - SwiftUI

static与const联合使用&&extern与const联合使用

为啥将@Transactional 与@Service 一起使用而不是与@Controller 一起使用

为啥将@Transactional 与@Service 一起使用而不是与@Controller 一起使用

将 require('...') 与变量一起使用与在 webpack 中使用字符串

C 语言数组与指针操作 ( 数组符号 [] 与 指针 * 符号 的 联系 与 区别 | 数组符号 [] 与 指针 * 符号 使用效果 基本等价 | 数组首地址 与 指针 本质区别 )