macOSWKWebViewios开发学习

WKWebView

2017-04-17  本文已影响925人  独木舟的木

最后更新:20180511

[TOC]

目录

WKWebView API

WKWebView 对象可以显示交互式Web内容,例如应用内浏览器。你可以使用 WKWebView 类将Web内容嵌入到你的应用程序中。 为此,创建一个 WKWebView 对象,像设置视图一样设置它,并向其发送加载Web内容的请求。

预览

重要

从 iOS 8.0 和 OS X 10.10 开始,在你的APP中使用 WKWebView 添加网页内容,不要使用 UIWebViewWebView

使用 initWithFrame:configuration: 方法创建了一个新的 WKWebView 对象之后,你需要加载web内容。使用 loadHTMLString:baseURL: 方法加载本地 HTML 文件或者使用 loadRequest: 方法开始加载 web 内容。使用 stopLoading 方法停止加载,并且使用 loading 属性查看 web 视图是否正在加载中。为对象设置委托属性以遵守 WKUIDelegate 协议,以跟踪Web内容的加载。

要允许用户在 web 历史页面中前进或者后退,为按钮设置使用 goBackgoForward方法的动作。当用户不能在某个方向上再移动时,使用canGoBackcanGoForward属性禁用按钮。

默认情况下,web 视图会自动将出现在 web 内容中的电话号码转换为电话链接。当电话链接被点击时,电话应用程序就会启动并拨打该号码。要关闭这个默认的行为,用 WKDataDetectorTypes 设置 dataDetectorTypes 属性以不包含 WKDataDetectorTypePhoneNumber 标志。

你还可以使用 setMagnification:centeredAtPoint: 以编程方式设置Web内容第一次在Web视图中显示的缩放比例。 此后,用户可以使用手势来改变比例。

Symbols


初始化web视图


查看web信息


设置委托


加载内容


缩放内容


导航


执行 JavaScript


实例方法


WKWebViewConfiguration API

用于初始化Web视图的属性集合。

预览

使用 WKWebViewConfiguration 类,你可以确定网页呈现的速度,媒体播放的处理方式,用户可以选择的项目的粒度等等。

WKWebViewConfiguration 仅在首次初始化Web视图时使用。 当web视图被创建以后,你就无法再使用此类来更改Web视图的配置信息了。

Symbols

配置新的Web视图的属性


确定页面可扩展性


设置渲染首选项


设置媒体播放首选项


设置选择粒度


选择用户界面方向


识别数据类型

WKNavigationDelegate API

WKNavigationDelegate 协议方法可以帮助你实现在Web视图接受,加载和完成导航请求的过程中触发的自定义行为。

初始化导航和跟踪加载进度

//  页面开始加载web内容时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;

//  当web内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;

//  页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;

//  页面加载失败时调用 ( 【web视图加载内容时】发生错误)
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error;

// 【web视图导航过程中发生错误】时调用。
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error;

// 当Web视图的Web内容进程终止时调用。
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView;

响应服务器操作

// 收到服务器重定向之后调用 (接收到服务器跳转请求)
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;

实例方法

当 Web 视图需要响应认证挑战(authentication challenge)时调用。

当使用 Https 协议加载web内容时,使用的证书不合法或者证书过期时需要使用该方法:

- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        if ([challenge previousFailureCount] == 0) {
            NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
        } else {
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
        }
    } else {
        completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
    }
}

允许导航

// 通常用于处理跨域的链接能否导航

// 在发送请求之前,决定是否允许或取消导航。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSLog(@"%s", __FUNCTION__);
    // 如果请求的是百度地址,则允许跳转
    if ([navigationAction.request.URL.host.lowercaseString isEqual:@"www.baidu.com"]) {
        // 允许跳转
        decisionHandler(WKNavigationActionPolicyAllow);
        return;
    }
    // 否则不允许跳转
    decisionHandler(WKNavigationActionPolicyCancel);
}

