How to Use Sfsafariviewcontroller with Swiftui

How do I use SFSafariViewController with SwiftUI?

Supplemental to Matteo Pacini post, .presentation(Modal()) was removed by iOS 13's release. This code should work (tested in Xcode 11.3, iOS 13.0 - 13.3):

import SwiftUI
import SafariServices

struct ContentView: View {
// whether or not to show the Safari ViewController
@State var showSafari = false
// initial URL string
@State var urlString = "https://duckduckgo.com"

var body: some View {
Button(action: {
// update the URL if you'd like to
self.urlString = "https://duckduckgo.com"
// tell the app that we want to show the Safari VC
self.showSafari = true
}) {
Text("Present Safari")
}
// summon the Safari sheet
.sheet(isPresented: $showSafari) {
SafariView(url:URL(string: self.urlString)!)
}
}
}

struct SafariView: UIViewControllerRepresentable {

let url: URL

func makeUIViewController(context: UIViewControllerRepresentableContext<SafariView>) -> SFSafariViewController {
return SFSafariViewController(url: url)
}

func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext<SafariView>) {

}

}

SFSafariViewController - How to change the color of the address bar and tab menu

You can use preferredControlTintColor and/or preferredControlTintColor depending on your needs

struct SFSafariViewWrapper: UIViewControllerRepresentable {
let url: URL

func makeUIViewController(context: UIViewControllerRepresentableContext<Self>) -> SFSafariViewController {
let sfVC = SFSafariViewController(url: url)
sfVC.preferredBarTintColor = .blue // set color to tint the background of the navigation bar and the toolbar.
sfVC.preferredControlTintColor = .yellow // set the color to tint the control buttons on the navigation bar and the toolbar.
return sfVC
}

func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext<SFSafariViewWrapper>) {
return
}
}

How to close SFSafariviewcontroller once we get openurl in SwiftUI iOS?

Here's a basic example showing how you could store the state of the browser being shown in an EnvironmentObject:

class SafariState : ObservableObject {
@Published var showSafari = false
}

@main struct StackOverflowApp: App {
@StateObject var safariState = SafariState()

var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(safariState)
.onOpenURL { url in
safariState.showSafari = false
}
}
}
}

struct ContentView: View {
@EnvironmentObject private var safariState : SafariState

var body: some View {
Button("Toggle safari") {
safariState.showSafari.toggle()

//include for testing to show how to dismiss it
//DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
// safariState.showSafari = false
//}
}.fullScreenCover(isPresented: $safariState.showSafari) {
SafariView(url: URL(string: "https://google.com")!)
}
}
}

Note how SafariState is passed into the view hierarchy using .environmentObject

How to present SFSafariViewController in UIView

Do like this,

@IBAction func privacyNoteBtnAction(_ sender: UIButton) {
if #available(iOS 9.0, *) {
let urlString = "https://stackoverflow.com/"
if let url = URL(string: urlString) {
let vc = SFSafariViewController(url: url, entersReaderIfAvailable: true)
vc.delegate = self

if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}

topController.present(vc, animated: true, completion: nil)
}

}
} else {
// Fallback on earlier versions
}
}

How to load a webpage in an ovally inside a SwiftUI App vs loading in safari

You need to wrap the SFSafariViewController (which is from UIKit) into SwiftUI, since it isn't possible to do this natively right now. You should use UIViewControllerRepresentable to do this.

import SwiftUI
import SafariServices

struct MyView: View {
@State var showStackoverflow:Bool = false

var body: some View {
Button(action: { self.showStackoverflow = true }) {
Text("Open stackoverflow")
}
.sheet(isPresented: self.$showStackoverflow) {
SFSafariViewWrapper(url: URL(string: "https://stackoverflow.com")!)
}
}
}
struct SFSafariViewWrapper: UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context: UIViewControllerRepresentableContext<Self>) -> SFSafariViewController {
return SFSafariViewController(url: url)
}
func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext<SFSafariViewWrapper>) {
return
}
}

Conflict using List/ScrollView with SFSafariViewController fullscreen sheet

Below is the working code..

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)
}
}


Related Topics



Leave a reply



Submit