sagezhong

iOS-用WKWebView和UIWebView把博客装进APP
最近帮了一个舍友将他写的WebAPP整进iOSAPP里面去,做法也很简单,直接一个UIWebView或者WKWeb...
扫描右侧二维码阅读全文
28
2018/07

iOS-用WKWebView和UIWebView把博客装进APP

最近帮了一个舍友将他写的WebAPP整进iOSAPP里面去,做法也很简单,直接一个UIWebView或者WKWebView就行了,然后再处理好缓存。所以我突然想把博客和博客的后台也整合进APP里,遇到了一点小问题,记录一下,方便以后开发遇到相同的问题

WKWebView和UIWebView

虽然直接用手机浏览器就能够查看博客和登陆博客后台了- -,好像有点多此一举。但是还是觉得做成APP的话,放在桌面直接点开就能够看了。。。

因为学习iOS的时间也不是很长,之前虽然有偶尔了解过Hybrid开发,但是一直都是在Native开发。之前只知道iOS有UIWebView这个UI控件可以用来解析Web和展示,这个控件是从iOS3.0之后就有的了,也是长期以来许多Hybrid型APP最为依赖的控件,但是自从iOS8.0以后,苹果又推出了新的WebKit,其中包括了14个类和三个协议。当然这是刚推出的时候,最近的几个系统版本WKWebView一直有在更新。

WebKit

相比于UIWebView,WKWebView有下面的优点

  • 占用更少的内存,个人使用过后内存占用真的低了很多
  • 与Safari浏览器相同的JS引擎
  • 支持了更多的HTML5特性
  • 支持手势返回
  • 页面的fps能达到了60,接近Native

iOS8开始苹果官方就推荐开发者不再使用UIWebView和WebView去加载Web页面,如果APP最低支持到iOS8就应该全面的使用WKWebView去加载Web页面以及进行交互。

WKWebView

WKWebView主要涉及到下面的类或者协议

加载视图

  • WKWebView

配置和偏好

  • WKWebViewConfiguration
  • WKPreferences

页面导航管理

  • WKNavigationDelegate
  • WKNavigation
  • WKNavigationAction
  • WKNavigationResponse
  • WKBackForwardList
  • WKBackForwardListItem

用户界面

  • WKUIDelegate
  • WKWindowFeatures

脚本注入

  • WKUserContentController
  • WKScriptMessage
  • WKUserScript

缓存数据和持久化

  • WKWebsiteDataStore
  • WKHTTPCookieStore
  • WKHTTPCookieStoreObserver
  • WKWebsiteDataRecord

处理WebKit无法处理的URL Scheme类型的资源

  • WKURLSchemeHandler
  • WKURLSchemeTask

页面结构

  • WKFrameInfo
  • WKSecurityOrigin

内容拦截

  • WKContentRuleList
  • WKContentRuleListStore

3D-Touch预览

  • WKPreviewElementInfo
  • WKPreviewActionItem
  • UIPreviewActionItem

Web的简单使用

WKWebView

WKWebView主要用来创建一个展示Web内容的View。

  • 使用 initWithFrame:configuration: 方法来创建WKWebView对象
  • 使用 loadHTMLString:baseURL: 方法来加载本地HTML文件
  • 使用 loadRequest: 方法来加载web内容
  • 使用 stopLoading 方法来停止加载
  • 使用 loading 属性来判断网页是否正在加载中
  • 使用 WKUIDelegate 协议来跟踪网页内容的加载过程

直接上代码

    WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://sagezhong.top"]]];
    self.webView = webView;
    [self.view addSubview:self.webView];

这样在Controller中就会开始加载一个WKWebView,这个使用方法和UIWebView类似

但是这样直接加载的出来的WKWebView会出现很多问题

WKWebView的一些简单使用细节

手势问题

在UIWebView中实现手势操作一般都是给WebView之注册一个手势然后再用自带的goback方法回到上一级的页面

    UISwipeGestureRecognizer *swip = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(back)] ;
    [self.webView addGestureRecognizer:swip];

// 实现back方法
{
    [self.webView goback];
}

这样能实现手势返回操作,但是体验感很不好,WKWebView对这个做出了很好的优化,自带有手势操作,而且体验感和Safari浏览器一样。

当我知道WKWebView自带有手势操作之后,马上着手试了一下,结果发现并不能使用手势,后来翻了一下官方的文档,发现WKWebView对象有allowsBackForwardNavigationGestures 这样的一个属性,字面意思就是允许导航手势,而且默认值是NO,所以把这个设置为YES即可,所以一定要仔细阅读官方的文档啊!设置为之后你会发现浏览起网页来和Safari体验感一样了,这点确实很好。

处理_blank跳转的问题