// 收到响应后,决定是否允许或取消导航。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
    NSLog(@"%s", __FUNCTION__);
    // 如果响应的地址是百度,则允许跳转
    if ([navigationResponse.response.URL.host.lowercaseString isEqual:@"www.baidu.com"]) {
        // 允许跳转
        decisionHandler(WKNavigationResponsePolicyAllow);
        return;
    }
    // 否则不允许跳转
    decisionHandler(WKNavigationResponsePolicyCancel);
}

导航策略

WKUIDelegate API

WKUIDelegate 类是网页视图的用户界面委托协议,提供了代表网页呈现本机用户界面元素的方法。

Web视图用户界面委托对象实现此协议来控制打开的新窗口,增加用户单击元素时显示的默认菜单项的行为,并执行其他与用户界面相关的任务。 可以通过处理 JavaScript 或其他插件内容来调用这些方法。 默认Web视图实现假定每个Web视图有一个窗口,因此非常规用户界面可能会实现用户界面委托。

参考:【iOS 开发】WKWebView 学习笔记 (3)-WKUIDelegate

Symbols

创建web视图


显示UI面板


关闭web视图


显示上传面板


响应强制触控动作


遇到的问题:

JavaScript 代码:

当 web 视图中调用了 JavaScript 代码的 alert、confirm、prompt 方法就会触发显示UI面板的三个协议方法:

// 显示一个 JavaScript 警告弹窗
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"AlertPanel" message:message preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction *alertAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        // if do nothing...
    }];
    [alertController addAction:alertAction];
    [self presentViewController:alertController animated:YES completion:nil];
    
}

发生程序崩溃:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Completion handler passed to -[JSViewController webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:] was not called'***

找到的原因一(后来证明是错误的❌):

WKWebview开发笔记 @Qin's Blog :解决办法: 在webView:decidePolicyForNavigationAction:decisionHandler:函数里需执行decisionHandler的block:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    // WKNavigationTypeLinkActivated:由用户激活的具有href属性的链接
    if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
        decisionHandler(WKNavigationActionPolicyAllow);
    }else {
        decisionHandler(WKNavigationActionPolicyCancel);
    }
}

判断了一下 WKNavigationType 属性,然而并没有用:

/*! @enum WKNavigationType
 @abstract The type of action triggering a navigation.
 @constant WKNavigationTypeLinkActivated    A link with an href attribute was activated by the user.
 @constant WKNavigationTypeFormSubmitted    A form was submitted.
 @constant WKNavigationTypeBackForward      An item from the back-forward list was requested.
 @constant WKNavigationTypeReload           The webpage was reloaded.
 @constant WKNavigationTypeFormResubmitted  A form was resubmitted (for example by going back, going forward, or reloading).
 @constant WKNavigationTypeOther            Navigation is taking place for some other reason.
 */
typedef NS_ENUM(NSInteger, WKNavigationType) {
    WKNavigationTypeLinkActivated,
    WKNavigationTypeFormSubmitted,
    WKNavigationTypeBackForward,
    WKNavigationTypeReload,
    WKNavigationTypeFormResubmitted,
    WKNavigationTypeOther = -1,
} API_AVAILABLE(macosx(10.10), ios(8.0));

原因二(✅):

WKWebView completionHandler called before dismissal @stackoverflow:

原因是 UIAlertViewcompletionHandler() 不能同时被触发:

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"AlertPanel" message:message preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction *alertAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        
        // ⚠️必须在这里回调 JavaScript 
        completionHandler();
        
    }];
    [alertController addAction:alertAction];
    [self presentViewController:alertController animated:YES completion:nil];
    
}

WKScriptMessageHandler API

遵守 WKScriptMessageHandler 协议的类,提供了从网页中运行 JavaScript 接收消息的方法。

// 从 web 界面中接收到一个脚本时调用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;

这个协议只包含以上一个必须实现的方法,这个方法是提高App与web端交互的关键,它可以直接将接收到的JS脚本转为 OC 或 Swift 对象。(当然,在 UIWebView 也可以通过“曲线救国”的方式与web进行交互,著名的Cordova 框架就是这种机制)

——WKWebView的新特性与使用 @saitjr

这个 API 真正神奇的地方在于 JavaScript 对象可以自动转换为 Objective-C 或 Swift 对象。

