0

I want to push to a full screen SFSafariViewController from half model sheet.

What I want is first present a sheet with a "Link Btn" , the click "Link Btn" to push to a full screen webview to show a web page, just like bellow:

enter image description here

My code is a bellow:

import SwiftUI
import SafariServices

struct ContentView: View {
    @State private var showingVC = false
    var body: some View {
        NavigationView() {
            VStack() {
                Button(action: {
                    self.showingVC = true
                }, label: {
                    Text("Show sheet")
                })
            }
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
            .background(Color.gray.opacity(0.2))
            .sheet(isPresented: $showingVC) {
                PresentView()
            }
        }
        
    }
}

struct PresentView: View {
    var body: some View {
        NavigationView() {
            VStack() {
                NavigationLink(destination: SafariView(url: URL(string: "https://github.com/")!)) {
                    Text("Link btn")
                        .foregroundColor(.blue)
                }
            }
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
            .background(Color.gray.opacity(0.2))
        }
    }
}

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

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

    }
}

But the final web page is not full screen and have two navigation bars as bellow:

enter image description here

How can I push to a full screen webview with only one navigation bar from a presented sheet view?

mars
  • 109
  • 5
  • 21

3 Answers3

3

It is needed not a link but fullScreenCover, like

struct PresentView: View {
    @State private var showSafari = false
    var body: some View {
        NavigationView() {
            VStack() {
                Button {
                    showSafari.toggle()    // << here !!
                } label: {
                    Text("Link btn")
                }
            }
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
            .background(Color.gray.opacity(0.2))
            .fullScreenCover(isPresented: $showSafari) {  
                SafariView(url: URL(string: "https://github.com/")!)  // << here !!
            }
        }
    }
}

Tested with Xcode 13.4 / iOS 15.5

demo

Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Thanks! The result is much better, but is Not exactly what I want. What I want is "push" to a full screen , not "present". I found that several apps have achieved this effect. I wonder whether they push to SFSafariViewController from the root view through notification, not in presented sheet view? Is there any way to push to a full screen? – mars Jul 27 '22 at 15:49
1

After clarifying your question here is an approach: The second button has to activate the link in main view and dismiss the sheet.

enter image description here

truct ContentView: View {
    
    @State private var showingVC = false
    @State private var showingBrowser = false

    var body: some View {
        NavigationView() {
            VStack() {
                Button("Show sheet") { self.showingVC = true }
                
                NavigationLink("",
                               destination: SafariView(url: URL(string: "https://github.com/")!),
                               isActive: $showingBrowser)
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.gray.opacity(0.2))
            .sheet(isPresented: $showingVC) {
                PresentView(showingBrowser: $showingBrowser) // pass binding
            }
        }
    }
}


struct PresentView: View {

    @Environment(\.dismiss) private var dismiss
    @Binding var showingBrowser: Bool

    var body: some View {
        NavigationView() {
            VStack() {
                Button("Link btn") {
                    showingBrowser = true // activate browser link back in main view
                    dismiss() // dismiss sheet
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.gray.opacity(0.2))
        }
    }
}
ChrisR
  • 9,523
  • 1
  • 8
  • 26
0

Day after day, I found a solution B as bellow:

import SwiftUI
import SafariServices

struct ContentView: View {
    @State private var showingVC = false
    var body: some View {
        //SafariView(url: URL(string: "https://github.com/")!)
        NavigationView() {
            ZStack() {
                VStack() {
                    Button(action: {
                        self.showingVC = true
                    }, label: {
                        Text("Show sheet")
                    })
                }
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
                .background(Color.gray.opacity(0.2))
                .sheet(isPresented: $showingVC) {
                    PresentView()
                }
            }
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
        }
    }
}

struct PresentView: View {
    @State private var showSafari = false
    var body: some View {
        NavigationView() {

            VStack() {
                Button {
                    let url: URL = URL(string: "https://github.com/")!
                    let currentVC = UIViewController.currentViewController()
                    let vc = SFSafariViewController(url: url)
                    currentVC?.present(vc, animated: true)  // << here !!
                } label: {
                    Text("Link btn")
                }
            }
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
            .background(Color.gray.opacity(0.2))
        }
    }
}

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

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

    }
}


extension UIViewController {
     /// MARK : - 当前顶层控制器
     class func currentViewController() -> UIViewController? {
        let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
        var currentVC = window?.rootViewController
        while ((currentVC?.presentedViewController) != nil) {
            currentVC = currentVC?.presentedViewController
        }
        if (currentVC is UITabBarController) {
            currentVC = (currentVC as? UITabBarController)?.selectedViewController
        }
        if (currentVC is UINavigationController) {
            currentVC = (currentVC as? UINavigationController)?.topViewController
        }
        return currentVC
    }
}

If you present a SFSafariViewController in a sheet model, you will get a full screen SFSafariViewController and slide from left to right....

Fuxxxking Apple!

mars
  • 109
  • 5
  • 21