当有一个链接为_blank时,如果是浏览器的话会重现点开一个标签页去渲染这个页面,如果是UIWebView能打开这个链接的网址。但是,在WKWebView中,点击_blank标签的链接会没有反应。

如果是UIWebView只有一个页面是没有问题的,但是WKWebView中提供了新的机制。WKWebView 的 WKNavigationDelegate 有一个方法。

(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

用户点击网页上的链接,需要打开新页面时,将先调用这个方法。这个方法的参数 WKNavigationAction 中有两个属性:sourceFrame和targetFrame,分别代表这个action的出处和目标。类型是 WKFrameInfo 。WKFrameInfo有一个 mainFrame 的属性,正是这个属性标记着这个frame是在主frame里还是新开一个frame。

如果 targetFrame的mainFrame 属性为NO,表明这个 WKNavigationAction 将会新开一个页面。

这个时候WKWebView会调用

- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;

这个方法返回的是一个WKWebView对象,使用这个方法首先要设置UIDelegate代理,这时候新的Web将会在你返回的WKWebView中渲染出来,需要注意的是不能是同一个WKWebView对象。

但是这样要重新写一个WKWebView对象显得很麻烦,所以可以使用下面这种方法来达到效果

- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
WKFrameInfo *frameInfo = navigationAction.targetFrame;
if (![frameInfo isMainFrame]) {
[webView loadRequest:navigationAction.request];
}
return nil;
}

简单来说就是判断targetFrame是不是mainFrame,当点击_blank标签链接的时候,此时的targetFrame不是mainFrame,然后给WKWebView重新加载新的请求,这样处理就是在同一个WKWebView上了。

注意的是要给WKWebView设置WKUIDelegate和WKNavigationDelegate

缓存问题

为了提高用户的体验感,需要注意Web的缓存问题,UIWebView的缓存问题比较容易解决,WKWebView的缓存机制有一些不一样,在网上查找了一下,分享一下简单的办法。

首先使用cocoapods引入第三方的库JWNetAutoCache,直接在Podfile中加入

pod `JWNetAutoCache`

在执行pod install即可

然后使用NSURLProtocol的分类NSURLProtocol+WKWebVIew

头文件如下

#import <Foundation/Foundation.h>

@interface NSURLProtocol (WKWebVIew)

+ (void)wk_registerScheme:(NSString*)scheme;

+ (void)wk_unregisterScheme:(NSString*)scheme;


@end

实现文件如下

#import "NSURLProtocol+WKWebVIew.h"
#import <WebKit/WebKit.h>
//FOUNDATION_STATIC_INLINE 属于属于runtime范畴,你的.m文件需要频繁调用一个函数,可以用static inline来声明。从SDWebImage从get到的。
FOUNDATION_STATIC_INLINE Class ContextControllerClass() {
    static Class cls;
    if (!cls) {
        cls = [[[WKWebView new] valueForKey:@"browsingContextController"] class];
    }
    return cls;
}

FOUNDATION_STATIC_INLINE SEL RegisterSchemeSelector() {
    return NSSelectorFromString(@"registerSchemeForCustomProtocol:");
}

FOUNDATION_STATIC_INLINE SEL UnregisterSchemeSelector() {
    return NSSelectorFromString(@"unregisterSchemeForCustomProtocol:");
}

@implementation NSURLProtocol (WebKitSupport)

+ (void)wk_registerScheme:(NSString *)scheme {
    Class cls = ContextControllerClass();
    SEL sel = RegisterSchemeSelector();
    if ([(id)cls respondsToSelector:sel]) {
    // 放弃编辑器警告
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [(id)cls performSelector:sel withObject:scheme];
#pragma clang diagnostic pop
    }
}

+ (void)wk_unregisterScheme:(NSString *)scheme {
    Class cls = ContextControllerClass();
    SEL sel = UnregisterSchemeSelector();
    if ([(id)cls respondsToSelector:sel]) {
     // 放弃编辑器警告
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [(id)cls performSelector:sel withObject:scheme];
#pragma clang diagnostic pop
    }
}

@end

这个分类出自github一个demo

然后在ViewDidload中执行

    [JWCacheURLProtocol startListeningNetWorking];
    [NSURLProtocol wk_registerScheme:@"http"];
    [NSURLProtocol wk_registerScheme:@"https"];

View消失之后记得执行停止监听

     [JWCacheURLProtocol cancelListeningNetWorking];

处理好缓存问题后就算没有网络也能加载本地的缓存进行浏览,提高了用户的体验感。

最终的效果

主页

后台

Last modification:April 28th, 2019 at 12:53 pm
请随意赞赏

One comment

  1. ygfxobjwom

    ?情感共鸣类?

Leave a Comment Cancel reply