iOS WKWebView 예제
1.Interface 작성
웹뷰를 사용하기위한 프로토콜을 채택하고, WKWebView 프로퍼티와 변수를 만들어준다. 여기서 스토리보드로 웹킷뷰를 만들어서 연결 시켜주면안된다. 스토리보드로 만든 웹킷뷰는 읽기 전용이므로, 자바스크립트와 통신을 위한 속성을 지정해줄수 없다. 그래서 UIView 타입의 웹뷰 컨테이너를 만들어서 웹뷰를 컨테이너 안에 넣어주는 방식으로 구현해야 한다.
ViewController.h
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
#import "Reachability.h"
// WkWebView에서 주소가 변경되거나 스크립트를 사용할 경우 사용할 Delegate함수를 사용하기 위해
// 아래의 Delegate들을 참조합니다
@interface ViewController : UIViewController <WKUIDelegate , WKNavigationDelegate , WKScriptMessageHandler>
{
//네트워크 연결 체크
Reachability *internetReach;
}
//JS 에서 네이티브로 리턴값 전달 출력 변수
@property (strong, nonatomic) IBOutlet UILabel *outputtext;
//웹뷰
@property (strong, nonatomic) WKWebView *webView;
//웹뷰 컨테이너
@property (strong, nonatomic) IBOutlet UIView *webViewContainer;
@end
2.지역변수 선언
웹뷰의 설정을 담당하는 객체와 자바스크립트와 통신을 주고 받을 수 있는 클래스를 변수로 설정한다.
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
{
// 지역 변수를 선언합니다.
// 웹뷰의 랜더 속도 및 각종 설정들을 담당하는 클래스입니다.
WKWebViewConfiguration *config;
// 자바스크립트에서 메시지를 받거나 자바스크립트를 실행하는데 필요한 클래스입니다.
WKUserContentController *contentController;
}
@synthesize webViewContainer;
@synthesize webView;
3. 네트워크 연결 체크
viewDidAppear 함수에서 Reachability 클래스를 이용해서 네트워크가 잘 연결되어있는지 체크를 한번 해준다.
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[self networkCheck];
}
#pragma 네트워크 상태 체크
//네트워크 상태 체크 Notification 등록
- (void)networkCheck
{
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(reachabilityChanged:) name: kReachabilityChangedNotification object: nil];
internetReach = [Reachability reachabilityForInternetConnection];
[internetReach startNotifier];
[self updateInterfaceWithReachability:internetReach];
}
// 네트워크 상태가 변경 될 경우 호출된다.
- (void)reachabilityChanged:(NSNotification *)note
{
Reachability *curReach = [note object];
NSParameterAssert([curReach isKindOfClass: [Reachability class]]);
[self updateInterfaceWithReachability:curReach];
}
// 네트워크 상태가 변경 될 경우 처리
- (void)updateInterfaceWithReachability:(Reachability *)curReach
{
if(curReach == internetReach)
{
NetworkStatus netStatus = [curReach currentReachabilityStatus];
NSString *statusString = @"";
switch (netStatus)
{
case NotReachable:
statusString = @"Access Not Available";
break;
case ReachableViaWiFi:
statusString = @"Reachable WiFi";
break;
case ReachableViaWWAN:
statusString = @"Reachable WWAN";
break;
default:
break;
}
NSLog(@"Net Status changed. current status=%@ ", statusString);
// 네트워크 상태에 따라 처리
}
}
4.웹뷰 초기화 웹킷뷰 로드하기
viewDidLoad 함수에서 웹킷뷰를 초기화 하고 , 해당 컨텐츠를 URL을 이용해서 Load 해준다.
웹킷뷰를 초기화 할때 WKUserContentController 클래스를 이용해서 자바스크립트와 통신을 위한 브릿지(hello, test)를 설정해준다. 그 후 WKUserContentController 객체를 WKWebViewConfiguration 객체의 UserContentController 에 할당해 준다. WKWebViewConfiguration 객체는 WKWebView를 초기화 할때 사용된다.
그리고 웹뷰를 웹뷰컨테이너에 맞춰주고 View에 넣어준다.
1. WKUserContentController 셋팅 => 2.WKWebViewConfiguration 셋팅 => 3.WKWebView 초기화
- (void)viewDidLoad {
[super viewDidLoad];
//1.웹킷뷰 초기화
[self initWebview_then_callFromJs];
//2.웹킷뷰로 로드하기
[self loadUrl];
}
//1.웹킷뷰 초기화
-(void) initWebview_then_callFromJs{
// WkWebViewConfiguration과 WKUserContentController를 초기화해줍니다.
config = [[WKWebViewConfiguration alloc]init];
contentController = [[WKUserContentController alloc]init];
//브릿지명 설정
// 자바스크립트 -> ios에 사용될 핸들러 이름을 추가해줍니다.
[contentController addScriptMessageHandler:self name:@"hello"];
[contentController addScriptMessageHandler:self name:@"test"];
// WkWebView의 configuration에 스크립트에 대한 설정을 정해줍니다.
[config setUserContentController:contentController];
[config.preferences setJavaScriptEnabled:TRUE];
//해당 config를 웹뷰 정의시 등록해주기
//WkWebView는 IBOutlet으로 제공되지 않아 스토리보드에서 추가할 수 없습니다.
//웹뷰의 크기를 정해준 후 초기화하고 본 ViewController의 뷰에 추가합니다.
webView = [[WKWebView alloc] initWithFrame:webViewContainer.bounds configuration:config];
//웹뷰 오토레이아웃 설정 (위치설정)
webView.translatesAutoresizingMaskIntoConstraints = FALSE;
[webViewContainer addSubview:webView];
[[[webView leadingAnchor] constraintEqualToAnchor:webViewContainer.leadingAnchor constant:0] setActive:TRUE];
[[[webView trailingAnchor] constraintEqualToAnchor:webViewContainer.trailingAnchor constant:0] setActive:TRUE];
[[[webView topAnchor] constraintEqualToAnchor:webViewContainer.topAnchor constant:0] setActive:TRUE];
[[[webView bottomAnchor] constraintEqualToAnchor:webViewContainer.bottomAnchor constant:0] setActive:TRUE];
}
//2.웹킷뷰로 로드하기
-(void) loadUrl{
NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"html"];
//WKWebView 특정 주소로 로딩 시키기
NSURL * url = [NSURL fileURLWithPath:urlStr];
NSMutableURLRequest *request =
[NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
//html,js 파일 로드하기
[webView loadRequest:request];
// 웹뷰의 딜리게이트들을 새로 초기화해줍니다.
[webView setUIDelegate:self];
[webView setNavigationDelegate:self];
}
5.WKScriptMessageHandler
WKScriptMessageHandler에 의해 생성된 delegate 함수.
자바스크립트에서 ios에 wekkit핸들러를 통해 함수 호출시 메세지의 name 과 body로 구분해서 어떤 js 함수에서 호출했는지와 데이터를 가져온다.
#pragma mark - WKScriptMessageHandler method
// WKScriptMessageHandler에 의해 생성된 delegate 함수입니다.
// 자바스크립트에서 ios에 wekkit핸들러를 통해 함수 호출시
//메세지의 name 과 body로 구분해서 어떤 js 함수에서 호출했는지와 데이터를 가져온다.
- (void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message{
NSLog(@"didReceiveScriptMessage !");
if([message.name isEqualToString:@"hello"]){
NSString *str = [message body];
_outputtext.text = str;
} else if([message.name isEqualToString:@"test"]){
NSString *str = [message body];
_outputtext.text = str;
}
}
6.WKUIDelegate Method 3 가지 : javascript와 관련된 트리거 이벤트 캐치
WKUIDelegate 3가지 필수 Callback 함수 : 자바스크립트에서 얼러트창 뜨면 이곳에서 처리된다.
#pragma WKUIDelegate Method 3 가지 : javascript와 관련된 트리거 이벤트 캐치
//WKUIDelegate 3가지 필수 Callback 함수 : 자바스크립트에서 얼러트창 뜨면 이곳에서 처리된다.
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
UIAlertController * alert = [UIAlertController
alertControllerWithTitle:@"알림"
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction
actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
completionHandler();
}];
UIAlertAction* cancel = [UIAlertAction
actionWithTitle:@"Cancel"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
completionHandler();
}];
[alert addAction:ok];
[alert addAction:cancel];
[self presentViewController:alert animated:YES completion:nil];
}
//자바스크립트에서 컨펌 함수 호출시
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
UIAlertController * alert = [UIAlertController
alertControllerWithTitle:@"알림"
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction
actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
completionHandler(YES);
}];
UIAlertAction* cancel = [UIAlertAction
actionWithTitle:@"Cancel"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
completionHandler(NO);
}];
[alert addAction:ok];
[alert addAction:cancel];
[self presentViewController:alert animated:YES completion:nil];
}
//프롬프트 함수 호출 시
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
UIAlertController * alert= [UIAlertController
alertControllerWithTitle:@"알림"
message:prompt
preferredStyle:UIAlertControllerStyleAlert];
//텍스트 필드 넣어주기
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.text = defaultText;
}];
UIAlertAction* ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
//Do Some action here
completionHandler(alert.textFields.firstObject.text);
}];
UIAlertAction* cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
completionHandler(nil);
}];
//버튼 넣어주기
[alert addAction:ok];
[alert addAction:cancel];
[self presentViewController:alert animated:YES completion:nil];
}
7. WKNavigationDelegate Method : 중복적으로 리로드 방지를 위한 함수
#pragma WKNavigationDelegate Method : 중복적으로 리로드 방지를 위한 함수
//WKNavigationDelegate 중복적으로 리로드 방지 (iOS 9 이후지원)
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{
[webView reload];
}
8. WKWebWeb Navigation Delegate Method
#pragma WKWebWeb Navigation Delegate Method
//웹뷰 로딩시 호출
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
NSLog(@"- didStartProvisionalNavigation : 웹뷰 로딩시 호출되는 델리게이트 함수");
}
//웹뷰 로딩 완료시 호출
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
NSLog(@"- didFinishNavigation : 웹뷰 로딩 완료시 호출되는 델리게이트 함수");
}
//웹뷰 로딩 실패시 호출
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{
NSLog(@"- didFailNavigation : 웹뷰 로딩 실패시 호출");
}
//웹뷰 로딩 여부 체크를 위한 델리게이트 함수
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
NSLog(@"- decidePolicyForNavigationAction : 웹뷰 로딩 여부 체크를 위한 델리게이트 함수");
// //WKWebView 특정 주소로 로딩 시키기
// NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"html"];
//
// //WKWebView 특정 주소로 로딩 시키기
// NSURL * url2 = [NSURL fileURLWithPath:urlStr];
//
// NSString *url = [url2 absoluteString];
// url = [url stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
//
// if ([url rangeOfString:@"test.html"].location != NSNotFound) {
// // custom fundation
// decisionHandler(WKNavigationActionPolicyCancel);
// return; // the same as to use if...else
// }
decisionHandler(WKNavigationActionPolicyAllow);
}
9.자바스크립트 인터페이스 구현
#pragma 자바스크립트 인터페이스 구현
//뒤로가기
- (IBAction)back:(id)sender {
if ([webView canGoBack]) {
[webView goBack];
}else{
NSLog(@"뒤로 갈 페이지가 없습니다.");
}
}
//앞으로가기
- (IBAction)forward:(id)sender {
if ([webView canGoForward]) {
[webView goForward];
}else{
NSLog(@"앞으로 갈 페이지가 없습니다.");
}
}
//리로드
- (IBAction)reload:(id)sender {
NSLog(@"reload 실행");
[webView reload];
}
//홈으로
- (IBAction)home:(id)sender {
NSLog(@"home 으로");
//WKWebView 특정 주소로 로딩 시키기
NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"html"];
//WKWebView 특정 주소로 로딩 시키기
NSURL * url = [NSURL fileURLWithPath:urlStr];
NSMutableURLRequest *request =
[NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
//html,js 파일 로드하기
[webView loadRequest:request];
}
//자바스크립트 호출
- (IBAction)callJS:(id)sender {
NSLog(@"ios => 자바스크립트 함수 호출");
NSString *funcName = @"myJsFunction()";
[webView evaluateJavaScript:funcName completionHandler:^(NSString * result, NSError * _Nullable error) {
// JS = > 네이티브 리턴 결과
NSLog(@"result : %@" , result);
}];
}
//자바스크립트 호출 with 파라미터
- (IBAction)callJSWithParams:(id)sender {
NSLog(@"ios => 자바스크립트 함수 호출 - 파라미터");
NSString *name = @"mynameis..";
NSString *age = @"22";
//파라미터 같이 전달
NSString *funcName = [[NSString alloc]initWithFormat:@"myJsFunctionParam('%@' , '%@')" ,name, age];
[webView evaluateJavaScript:funcName completionHandler:^(NSString * result, NSError * _Nullable error) {
// JS = > 네이티브 리턴 결과
NSLog(@"result : %@" , result);
}];
}
10. html 및 js 부분
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
<title>Hello, world!</title>
</head>
<body>
<div class="container-fluid">
<h1>webViewTest</h1>
<!-- js 에서 네이티브 함수 호출 -->
<button type="button" onclick="myFunction()" class="btn btn-outline-danger">JS=>IOS - 1 호출</button>
<button type="button" onclick="callNativeFunctionTwo()" class="btn btn-outline-warning">JS=>IOS - 2 호출</button>
<!-- 인터렉티브 창 함수 호출 (JS => 네이티브)-->
<button type="button" onclick="showAlert()" class="btn btn-outline-primary">showAlert</button>
<button type="button" onclick="showPrompt()" class="btn btn-outline-secondary">showPrompt</button>
<button type="button" onclick="showConfirm()" class="btn btn-outline-success">showConfirm</button>
</div>
<script>
//네이티브 함수1
function myFunction() {
console.log("myFunction");
var data = "JS=>IOS - 1";
window.webkit.messageHandlers.hello.postMessage(data);
}
//네이티브 함수2
function callNativeFunctionTwo() {
console.log("callNativeFunctionTwo");
var data = "JS=>IOS - 2";
window.webkit.messageHandlers.test.postMessage(data);
}
//네이티브 함수 3
function callNativeFunctionThree() {
console.log("callNativeFunction Three");
var data = "JS=>IOS - 3";
window.webkit.messageHandlers.test.postMessage(data);
}
//alert 함수
function showAlert() {
alert("showAlert");
}
//프롬프트창 함수
function showPrompt() {
var userInput = prompt("What u r name"+"?");
alert(userInput+" hi~");
}
//콘펌창 함수
function showConfirm() {
var result = confirm("can you?");
if(result){
alert("yes~!");
}else{
alert("oh~ no~");
}
}
//IOS 네이티브 => JS 호출 함수 - 1
function myJsFunction() {
return "good job!";
}
//IOS 네이티브 => JS 호출 함수 - 2
function myJsFunctionParam(paramA, paramB) {
var result = paramA + " : " + paramB ;
return result;
}
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous">
</script>
</body>
</html>
네트워크 체크 클래스 파일
예제 파일
애플로그인 + keychain 프로젝트
ios 실시간 네트워크 체크
https://reysion.tistory.com/48
웹킷뷰 개념
https://caution-dev.github.io/tag/#WebKit
melod-it.gitbook.io/sagwa/app-services/webkit/wkwebview
'아이폰 개발 > ios 개념&튜토리얼' 카테고리의 다른 글
iOS 화면보호기 예제 (0) | 2021.02.03 |
---|---|
iOS UIActivityIndicatorView 예제 (0) | 2021.02.02 |
NSURLSession 공통 모듈 예제 (0) | 2021.01.29 |
iOS 공통 로그 예제 (0) | 2021.01.28 |
디자인 패턴 - 싱글톤 패턴 예제 (0) | 2021.01.28 |