——WKWebView @nshipster

在 JavaScript 端通过 window.webkit.messageHandlers.{InjectedName}.postMessage() 方法来发送消息到native。我们需要遵守该协议,然后实现代理方法,就可以收到消息,并做相应的的处理。

如果在开始时就注入很多的名称,就需要区分处理

if (message.name == "AppModel" ) {

//...

}

——WKWebView特性及使用 @360doc 【Swift】

WKUserScript API

WKUserScript 对象表示可以注入到网页中的脚本。

Symbols

初始化脚本


检查脚本信息


常量

typedef enum WKUserScriptInjectionTime : NSInteger {
    WKUserScriptInjectionTimeAtDocumentStart, // 在文档元素创建之后,但在加载任何其他内容之前注入脚本。
    WKUserScriptInjectionTimeAtDocumentEnd //在文件完成加载后,但在其他子资源完成加载之前注入该脚本。
} WKUserScriptInjectionTime;

使用示例

WKUserScript 对象可以以 JavaScript 源码形式初始化,初始化时还可以传入时间是在加载之前还是结束时注入,以及脚本影响的是这个布局还是仅主要布局。于是用户脚本被加入到 WKUserContentController 中,并且以 WKWebViewConfiguration 属性传入到 WKWebView 的初始化过程中。

- (void)viewDidLoad {
    [super viewDidLoad];
  
    // 使用 WKUserScript 注入JavaScript脚本
    // 图片缩放的js代码
    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 + '张图');";
    
    // 根据JS字符串初始化 WKUserScript 对象
    WKUserScript *script = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
    
    // 根据生成的 WKUserScript 对象,初始化 WKWebViewConfiguration 对象
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    [config.userContentController addUserScript:script];
    
    // 初始化 WKWebView 对象
    _webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
    _webView.UIDelegate = self;
    _webView.navigationDelegate = self;
    [_webView loadHTMLString:@"<head></head>![](http:https://img.haomeiwen.com/i2648731/54ae5490e32407cf.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)" baseURL:nil];
    [self.view addSubview:_webView];
    
}

使用 WKWebView

WKWebView 新特性

加载 web 页面

加载本地资源

//设定主页文件的基本路径,通过一个HTML字符串加载主页数据
- (IBAction)loadHTMLString:(id)sender {
    
    //主页文件名
    NSString *htmlPath = [[NSBundle mainBundle] pathForResource:@"Baidu001"
                 ofType:@"html"];
    //主页文件的基本路径
    NSURL *bundleUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
    
    //将 index.html 文件的内容读取到 NSString 对象中
    NSError *error = nil;  
    NSString *html = [[NSString alloc] initWithContentsOfFile:htmlPath
                                                     encoding:NSUTF8StringEncoding
                                                        error:&error];
    //数据加载没有错误的情况下
    if (error == nil) {
        [self.webView loadHTMLString:html baseURL:bundleUrl];
    } 
}
//NSData是一种二进制的字节数组类型。它没有字符集的概念,但用它来装载webView的时候必须指定字符集。
- (IBAction)loadDATA:(id)sender {
    
    NSString *htmlPath = [[NSBundle mainBundle] pathForResource:@"index"
                 ofType:@"html"];
    NSData *htmlData = [[NSData alloc] initWithContentsOfFile:htmlPath];
  
    NSURL *bundleUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
    
    [self.webView loadData:htmlData
                  MIMEType:@"text/html"
          textEncodingName:@"UTF-8"
                   baseURL:bundleUrl];
    
}
  1. 加载本地HTML:iOS9以上的系统可以使用 WKWebView loadFileURL:allowingReadAccessToURL:,iOS9以下的版本没有这个方法,需要先将本地HTML文件的复制到tmp目录中,然后使用 loadRequest: 方法来加载。如果在HTML中引入其他资源文件,例如js,css,image等必须一同复制到temp目录中

    ——WKWebview开发笔记 @Qin's Blog

加载网络资源

// 创建 WKWebView 对象
CGRect rect = CGRectMake(0, 0, self.view.width, self.view.height);
WKWebView *webView = [[WKWebView alloc] initWithFrame:rect];
// 设置导航代理,监听网页加载进程
_webView.navigationDelegate = self;
// 将 webView 对象添加到视图
[self.view addSubview:webView];
// 根据URL发起网络请求
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"www.google.com"]];
[webView loadRequest:request];

创建可与 JavaScript 交互的 WKWebView

参考:WKWebView的使用和各种坑的解决方法(OC+Swift)

- (WKWebViewConfiguration *)webViewConfiguration {
    if (!_webViewConfiguration) {
        
        // 创建 WKWebViewConfiguration 对象
        _webViewConfiguration = [[WKWebViewConfiguration alloc] init];
        
        // 创建 WKUserContentController 对象,提供 JavaScript 向 webView 发送消息的方法
        WKUserContentController *userContentColtroller = [[WKUserContentController alloc] init];
        // 添加消息处理,注意:self指代的对象需要遵守 WKScriptMessageHandler 协议,结束时需要移除
        [userContentColtroller addScriptMessageHandler:self name:@"close"];
        // 将 userConttentController 设置到配置文件
        _webViewConfiguration.userContentController = userContentColtroller;
        
    }
    return _webViewConfiguration;
}

ScriptMessageHandler 的 name 必须和 web 中的 JavaScript 方法名称一致:

window.webkit.messageHandlers.close.postMessage("message");

<script type="text/javascript">

    function doPrint() {
        if (checkiOS() == true) {
            // iOS 注入代码
            window.webkit.messageHandlers.scriptName.postMessage("script message!");
        } else {
            // Android 注入代码
            Android.startLHYL();
        }
    }

    // Android/iOS设备判断
    function checkiOS() {

        var u = navigator.userAgent;

        var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; // Android
        var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);             // iOS

        if (isiOS) {
            return true;
        } else {
            return false;
        }
    }

</script>
#pragma mark - WKScriptMessageHandler

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {

    NSLog(@"%s",__FUNCTION__);
    
    if ([message.name isEqualToString:@"close"]) {
        
        NSLog(@"WKScriptMessage:%@",message.body);
    }
}
- (void)dealloc { 
  
    // 移除 ScriptMessageHandler
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"close"];
}

显示加载进度条

对于 WKWebView ,有三个属性支持 KVO,因此我们可以监听其值的变化,分别是:loadingtitleestimatedProgress,对应功能表示为:是否正在加载中、页面的标题、页面内容加载进度(值为0.0~1.0)

刚开始用的方法:WKWebView添加进度条 ,参照着写完发现如果网速好的话,会出现进度条走不完就被隐藏的现象。
解决方法:添加延时animation动画,参考:iOS WKWebView添加网页加载进度条

代码如下:

//
//  OSCViewController.m
//  2015-01-31-WKWebViewDemo
//
//  Created by TangJR on 15/1/31.
//  Copyright (c) 2015年 tangjr. All rights reserved.
//

#import "OSCViewController.h"
#import <WebKit/WebKit.h>

@interface OSCViewController () <WKNavigationDelegate>

@property (nonatomic, strong) NSURL *URL;
@property (nonatomic, strong) WKWebViewConfiguration *webViewConfiguration;
@property (nonatomic, strong) WKWebView *webView;

/** 1️⃣ 添加 progressView 属性*/
@property (nonatomic, strong) UIProgressView *progressView;

@end

@implementation OSCViewController

#pragma mark - Lifecycle

- (void)loadView {
    [super loadView];
    self.view = self.webView;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // 3️⃣ 将 progressView 添加到父视图
    [self.view addSubview:self.progressView];

    // 4️⃣ 使用 KVO 注册观察者
    // 监听 WKWebView 对象的 estimatedProgress 属性,就是当前网页加载的进度
    [self.webView addObserver:self
                   forKeyPath:@"estimatedProgress"
                      options:NSKeyValueObservingOptionNew
                      context:nil];
    
    NSURLRequest *request = [NSURLRequest requestWithURL:self.URL];
    [self.webView loadRequest:request];
}

- (void)dealloc {
    // 6️⃣ 移除观察者
    [self.view removeObserver:self forKeyPath:@"estimatedProgress"];

}
#pragma mark - Custom Accessors

- (NSURL *)URL {
    if (!_URL) {
        _URL = [NSURL URLWithString:@"http://www.oschina.net/"];
    }
    return _URL;
}

- (WKWebViewConfiguration *)webViewConfiguration {
    if (!_webViewConfiguration) {
        _webViewConfiguration = [[WKWebViewConfiguration alloc] init];
    }
    return _webViewConfiguration;
}

- (WKWebView *)webView {
    if (!_webView) {
        _webView = [[WKWebView alloc] initWithFrame:[[UIScreen mainScreen] bounds] configuration:self.webViewConfiguration];
        // 设置导航代理,监听网页加载进程
        _webView.navigationDelegate = self;
    }
    return _webView;
}

/**
 2️⃣ 初始化progressView

 @return 返回初始化后的进度条视图
 */
- (UIProgressView *)progressView {
    if (!_progressView) {
        CGRect sreenBounds = [[UIScreen mainScreen] bounds];
        CGRect progressViewFrame = CGRectMake(0, 64, sreenBounds.size.width, 1);
        _progressView = [[UIProgressView alloc] initWithFrame:progressViewFrame];
        // 设置进度条色调
        _progressView.tintColor = [UIColor blueColor];
        // 设置进度条跟踪色调
        _progressView.trackTintColor = [UIColor whiteColor];
    }
    return _progressView;
}

#pragma mark - KVO

// 5️⃣ 接收变更后的通知,计算 webView 的进度条
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(void *)context {
    
    if ([keyPath isEqualToString:@"estimatedProgress"]) {

        self.progressView.progress = self.webView.estimatedProgress;
        
        if (self.progressView.progress == 1) {
            /*
             * 添加一个简单的动画,将 progressView 的 Height 变为1.5倍
             * 动画时长0.25s,延时0.3s后开始动画
             * 动画结束后将 progressView 隐藏
             */
            __weak __typeof(self)weakSelf = self;
            [UIView animateWithDuration:0.25f delay:0.3f options:UIViewAnimationOptionCurveEaseInOut animations:^{
                weakSelf.progressView.transform = CGAffineTransformMakeScale(1.0f, 1.5f);
            } completion:^(BOOL finished) {
                weakSelf.progressView.hidden = YES;
            }];
        }
        
    }else {
        [super observeValueForKeyPath:keyPath
                             ofObject:object
                               change:change
                              context:context];
    }
}

#pragma mark - WKNavigationDelegate

//  页面开始加载web内容时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
    self.progressView.hidden = NO;
    // 防止 progressView 被网页挡住
    [self.view bringSubviewToFront:self.progressView];
}

//  当web内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
    
}

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

//  页面加载失败时调用 ( 【web视图加载内容时】发生错误)
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    NSLog(@"Error:%@",error.localizedDescription);
    self.progressView.hidden = YES;
}

// 【web视图导航过程中发生错误】时调用。
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    NSLog(@"Error:%@",error.localizedDescription);
    self.progressView.hidden = YES;
}

// 当Web视图的Web内容进程终止时调用。
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
    self.progressView.hidden = YES;
}

@end

监听webView的 title 属性并设置标题

- (void)viewDidLoad {
    [super viewDidLoad];
        
    // ------ 监听 WKWebView 对象的 title 属性
    [self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];
    
    NSURLRequest *request = [NSURLRequest requestWithURL:self.URL];
    [self.webView loadRequest:request];
}

- (void)dealloc {
    // ------ 移除观察者
    [self.view removeObserver:self forKeyPath:@"title"];
}

#pragma mark - KVO

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(void *)context {
    
     if ([keyPath isEqualToString:@"title"]) {
        self.title = change[@"new"];
    }else {
        [super observeValueForKeyPath:keyPath
                             ofObject:object
                               change:change
                              context:context];
    }
}

URL 中文处理

// URL 中文处理
- (NSURL *)URL {
    if (!_URL) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
        _URL = [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)@"https://www.oschina.net/ios/home", (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))];
