iOS 使用WKWebView替换UIWebView(二)——代码实战(包括OC与JS互调)

本文详细介绍如何使用WKWebView进行iOS应用内的网页加载与JavaScript交互。包括配置WKWebView、注入JavaScript脚本、实现页面加载监听及处理JavaScript对话框等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、ViewController.h

//
//  ViewController.h
//  WebViewDemo
//
//  Created by 555chy on 6/28/16.
//  Copyright © 2016 555chy. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>

@interface ViewController : UIViewController<WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler>


@end

2、ViewController.m

//
//  ViewController.m
//  WebViewDemo
//
//  Created by 555chy on 6/28/16.
//  Copyright © 2016 555chy. All rights reserved.
//

#import "ViewController.h"

#define SCRIPT_MESSAGE_HANDLER_NAME @"scriptMessageHandlerName"

@interface ViewController ()
@property UIButton *button;
@property WKWebView *webView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    CGRect rect = self.view.bounds;
    //把内容的范围减去标题栏
    rect.origin.y += 22;
    rect.size.height -= 22;
    
    self.button = [[UIButton alloc] initWithFrame:CGRectMake(rect.origin.x, rect.origin.y+rect.size.height-50, rect.size.width, 50)];
    self.button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
    self.button.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
    [self.button setTitle:@"button" forState:UIControlStateNormal];
    [self.button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [self.button setBackgroundColor:[UIColor blackColor]];
    [self.view addSubview:self.button];
    
    NSString *js = @"var count = document.images.length; for(var i=0;i<count;i++) {var image = document.images[i]; image.style.width=320;}; window.alert('找到'+count+'张图')";
    /*
     @enum WKUserScriptInjectionTime
     @abstract when a user script should be injected into a webpage.
     @constant WKUserScriptInjectionTimeAtDocumentStart    Inject the script after the document element has been created, but before any other content has been loaded.
     @constant WKUserScriptInjectionTimeAtDocumentEnd      Inject the script after the document has finished loading, but before any subresources may have finished loading.
     NS_ASSUME__BEGIN
     
     typedef NS_ENUM(NSInteger, WKUserScriptInjectionTime) {
     WKUserScriptInjectionTimeAtDocumentStart,
     WKUserScriptInjectionTimeAtDocumentEnd
     } NS_ENUM_AVAILABLE(10_10, 8_0);
     */
    WKUserScript *jsScript = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
    
    NSString *cookie = @"TestCookieKey1=TestCookieValue1;TestCookieKey2=TestCookieValue2";
    WKUserScript *cookieScript = [[WKUserScript alloc] initWithSource:cookie injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
    
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    //设置加载完成后,就会被调用的javascript脚本
    [configuration.userContentController addUserScript:jsScript];
    [configuration.userContentController addUserScript:cookieScript];
    
    //JavaScript只能调用向WKWebView注册过的方法
    [configuration.userContentController addScriptMessageHandler:self name:SCRIPT_MESSAGE_HANDLER_NAME];
    
    //To initialize an instance with the default configuration. The initializer copies the specified configuration, so mutating the configuration after invoking the initializer has no effect on the web view.
    //意思是configuration仅能在webview初始化的时候设置
    self.webView = [[WKWebView alloc] initWithFrame:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height-100) configuration:configuration];
    
    [self.webView setNavigationDelegate:self];
    [self.webView setUIDelegate:self];
    
    //加载网页(前面加不加<html>或<body>的标签都一样)
    [self.webView loadHTMLString:@"<ol><li><image src='https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1467217122235&di=dbb5ffb49d8f588813f73e695467affe&imgtype=jpg&src=http%3A%2F%2Fwww.yooyoo360.com%2Fphoto%2F2009-1-1%2F20090112124741382.jpg' /></li><li><image src='https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1467207071&di=288773b78ea4efc22ce9145364cfad0a&src=http://www.yooyoo360.com/photo/2009-1-1/20090112114754542.jpg' /></li><ol>" baseURL:nil];
    
    /*
    //加载网页
    NSString *urlStr = @"https://www.baidu.comfadfa";
    NSURL *url = [NSURL URLWithString:urlStr];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    //添加Cookie,让WKWebView知道登录状态
    [request addValue:cookie forHTTPHeaderField:@"Cookie"];
    [self.webView loadRequest:request];
    */
    
    //添加左滑回退的功能
    self.webView.allowsBackForwardNavigationGestures = true;
    [self.view addSubview:self.webView];
    
    [self.button addTarget:self action:@selector(buttonDidClick) forControlEvents:UIControlEventTouchUpInside];
}

#pragma mark - WKScriptMessageHandler(@protocol)
/*
 @abstract Invoked when a script message is received from a webpage.
 @param userContentController The user content controller invoking the
 delegate method.
 @param message The script message received.
 
 OC中注册供JS调用的方法
 [self.webView.configuration.userContentController addScriptMessageHandler:self name:<name>];
 JS在调用OC中注册的方法的时候要使用下面的方式:
 window.webkit.messageHandlers.<name>.postMessage(<messageBody>);
 注意:name(方法名)是放在中间的,messageBody只能是一个对象,如果要传多个值,需要封装成数组或是字典。
 另外:xxx...xxx.postMessage(),即如果‘messageBody’不填,也会报错
 */
-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    NSLog(@"didReceiveScriptMessage, name = %@, body = %@", message.name, message.body);
}

