• SwiftUI中如何使用SFSafariViewController显示网页?
  • 发布于 1个月前
  • 80 热度
    0 评论
  • Kily
  • 0 粉丝 27 篇博客
  •   
前言
之前讲过如何在项目中使用 SFSafariViewController,但那个是 Swift 版的,今天看到有人问如何在 SwiftUI 中使用 SFSafariViewController,虽然在 Swift UIKit 中很容易使用,但是在 SwiftUI 中还是有些难度的,今天就来稍微讲讲。

为 SFSafariViewController 创建 SwiftUI 包装器
其实 SwiftUI 并没有直接提供 SFSafariViewController 这个组件,那么在 SwiftUI 中使用就只能通过桥接一层。我们先通过创建一个继承 UIViewRepresentable 协议的自定义结构体,然后再开始实现对应的功能。该协议允许我们创建一个包装 UIKit 视图控制器的 SwiftUI 视图:

首先导入三个需要用到的框架:
import SwiftUI
import UIKit
import SafariServices
然后创建自定义的 SFSafariView:
struct MySFSafariView: UIViewControllerRepresentable {
    let url: URL
    func makeUIViewController(context: UIViewControllerRepresentableContext<Self>) -> SFSafariViewController {
        return SFSafariViewController(url: url)
    }
    func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext<MySFSafariView>) {
        
    }
}
要自定义一个 SwiftUI 中桥接 UIKit 的 View,首先要继承 UIViewControllerRepresentable 协议,然后必须要实现协议中的两个方法:
makeUIViewController(context: ) 方法将调用它来创建 UIViewController 实例
updateUIViewController(_ : context: ) 方法将调用它以使用来自 SwiftUI 的数据以更新 UIViewController 的状态
在我们的例子中,我只是简单的创建了一个 SFSafariViewController 对象,实际中你可以根据需求来增加功能。

创建可重用的视图修饰符
为了以后可以重复使用这个 View,我们再来创建一些可重用的视图修饰符 ViewModifier,便于使用 openURL 打开一个链接:
extension Binding where Value == Bool {
    init(binding: Binding<(some Any)?>) {
        self.init(
            get: {
                binding.wrappedValue != nil
            },
            set: { newValue in
                guard newValue == false else { return }
                binding.wrappedValue = nil
            }
        )
    }
}

extension Binding {
    func mappedToBool<Wrapped>() -> Binding<Bool> where Value == Wrapped? {
        Binding<Bool>(binding: self)
    }
}
private struct SafariViewControllerViewModifier: ViewModifier {
    @State private var urlToOpen: URL?

    func body(content: Content) -> some View {
        content
            .environment(\.openURL, OpenURLAction { url in
                urlToOpen = url
                return .handled
            })
            .sheet(isPresented: $urlToOpen.mappedToBool(), onDismiss: {
                urlToOpen = nil
            }, content: {
                MySFSafariView(url: urlToOpen!)
            })
    }
}
我们使用视图修饰符来捕获任何传入的 URL,并将它们用作 sheet 的输入。这个新的 Sheet 使用我们之前创建 MySFSafariView 的 URL 在应用程序内显示 SFSafariViewController。

在 SwiftUI 中展示 SFSafariViewController
我们先给 View 写一个扩展方法,以使用刚刚写好的视图修饰符:
extension View {
    func handleOpenURLUseSafariView() -> some View {
        modifier(SafariViewControllerViewModifier())
    }
}
现在我们已经写好了所有逻辑,现在可以开始在 SwiftUI 中展示任何传入的 URL 的视图了。
struct ContentView: View {
    var body: some View {
        VStack {
            Link("打开 Apple 官网", destination: URL(string: "https://www.apple.com")!)
                . handleOpenURLUseSafariView()
        }
    }
}

点击之后可以看到能够正常展示 SFSafariViewController:

总结
以上就是使用 SwiftUI 展示 SFSafariViewController 的内容了,而且我们还对代码进行了封装,以后再有类似的需求,只需要在有链接的 View 上使用 .handleOpenURLUseSafariView() 方法即可。
用户评论