How to use SFSafariViewController in SwiftUI
source link: https://sarunw.com/posts/sfsafariviewcontroller-in-swiftui/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Currently (iOS 16), there is no native way to present SFSafariViewController
in SwiftUI.
In this article, I will teach you what I think is the proper way to present SFSafariViewController
in a SwiftUI app.
When we want to use a UIViewController in a SwiftUI app, we have two options.
For SFSafariViewController
, I prefer the second method. Let's see why I preferred that.
What is SFSafariViewController
SFSafariViewController
is a standalone view controller. That means it can, and should operate without our intervention.
It comes pre-equipped with a navigation bar and controls that are necessary for browsing.
Here is an example where we present SFSafariViewController
in UIKit. It presents as a pushed animation even though the parent view controller doesn't have one.
SFSafariViewController
Let's see what happens when we try to wrap this in a UIViewControllerRepresentable
.
You can easily support sarunw.com by checking out this sponsor.
Sponsor sarunw.com and reach thousands of iOS developers.
Create UIViewControllerRepresentable of SFSafariViewController
We can easily create UIViewControllerRepresentable
of SFSafariViewController
like this.
import SwiftUI
import SafariServices
struct SafariWebView: UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context: Context) -> SFSafariViewController {
return SFSafariViewController(url: url)
}
func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {
}
}
Let's see why I don't recommend this approach.
SFSafariViewController with NavigationLink
In this example, I use a NavigationLink
to push a SafariWebView
into a navigation stack.
struct ContentView: View {
var body: some View {
NavigationStack {
NavigationLink("Push") {
SafariWebView(url: URL(string: "https://sarunw.com")!)
.ignoresSafeArea()
}
}
}
}
The result is a disaster.
Since SFSafariViewController
come pre-equipped with a navigation bar, using NavigationLink
create a double navigation bar.
So, this isn't a way to go.
Double navigation bar.
SFSafariViewController with fullScreenCover
We can choose to present SFSafariViewController
using a full-screen cover presentation instead.
Presenting it this way won't cause a double navigation bar.
struct ContentView: View {
@State private var isPresentWebView = false
var body: some View {
Button("Present as full screen cover") {
isPresentWebView = true
}
.fullScreenCover(isPresented: $isPresentWebView) {
SafariWebView(url: URL(string: "https://sarunw.com")!)
.ignoresSafeArea()
}
}
}
If you want to use UIViewControllerRepresentable
, I think presenting it using .fullScreenCover
is a way to go.
SFSafariViewController is present without a problem a the full-screen cover.
You need .ignoresSafeArea()
. Otherwise, SFSafariViewController
will present inside the safe areas.
SFSafariViewController without .ignoresSafeArea()
Caveat
Present SFSafariViewController
as a full-screen cover might look OK, but if you try to rotate your app, it still has some UI glitches.
You will see a black screen when rotating SFSafariViewController
.
You might get away with this approach if your app doesn't support rotation.
Small UI glitches when rotated.
You can easily support sarunw.com by checking out this sponsor.
Sponsor sarunw.com and reach thousands of iOS developers.
Use SFSafariViewController without wrapping
I think the easiest way to use SFSafariViewController
is to use it in a UIKit context.
In this case, I grab the root view controller and present SFSafariViewController
from that root view controller.
struct ContentView: View {
var body: some View {
Button("Present SFSafariViewController") {
// 1
let vc = SFSafariViewController(url: URL(string: "https://sarunw.com")!)
// 2
UIApplication.shared.firstKeyWindow?.rootViewController?.present(vc, animated: true)
}
}
}
extension UIApplication {
// 3
var firstKeyWindow: UIWindow? {
return UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.filter { $0.activationState == .foregroundActive }
.first?.keyWindow
}
}
1 We create SFSafariViewController
directly without wrapping.
2 Then, present it from the root view controller.
3 There are many ways to grab a root view controller in a SwiftUI app. I choose a quick and dirty one for this example.
You will get the same behavior as you use in UIKit.
Using SFSafariViewController in a UIKit context.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK