본문 바로가기

EXPERIENCE/iOS

[Xcode/iOS] SwiftUI 웹뷰(WebView)와 사파리(Safari)로 페이지 화면가져오는 코드 구현해보기 (with. Safari URL 변경 안될 때)

728x90
728x90
SMALL

 

 

 

 

 

 

 

 

 

간단하지만 꼭 사용하게 되는 웹뷰 기능 iOS는 2~3가지 구현 방법이 있다고 하는데 그 중 2가지를 살펴보고자 한다.

첫번째는 가장 일반적인 WKWebView 라이브러리를 사용하는 방법이다.

두번째는 Safari와 같은 환경으로 보여주는 SafariService 프레임워크를 사용하는 방법이다.

두개 다 크게 어려운건 없었기에 아래 간단히 결과화면과 코드 설명만 작성해본다.

 

 

 

 

 

 

 

결과화면

 

 

 

 

좌측은 Safari 환경이 적용된 SFSafariViewController로 구현한 모습이고,

우측은 WKWebView를 이용해 구현한 웹뷰이다.

 

 

 

 

 

 

 

 

 

Github
 

GitHub - sohay19/WebView: Example WebView

Example WebView. Contribute to sohay19/WebView development by creating an account on GitHub.

github.com

 

 

 

 

 

 

 

 

 

구현

 

  • Enums.swift
enum Page:String {
	case Naver = "https://www.naver.com"
	case Google = "https://www.google.com"
	case Tistory = "https://s-o-h-a.tistory.com"
}
오픈할 페이지 URL주소를 Enum 형식으로 관리

 

 

  • SafariView.swift
import SafariServices

class CustomSafariViewController : UIViewController {
    private var currentViewController:SFSafariViewController?
    
    func openUrl(_ url:URL) {
        let newViewController = SFSafariViewController(url: url)
        if let currentViewController = currentViewController {
            currentViewController.view.removeFromSuperview()
            currentViewController.removeFromParent()
            self.currentViewController = nil
        }
        addChild(newViewController)
        newViewController.view.frame = view.frame
        view.addSubview(newViewController.view)
        currentViewController = newViewController
    }
}


struct SafariView: UIViewControllerRepresentable {
    var url: URL
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<SafariView>) -> CustomSafariViewController {
        return CustomSafariViewController()
    }
    
    func updateUIViewController(_ uiViewController: CustomSafariViewController, context: UIViewControllerRepresentableContext<SafariView>) {
        uiViewController.openUrl(url)
    }
}


struct SafariView_Previews: PreviewProvider {
    static var previews: some View {
        SafariView(url: URL(string: "http://www.naver.com")!)
    }
}
- Safari 형식으로 열리는 웹뷰
- UIViewController에 SFSafariViewController가 자식으로 들어있는 형태
- SafariView를 생성할 때 인스턴스를 재사용하기에 직접 삭제 후 URL 주소를 초기화 해주어야함 
- UIViewController이므로 UIViewControllerRepresentable을 사용하여 SwiftUI에서 사용할 수 있도록 한다

 

 

  • WebView.swift
import WebKit

class CustomWebViewContorller : UIViewController {
    var url:URL?
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // 웹뷰 추가
        let newView = WKWebView()
        newView.frame = view.frame
        view.addSubview(newView)
        // URL 로드
        guard let url = url else { return }
        newView.load(URLRequest(url: url))
    }
}

struct WebView : UIViewControllerRepresentable {
    var url: URL
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<WebView>) -> CustomWebViewContorller {
        return CustomWebViewContorller()
    }
    
    func updateUIViewController(_ uiViewController: CustomWebViewContorller, context: UIViewControllerRepresentableContext<WebView>) {
        uiViewController.url = url
    }
}

struct WebView_Previews: PreviewProvider {
    static var previews: some View {
        WebView(url: URL(string: "http://www.naver.com")!)
    }
}
- 일반 WebView 형식
- UIViewController를 열고 WKWebView화면을 추가하여 보여준다
- "This method should not be called on the main thread as it may lead to UI unresponsiveness"라는 오류가 계속 관찰되는데, Xcode 14이상 & iOS 16이상 에서 관찰되는 문제로 DispatchQueue.main을 사용해도 소용없는 버그로 보여진다. 추후 Apple에서 수정하거나 방법을 찾아봐야할 것으로 보여짐

 

 

  • ContentView.swift