#pragma mark - WKWebView执行javascript的方法
-(void)buttonDidClick {
    NSLog(@"buttonDidClick");
    
    //js显示对话框
    NSString *js1 = @"var count = document.images.length; for(var i=0;i<count;i++) {var image = document.images[i]; image.style.width=320;}; window.alert('找到'+count+'张图')";
    [self.webView evaluateJavaScript:js1 completionHandler:^(id _Nullable obj, NSError * _Nullable error) {
        NSLog(@"evaluateJavaScript, obj = %@, error = %@", obj, error);
    }];
    
    //js调用oc的方法
    NSString *js2 = [NSString stringWithFormat:@"var msg = {'key1':'value1','key2':'value2'};window.webkit.messageHandlers.%@.postMessage(msg);", SCRIPT_MESSAGE_HANDLER_NAME];
    [self.webView evaluateJavaScript:js2 completionHandler:^(id _Nullable obj, NSError * _Nullable error) {
        NSLog(@"evaluateJavaScript, obj = %@, error = %@", obj, error);
    }];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - WKNavigationDelegate 用来追踪加载过程
//Provisional - 临时的
//页面开始加载时调用
-(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
    NSLog(@"didStartProvisionalNavigation");
}

//内容开始返回时调用
-(void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"didCommitNavigation");
}

//页面加载完成之后被调用
-(void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"didFinishNavigation");
}

//页面加载失败之后被调用
-(void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:( NSError *)error {
    NSLog(@"didFailProvisionalNavigation");
}

#pragma mark - WKNavigationDelegate 页面跳转
//在接受到服务器的重定向请求之后调用
-(void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
}

//Policy - 策略性的
//在接收到响应后,决定是否跳转
-(void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
    
    NSLog(@"decidePolicyForNavigationResponse, navigationResponse = %@", navigationResponse);
    decisionHandler(WKNavigationResponsePolicyAllow);
}

//在发送请求之前,决定是否跳转
-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    
    //'stringByReplacingPercentEscapesUsingEncoding:' is deprecated: first deprecated in iOS 9.0 - Use -stringByRemovingPercentEncoding instead, which always uses the recommended UTF-8 encoding.
    //NSString *urlStr = [navigationAction.request.URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSString *urlStr = [navigationAction.request.URL.absoluteString stringByRemovingPercentEncoding];
    NSLog(@"decidePolicyForNavigationAction, navigationAction.request.URL = %@", urlStr);
    /*
     typedef NS_ENUM(NSInteger, WKNavigationActionPolicy) {
     WKNavigationActionPolicyCancel,
     WKNavigationActionPolicyAllow,
     } NS_ENUM_AVAILABLE(10_10, 8_0);
     */
    decisionHandler(WKNavigationActionPolicyAllow);
}

//接收到用户身份认证之后触发
-(void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(nonnull NSURLAuthenticationChallenge *)challenge completionHandler:(nonnull void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
    
    NSLog(@"didReceiveAuthenticationChallenge");
    /*
     typedef NS_ENUM(NSUInteger, NSURLCredentialPersistence) {
     NSURLCredentialPersistenceNone,
     NSURLCredentialPersistenceForSession,
     NSURLCredentialPersistencePermanent,
     NSURLCredentialPersistenceSynchronizable NS_ENUM_AVAILABLE(10_8, 6_0)
     };
     */
    NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:@"username" password:@"password" persistence:NSURLCredentialPersistenceForSession];
    /*
     typedef NS_ENUM(NSInteger, NSURLSessionAuthChallengeDisposition) {
        NSURLSessionAuthChallengeUseCredential = 0,                                        Use the specified credential, which may be nil
        NSURLSessionAuthChallengePerformDefaultHandling = 1,                               Default handling for the challenge - as if this delegate were not implemented; the credential parameter is ignored.
        NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2,                        The entire request will be canceled; the credential parameter is ignored.
        NSURLSessionAuthChallengeRejectProtectionSpace = 3,                                This challenge is rejected and the next authentication protection space should be tried; the credential parameter is ignored.
    } NS_ENUM_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0);
    */
    completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
}

#pragma mark - WKUIDelegate 新窗口
//打开一个新的窗口
-(WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(nonnull WKWebViewConfiguration *)configuration forNavigationAction:(nonnull WKNavigationAction *)navigationAction windowFeatures:(nonnull WKWindowFeatures *)windowFeatures {
    
    NSLog(@"createWebViewWithConfiguration");
    CGRect rect = self.view.bounds;
    self.webView = [[WKWebView alloc] initWithFrame:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height-100) configuration:configuration];
    [self.view addSubview:self.webView];
    return webView;
    
}

#pragma mark - WKUIDelegate 对话框
//js的对话框是不能在iOS中显示的,要想显示这些对话框就需要调用iOS中的相应方法。
//警告框
-(void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    
    NSLog(@"runJavaScriptAlertPanelWithMessage");
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"AlertTitle" message:message preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
        completionHandler();
    }]];
    [self presentViewController:alertController animated:YES completion:nil];
}

//确认框
-(void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:( WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler {
    
    NSLog(@"runJavaScriptConfirmPanelWithMessage");
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"AlertTitle" message:message preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
        completionHandler(YES);
    }]];
    [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action){
        completionHandler(NO);
    }]];
    [self presentViewController:alertController animated:YES completion:nil];
}

//输入框
-(void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:( void (^)(NSString * _Nullable))completionHandler {

    NSLog(@"runJavaScriptTextInputPanelWithPrompt");
    completionHandler(@"Client Not handler");
}

@end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值