누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.
참고 자료: WebKit 공식문서
Overview
WKWebView 객체는 플랫폼 기본 뷰로, 앱 UI에 웹 콘텐츠를 매끄럽게 통합할 수 있도록 지원합니다. 웹 뷰는 전체적인 웹 브라우징 경험을 지원하며, HTML, CSS 및 JavaScript 콘텐츠를 앱의 네이티브 뷰와 함께 제공합니다. 앱의 레이아웃 및 스타일링 요구 사항이 네이티브 뷰보다 웹 기술로 더 쉽게 충족되는 경우에 사용할 수 있습니다. 예를 들어, 앱의 콘텐츠가 자주 변경되는 경우 사용할 수 있습니다.
웹 뷰는 델리게이트 객체를 통해
탐색 및 사용자 경험에 대한 제어를 제공
합니다. 네비게이션 델리게이트를 사용
하여 사용자가 웹 콘텐츠에서 링크를 클릭하거나 탐색에 영향을 미치는 방식으로 콘텐츠와 상호 작용할 때 반응
할 수 있습니다. 예를 들어, 특정 조건이 충족될 때까지 사용자가 새 콘텐츠로 이동하는 것을 방지할 수 있습니다. UI 델리게이트를 사용하여 웹 콘텐츠와 상호 작용에 대한 응답으로 경고 또는 콘텍스트 메뉴와 같은 네이티브 UI 요소를 표시할 수 있습니다.
WKWebView 객체를 프로그래밍 방식으로 뷰 계층 구조에 내장
하거나 인터페이스 빌더
를 사용하여 추가할 수 있습니다. 인터페이스 빌더는 데이터 감지기, 미디어 재생 및 상호 작용 동작과 같은 많은 사용자 정의를 지원합니다. 더 많은 사용자 정의가 필요한 경우, WKWebViewConfiguration 객체를 사용
하여 웹 뷰를 프로그래밍 방식으로 만들 수 있습니다. 예를 들어, 웹 뷰 구성 객체를 사용하여 사용자 정의 URL 스키마에 대한 핸들러를 지정
하고, 쿠키를 관리
하고, 웹 콘텐츠에 대한 환경 설정을 사용자 정의
할 수 있습니다.
화면에 웹 뷰가 나타나기 전
에 URLRequest
구조체를 사용하여 웹 서버에서 콘텐츠를 로드하거나 로컬 파일 또는 HTML 문자열에서 콘텐츠를 직접 로드
합니다. 웹 뷰는 초기 로드 요청의 일부로 이미지나 동영상과 같은 임베디드 리소스를 자동으로 로드
합니다. 그런 다음 콘텐츠를 렌더링하고 결과를 뷰의 바운드 사각형 내에 표시합니다. 다음 코드 예제는 기본 뷰를 사용자 정의 WKWebView 객체로 대체하는 뷰 컨트롤러를 보여줍니다.
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate {
var webView: WKWebView!
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string:"https://www.apple.com")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
}
웹 뷰는 웹 콘텐츠에 나타나는 전화번호를 자동으로 전화 링크로 변환합니다. 사용자가 전화 링크를 탭하면 전화 앱이 시작되고 번호가 다이얼됩니다. 기본 데이터 감지기 동작을 변경하려면 WKWebViewConfiguration 객체를 사용하세요.
setMagnification(_:centeredAt:)
를 사용하여 웹 콘텐츠의 크기
를 웹 뷰에 처음 나타날 때
프로그래밍 방식으로 설정할 수 있습니다. 그 이후로
는 사용자가 제스처를 사용하여 크기를 변경
할 수 있습니다.
웹 콘텐츠를 통한 탐색 관리(Manage the navigation through your web content)
WKWebView는 링크, 앞으로/뒤로 버튼 등을 사용하여 다른 웹 페이지 사이를 이동할 수 있는 완전한 브라우징 경험을 제공합니다. 사용자가 콘텐츠에서 링크를 클릭하면 웹 뷰는 브라우저처럼 작동하여 해당 링크의 콘텐츠를 표시합니다. 탐색을 금지하거나 웹 뷰의 탐색 동작을 사용자 정의
하려면 WKNavigationDelegate 프로토콜
을 준수하는 개체인 탐색 대리자(navigation delegate)를 제공하세요. 탐색 대리자를 사용하여 웹 뷰의 탐색 동작을 수정하거나 새 콘텐츠의 로딩 진행 상황을 추적하세요.
또한 WKWebView의 메소드를 사용하여 콘텐츠를 프로그래밍 방식으로 탐색하거나 앱 인터페이스의 다른 부분에서 탐색을 트리거할 수도 있습니다. 예를 들어, UI에 앞으로/뒤로 버튼이 포함되어 있는 경우 해당 버튼을 웹 뷰의 goBack(_:)
및 goForward(_:)
메소드에 연결하여 해당하는 웹 탐색을 트리거합니다. canGoBack 및 canGoForward 속성
을 사용하여 버튼을 활성화 또는 비활성화할 시기를 결정하세요.
공유 옵션 제공(Provide sharing options)
사람들은 WKWebView의 내용을 앱이나 다른 사람들과 공유하고자 할 수 있습니다. UIActivityViewController를 사용하여 공유 시트를 제공하여 사람들이 웹 콘텐츠를 공유할 수 있는 모든 방법을 제공하세요.
만약 앱이 com.apple.developer.web-browser 권한
을 가지고 있다면, iOS 공유 시트는 http 또는 https 웹 페이지에 대해 홈 화면에 추가하는 Add to Home Screen 옵션
을 제공할 수 있습니다. 이를 통해 웹 앱이나 북마크에 대한 편리한 링크를 만들 수 있습니다. 현재 웹 페이지를 홈 화면에 추가하도록 허용하려면
, UIActivityViewController를 생성할 때 activityItems 배열
에 WKWebView 인스턴스를 포함
시켜 init(activityItems:applicationActivities:)를 호출
하세요. 브라우저 앱을 만드는 방법에 대한 자세한 정보는 Preparing your app to be the default web browser을 참조하세요.
기본적인 설정이나 설치 부분은 많은 자료가 있으니 설치는 알아서..
한줄 설명 : [프로젝트 -> TARGETS > Build Phases -> Link Binary With Libraries -> + -> WebKit.framework]
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate {
var webView: WKWebView!
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string:"https://www.apple.com")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
}
WKUIDelegate
WKNavigationDelegate
브리지 설정을 위해서는 WKWebViewConfiguration
을 WKWebView 초기화 시 넣어주어야 한다.
let contentController = WKUserContentController()
contentController.add(self,name: "bridge")
let configuration = WKWebViewConfiguration()
configuration.userContentController = contentController
bridge를 사용하기 위해선 우선 WKUserContentController
를 생성해 그 안에 사용할 bridge의 이름을 추가한다.
그 후에 WKWebViewConfiguration
를 생성하여 userContentController
에 WKUserContentController
를 넣어준다.
Protocol: WKScriptMessageHandler
.add 를 할 경우 WKScriptMessageHandler
프로토콜을 준수해줘야 하며 아래의 해당 함수를 선언해줘야 한다.
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { }
self.webView = WKWebView(frame: self.view.bounds, configuration: configuration)
self.webViewBase.addSubview(self.webView)
self.layout()
private func layout() {
self.webView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.init(item: self.webView, attribute: .leading, relatedBy: .equal, toItem: self.webViewBase, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint.init(item: self.webView, attribute: .trailing, relatedBy: .equal, toItem: self.webViewBase, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint.init(item: self.webView, attribute: .top, relatedBy: .equal, toItem: self.webViewBase, attribute: .top, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint.init(item: self.webView, attribute: .bottom, relatedBy: .equal, toItem: self.webViewBase, attribute: .bottom, multiplier: 1.0, constant: 0).isActive = true
view.layoutIfNeeded()
}
WKWebView 초기화 시 configuration
에는 1에서 만들어둔 WKWebViewConfiguration
를 넣어준다.
load 메서드를 사용하며 해당 메서드의 선언은 다음과 같다.
func load(_ request: URLRequest) -> WKNavigation?
입력 파라미터로는 URLRequest
를 받는다.
URLRequest의 init초기화 선언은 다음과 같다.
init(
url: URL,
cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy,
timeoutInterval: TimeInterval = 60.0
)
URL 은 string 값이므로 String을 입력하면 되겠지만 조금 더 깔끔한 코드 작성을 위해서 URLComponents
를 사용할 것이다.
URLComponets
에서 base 가 될 URL을 String
이라는 파라미터에 넣어준다.
URLQueryItem
을 만들어준다. URL의 쿼리 부분으로 들어갈 name-value 쌍이다.
URLComponents
의 queryItems
에 만들어준 item들을 넣어준다. 넣어준 순서대로 URL 쿼리 문자열에 나오게 된다.
var components = URLComponents(string: "https://m.search.naver.com/search.naver?sm=mtp_hty.top&where=m&")!
components.queryItems = [URLQueryItem(name: "query", value: "Swift")]
let request = URLRequest(url: components.url!)
self.webView.load(request)
1에서 WKScriptMessageHandler
프로토콜을 위임 받은 후에 선언해준 함수를 통해서 Bridge 가 들어오게 된다.
message.name
으로 어떠한 Bridge인지 확인하고 그에 맞는 로직을 작성한다.
파라메터는 message.body
에 담겨오며 Any 타입
이기에 사용하고자 하는 타입으로 타입캐스팅 후 사용하면 된다.
Protocol: WKNavigationDelegate
Protocol: WKUIDelegate
위의 두 프로토콜의 경우에는 위에서 기초적인 코드
부분에서 관련 프로토콜부분에서 이야기를 했으며 해당 프로토콜로 동작하고자 하는 함수들은 따로 문서를 보고 원하는 함수들을 사용한다.
Preferences
와 Configuration
의 설정이 추가로 더 필요하다.
let preferences = WKPreferences()
let defaultPreferences = WKWebpagePreferences()
preferences.javaScriptCanOpenWindowsAutomatically = true
if #available(iOS 14.0, *) {
defaultPreferences.allowsContentJavaScript = true
configuration.defaultWebpagePreferences = defaultPreferences
} else {
preferences.javaScriptEnabled = true
configuration.preferences = preferences
}
javaScriptCanOpenWindowsAutomatically
해당 프로퍼티는 사용자의 동작 없이 자동으로 자바스크립트가 창을 열 수 있는지를 확인 하는 값이다.
javaScriptEnabled
와 allowsContentJavaScript
는 둘 다 자바스크립트의 실행 여부를 확인하는 프로퍼티이다.
분기처리를 한 이유는 javaScriptEnabled
해당 프로퍼티가 deprecated 되기 때문이다.
velog: [Deprecated] WebKit : javaScriptEnabled
해당 preference 작업이 끝나면 configuration 의 프로퍼티에 맞게 넣어주면 된다.
그 후에는 델리게이트 사용을 위한 델리게이트 지정까지 끝내주면 WebView 구현은 끝나게 된다.
self.webView.uiDelegate = self
self.webView.navigationDelegate = self
import UIKit
import WebKit
class WebViewController: UIViewController{
@IBOutlet weak var webViewBase: UIView!
private var webView: WKWebView = WKWebView()
override func viewDidLoad() {
super.viewDidLoad()
self.webView.uiDelegate = self
self.webView.navigationDelegate = self
self.navigationItem.title = "WebView"
let contentController = WKUserContentController()
let configuration = WKWebViewConfiguration()
contentController.add(self,name: "bridge")
configuration.userContentController = contentController
let preferences = WKPreferences()
let defaultPreferences = WKWebpagePreferences()
preferences.javaScriptCanOpenWindowsAutomatically = true
if #available(iOS 14.0, *) {
defaultPreferences.allowsContentJavaScript = true
configuration.defaultWebpagePreferences = defaultPreferences
} else {
preferences.javaScriptEnabled = true
configuration.preferences = preferences
}
self.webView = WKWebView(frame: self.view.bounds, configuration: configuration)
self.layout()
var components = URLComponents(string: "https://m.search.naver.com/search.naver?sm=mtp_hty.top&where=m&")!
components.queryItems = [URLQueryItem(name: "query", value: "Swift")]
let request = URLRequest(url: components.url!)
webView.load(request)
}
private func layout() {
self.webViewBase.addSubview(self.webView)
self.webView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.init(item: self.webView, attribute: .leading, relatedBy: .equal, toItem: self.webViewBase, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint.init(item: self.webView, attribute: .trailing, relatedBy: .equal, toItem: self.webViewBase, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint.init(item: self.webView, attribute: .top, relatedBy: .equal, toItem: self.webViewBase, attribute: .top, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint.init(item: self.webView, attribute: .bottom, relatedBy: .equal, toItem: self.webViewBase, attribute: .bottom, multiplier: 1.0, constant: 0).isActive = true
view.layoutIfNeeded()
}
}
extension WebViewController: WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.name)
}
}
관련 동작을 하는 부분들을 묶어 두었으니 확인하기에는 편하나 사용에서는 변수나 함수의 위치는 새로고쳐서 코드 작성을 해야한다.
Protocol: WKScriptMessageHandler
Protocol: WKNavigationDelegate
Protocol: WKUIDelegate
Struct: URLRequest
Struct: URLComponents
velog: [Deprecated] WebKit : javaScriptEnabled
당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.