#pragma clang diagnostic pop
    }
    return _URL;
}

导航栏右侧添加刷新按钮

#pragma mark - Lifecycle

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 刷新按钮
    UIBarButtonItem *rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(rightBarButtonDidClicked:)];
    self.navigationItem.rightBarButtonItem = rightBarButtonItem;
}

#pragma mark - IBActions

- (void)rightBarButtonDidClicked:(id)sender {
    [self.webView reload];
}

其他

加载HTML图片大小自适应

参考 WebView加载HTML图片大小自适应与文章自动换行

在 HTML 代码中设置内容样式,一般使用 css 或者 js ,根据加载优先级以及加载效果,可以自行选择。

一:使用css进行图片的自适应

#define HTML_NO_HEAD @"![](http:https://img.haomeiwen.com/i2648731/a7ddbb6f227eb44d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)<br/>![](http:https://img.haomeiwen.com/i2648731/f4855a77d3daa14f.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)<br/>"

#define HTML_HAD_HEAD @"<head></head><body>![](http:https://img.haomeiwen.com/i2648731/a7ddbb6f227eb44d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)<br/>![](http:https://img.haomeiwen.com/i2648731/f4855a77d3daa14f.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)<br/></body>"

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 没有<head>标签
//    [self.webView loadHTMLString:[self reSizeImageWithHTMLNoHead:HTML_NO_HEAD] baseURL:nil];
    
    // 有<head>标签
    [self.webView loadHTMLString:[self reSizeImageWithHTMLHadHead:HTML_HAD_HEAD] baseURL:nil];
}

/   ----------------------------------------------------------------
// 1.图片样式:不管用户以前设置的图片尺寸是多大,都缩放到宽度为320px大小。

// 如果后台返回的HTML代码中,不包含 <head> 标签,则可以直接在HTML字符串前拼接代码。
- (NSString *)reSizeImageWithHTMLNoHead:(NSString *)html {
    
    return [NSString stringWithFormat:@"<head><style>img{width:320px !important;}</style></head>%@", html];
}

// 如果包含 <head> 标签,则在<head>标签内部替换添加
- (NSString *)reSizeImageWithHTMLHadHead:(NSString *)html {
    
    return [HTML_HAD_HEAD stringByReplacingOccurrencesOfString:@"<head>" withString:@"<head><style>img{width:320px !important;}</style>"];
}

/   ----------------------------------------------------------------
// 2.图片样式:若需要根据图片原本大小,宽度小于320px的不缩放,大于320px的缩小到320px,那么在HTML字符串前加上一下代码:
- (NSString *)reSizeImageWithHTML:(NSString *)html {
    
    return [NSString stringWithFormat:@"<head><style>img{max-width:320px !important;}</style></head>%@", html];
}

二:使用js进行图片的自适应

在webview的代理中,执行js代码。(下面这段代码是必须有<head>标签的)

如果没有<head>标签,也很简单,只需要给返回的HTML字符串前面拼接一个<head></head>即可。

- (void)webViewDidFinishLoad:(UIWebView *)webView
 {
    [webView stringByEvaluatingJavaScriptFromString:
     @"var script = document.createElement('script');"
     "script.type = 'text/javascript';"
     "script.text = \"function ResizeImages() { "
         "var myimg,oldwidth,oldheight;"
         "var maxwidth=320;"// 图片宽度
         "for(i=0;i <document.images.length;i++){" "myimg = document.images[i];" "if(myimg.width > maxwidth){"
                 "myimg.width = maxwidth;"
             "}"
         "}"
     "}\";"
     "document.getElementsByTagName('head')[0].appendChild(script);"];
    [webView stringByEvaluatingJavaScriptFromString:@"ResizeImages();"];
}

文章内容自动换行

文章的自动换行也是通过css实现的,书写方式图片缩放类似。在没有<body>标签的情况下,在HTML代码前,直接拼接以下代码即可(若包含<body>,则将代码添加到body标签内部),意思是全部内容自动换行。

<body width=320px style=\"word-wrap:break-word; font-family:Arial\">

参考

上一篇 下一篇

猜你喜欢

热点阅读