struct ContentView: View {
    // Safari 열림 체크
    @State private var isSafariShow = false
    // WebView 열림 체크
    @State private var isWebShow = false
    // 열릴 URL
    @State private var targetUrl:URL?
    
    var body: some View {
        NavigationStack {
            // Safari
            VStack {
                Text("Safari로 열기")
                    .padding()
                    .background(.black)
                    .foregroundColor(.white)
                    .font(.headline)
                    .cornerRadius(10)
                
                Button {
                    setSafariUrl(.Naver)
                } label: {
                    Text("네이버 열기")
                }
                .navigationDestination(isPresented: $isSafariShow) {
                    if let targetUrl = targetUrl {
                        SafariView(url: targetUrl)
                    }
                }
                .padding()
                
                Button {
                    setSafariUrl(.Google)
                } label: {
                    Text("구글 열기")
                }
                .navigationDestination(isPresented: $isSafariShow) {
                    if let targetUrl = targetUrl {
                        SafariView(url: targetUrl)
                    }
                }
                .padding()
                
                Button {
                    setSafariUrl(.Tistory)
                } label: {
                    Text("티스토리 열기")
                }
                .navigationDestination(isPresented: $isSafariShow) {
                    if let targetUrl = targetUrl {
                        SafariView(url: targetUrl)
                    }
                }
                .padding()
            }
            .padding(60)
            
            //WebView
            VStack {
                Text("WebView로 열기")
                    .padding()
                    .background(.black)
                    .foregroundColor(.white)
                    .font(.headline)
                    .cornerRadius(10)
                
                Button {
                    setWebUrl(.Naver)
                } label: {
                    Text("네이버 열기")
                }
                .navigationDestination(isPresented: $isWebShow) {
                    if let targetUrl = targetUrl {
                        WebView(url: targetUrl)
                    }
                }
                .padding()
                
                Button {
                    setWebUrl(.Google)
                } label: {
                    Text("구글 열기")
                }
                .navigationDestination(isPresented: $isWebShow) {
                    if let targetUrl = targetUrl {
                        WebView(url: targetUrl)
                    }
                }
                .padding()
                
                Button {
                    setWebUrl(.Tistory)
                } label: {
                    Text("티스토리 열기")
                }
                .navigationDestination(isPresented: $isWebShow) {
                    if let targetUrl = targetUrl {
                        WebView(url: targetUrl)
                    }
                }
                .padding()
            }
        }
    }
    
    // Safari로 열릴 페이지 설정
    func setSafariUrl(_ pageType:Page) {
        guard let url = URL(string: pageType.rawValue) else { return }
        targetUrl = url
        isSafariShow.toggle()
    }
    
    // Web으로 열릴 페이지 설정
    func setWebUrl(_ pageType:Page) {
        guard let url = URL(string: pageType.rawValue) else { return }
        targetUrl = url
        isWebShow.toggle()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
- Safari혹은 일반 WebView로 페이지를 여는 화면
- 버튼을 클릭할 때 url을 세팅하고 bool값을 변경시키며 동작하는 형태이다.

 

 

 

 

 

 

 

 

참고사이트
 

How do I use SFSafariViewController with SwiftUI?

I'm trying to present a SFSafariViewController from a NavigationButton but I'm not sure how to do that with SwiftUI. In UIKit, I would just do: let vc = SFSafariViewController(url: URL(string: "...

stackoverflow.com

 

 

SwiftUI and SFSafariViewController

This week I started playing with SwiftUI and tried implementing some basic things: building a List with Sections, creating Custom cells etc. And I wanted then to simply display a SFSafariViewController when selecting an element of my List.

david.y4ng.fr

 

 

[SwiftUI] WKWebView를 이용한 WebView 만들기

WKWebview, UIViewRepresentable, URL을 활용한swiftui에서 webview 띄우기

velog.io

 

728x90
728x90
LIST