kittonian
kittonian

Reputation: 1431

SFSafariViewController Done Button Tappable Area Too Small

There is another post on SO regarding this issue but it's from a while back and only deals with UIKit, not SwiftUI. I have SFSafariViewController working perfectly, and am using it via a programmatic NavigationLink.

The issue is that the tappable area of the Done button is too small. I can only tap on the "ne" of the word "Done" and only at a certain angle. It's very strange and I haven't been able to solve the issue. The rest of the buttons all work perfectly.

If I change to using it modally via a sheet, the Done button works perfectly, but I need to use it via a NavigationLink.

I just need to solve the problem with the tappable area of the Done button.

Thanks!

Here's my SFSafariViewController:

import SwiftUI
import SafariServices

struct SafariView: UIViewControllerRepresentable {
    @Environment(\.dismiss) var dismiss

    let url: URL

    func makeUIViewController(context: Context) -> SFSafariViewController {
        let vc = SFSafariViewController(url: url)
        vc.preferredControlTintColor = .tintColor
        vc.delegate = context.coordinator
        return vc
    }

    func updateUIViewController(_ vc: SFSafariViewController, context: Context) {}

    class Coordinator: NSObject, SFSafariViewControllerDelegate {
        var dismissAction: DismissAction?

        func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
            dismissAction?()
        }
    }

    func makeCoordinator() -> Coordinator {
        let coordinator = Coordinator()
        coordinator.dismissAction = dismiss
        return coordinator
    }
}

Here's my view:

import SwiftUI

struct HomeView: View {

    @State private var isBtnActive: Bool = false

    var body: some View {

        NavigationLink(
            destination: SafariView(url: URL(string: "https://www.example.com")!)
                .navigationBarHidden(true),
            isActive: $isBtnActive,
            label: {
                EmptyView()
            }
        )
        .accessibilityHidden(true)

        VStack(spacing: 0) {
            Spacer()
            Button("Show Website") {
                isBtnActive = true
            }
            Spacer()
        }
    }
}

Upvotes: 2

Views: 304

Answers (1)

Carlos Veny
Carlos Veny

Reputation: 26

I have found a solution in SwiftUI regarding this topic.

This code embeds a SafariViewController into a NavigationView and the "Done" button is working. The only thing that doesn't work is the swipe back gesture.

import SwiftUI
import SafariServices

struct SafariWebView: UIViewControllerRepresentable {
  let url: URL
  @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
  
  func makeCoordinator() -> Coordinator {
    Coordinator(self)
  }
  
  func makeUIViewController(context: Context) -> UINavigationController {
    let safariVC = SFSafariViewController(url: url)
    safariVC.delegate = context.coordinator
    
    let navController = UINavigationController(rootViewController: safariVC)
    navController.setNavigationBarHidden(true, animated: false)
    return navController
  }
  
  func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}
  
  class Coordinator: NSObject, SFSafariViewControllerDelegate {
    var parent: SafariWebView
    
    init(_ parent: SafariWebView) {
      self.parent = parent
    }
    
    func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
      parent.presentationMode.wrappedValue.dismiss()
    }
  }
}

struct AppLink: View {
  var url: String
  
  var body: some View {
    SafariWebView(url: URL(string: url)!)
      .ignoresSafeArea()
      .navigationBarBackButtonHidden(true)
  }
}

#Preview {
  AppLink(url: "https://www.example.com")
}

And then you can use it with a NavigationLink:

NavigationStack {
  NavigationLink("Link App", destination: AppLink(url: "https://www.example.com"))
}

Image:
SafariViewController in NavigationView

Upvotes: 1

Related Questions