当前位置: 代码迷 >> 综合 >> UIWebView 替换 WKWebView
  详细解决方案

UIWebView 替换 WKWebView

热度:87   发布时间:2024-01-31 10:26:59.0

已经快要到苹果规定更新包不允许使用UIWebView的最后期限了,今天刚把项目中的UIWebView替换完成,记录一下具体遇到的问题

目录

  • 一:基本使用
  • 二:替换
    • 1、scalesPageToFit 属性替换:
    • 2、UIWebView 和WKWebView对应代理方法
      • 2.1、shouldStartLoadWithRequest
      • 2.2、开始加载
      • 2.3、加载成功
      • 2.4、加载失败
    • 3、Native和JS互相调用:
      • 3.1、Native调用js
      • 3.2、js调用Native
      • 4、左滑返回上一步

一:基本使用

首先说下基本用法:
UIWebView:

UIWebView *webView = [[UIWebView alloc] init];
NSURL *url = [NSURL URLWithString:@"https://www.hujiang.com"];
[webView loadRequest:[NSURLRequest requestWithURL:url]];

WKWebView:

#import <WebKit/WebKit.h>WKUserContentController *wkUController = [[WKUserContentController alloc] init];
WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init];
wkWebConfig.userContentController = wkUController;WKWebView* mainWebView = [[WKWebView alloc] initWithFrame:self.view.frameconfiguration:wkWebConfig];
mainWebView.navigationDelegate = self;
mainWebView.backgroundColor = [UIColor clearColor];

二:替换

1、scalesPageToFit 属性替换:

在WKWebView中不支持scalesPageToFit属性,可以通过注入js的方法实现:

NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta); var imgs = document.getElementsByTagName('img');for (var i in imgs){imgs[i].style.maxWidth='100%';imgs[i].style.height='auto';}";
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];WKUserContentController *wkUController = [[WKUserContentController alloc] init];
[wkUController addUserScript:wkUScript];

2、UIWebView 和WKWebView对应代理方法

@protocol WKNavigationDelegate; //类似于UIWebView的加载成功、失败、是否允许跳转等
@protocol WKUIDelegate; //主要是一些alert、打开新窗口之类的

2.1、shouldStartLoadWithRequest

UIWebView

(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {return YES;
}

WKWebView

//先:针对一次action来决定是否允许跳转,action中可以获取request
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{//允许与否都需要调用decisionHandler,如:decisionHandler(WKNavigationActionPolicyAllow);
}
//后:根据response来决定,是否允许跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{//允许与否都需要调用decisionHandler,如:decisionHandler(WKNavigationActionPolicyCancle);
}

2.2、开始加载

UIWebView

- (void)webViewDidStartLoad:(UIWebView *)webView;

WKWebView

- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation;

2.3、加载成功

UIWebView:

- (void)webViewDidFinishLoad:(UIWebView *)webView;

WKWebView:

- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation;

2.4、加载失败

UIWebView:

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;

WKWebView:

- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;

3、Native和JS互相调用:

3.1、Native调用js

UIWebView:

[weiview stringByEvaluatingJavaScriptFromString:jsString];

WKWebView:

[wkWebView evaluateJavaScript:@"jsFunction"completionHandler:^(id _Nullable response, NSError * _Nullable error) {NSString *title = response;
}];

3.2、js调用Native

UIWebView:

//第一种方法,简单
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;//第二种:通过拦截URL的方式:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{NSURL *URL = request.URL;    if ([URL.scheme isEqualToString:@"urlscheme"]) {if ([URL.host isEqualToString:@"jsFunction"]) {NSLog(@"拦截执行js中的方法: %@", URL.query);return NO;}}return YES;
}

WKWebView:
第一种方式:


WKUserContentController *userContent = [[WKUserContentController alloc] init];
[userContent addScriptMessageHandler:id<WKScriptMessageHandler> name:@"jsFunction"];WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = userContent;
WKWebView *wkWebView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];在代理方法中截取:
-  (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {if ([message.name isEqualToString:@"jsFunction"]) {NSLog(@"native执行js的方法:%@",message.body);}
}//注意在合适的地方移除messageHandler:
- (void)dealloc {[wkWebView.configuration.userContentController removeScriptMessageHandlerForName:@"jsFunction"];
}

第二种方式:URL拦截

缺点:无法直接获取本次交互的返回值,比较适合单向传参,且不关心回调的情景,比如h5页面跳转到native页面等

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {//可以通过navigationAction.navigationType获取跳转类型,如新链接、后退等NSURL *URL = navigationAction.request.URL;//判断URL是否符合自定义的URL Schemeif ([URL.scheme isEqualToString:@"urlscheme"]) {//根据不同的业务,来执行对应的操作,且获取参数if ([URL.host isEqualToString:@"jsFunction"]) {decisionHandler(WKNavigationActionPolicyCancel);return;}}decisionHandler(WKNavigationActionPolicyAllow);
}

4、左滑返回上一步

第一种实现:

//事实证明并不好用,请看第二种方式
self.webview.allowsBackForwardNavigationGestures = true;

第二种实现: 创建DL_PanableWebViewHandler分类

DL_PanableWebViewHandler.h

#import <WebKit/WebKit.h>NS_ASSUME_NONNULL_BEGIN@class DL_PanableWebView;@protocol DL_PanableWebViewHandler <NSObject>@optional- (void)DL_PanableWebView:(DL_PanableWebView *)webView panPopGesture:(UIPanGestureRecognizer *)pan;@end@interface DL_PanableWebView : WKWebView@property(nonatomic, weak) id<DL_PanableWebViewHandler> panDelegate;
@property(nonatomic, assign) BOOL enablePanGesture;@end

DL_PanableWebViewHandler.m


#import "DL_PanableWebView.h"@interface DL_PanableWebView()<WKNavigationDelegate,WKUIDelegate>{UIGestureRecognizer* popGesture_;CGFloat panStartX_;NSMutableArray *historyStack_;UIImageView *_historyView;__weak id<WKNavigationDelegate> originDelegate_;
}@end@implementation DL_PanableWebView+ (UIImage *)screenshotOfView:(UIView *)view{UIGraphicsBeginImageContextWithOptions(view.frame.size, YES, 0.0);if ([view respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];}else{[view.layer renderInContext:UIGraphicsGetCurrentContext()];}UIImage *image = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();return image;
}+ (void)addShadowToView:(UIView *)view{CALayer *layer = view.layer;UIBezierPath *path = [UIBezierPath bezierPathWithRect:layer.bounds];layer.shadowPath = path.CGPath;layer.shadowColor = [UIColor blackColor].CGColor;layer.shadowOffset = CGSizeZero;layer.shadowOpacity = 0.4f;layer.shadowRadius = 8.0f;
}- (void)setDelegate:(id<WKNavigationDelegate>)delegate{originDelegate_ = delegate;
}- (id<WKNavigationDelegate>)delegate{return originDelegate_;
}-(WKNavigation *)goBack{[historyStack_ removeLastObject];return [super goBack];
}
- (void)setEnablePanGesture:(BOOL)enablePanGesture{popGesture_.enabled = enablePanGesture;
}- (BOOL)enablePanGesture{return popGesture_.enabled;
}- (UIImageView *)historyView{if (!_historyView) {if (self.superview) {_historyView = [[UIImageView alloc] initWithFrame:self.bounds];[self.superview insertSubview:_historyView belowSubview:self];}}return _historyView;
}
- (id)init{if (self = [super init]) {[self commonInit];}return self;
}- (id)initWithCoder:(NSCoder *)aDecoder{if (self = [super initWithCoder:aDecoder]) {[self commonInit];}return self;
}- (id)initWithFrame:(CGRect)frame{if (self = [super initWithFrame:frame]) {[self commonInit];}return self;
}- (void)commonInit{historyStack_ = [NSMutableArray array];popGesture_ = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGesture:)];[self addGestureRecognizer:popGesture_];[super setNavigationDelegate:self];[DL_PanableWebView addShadowToView:self];
}- (void)dealloc{if (_historyView) {[_historyView removeFromSuperview];_historyView = nil;}
}- (void)layoutSubviews{[super layoutSubviews];[self historyView].frame = self.bounds;
}#pragma mark === gesture===
- (void)panGesture:(UIPanGestureRecognizer *)sender{if (![self canGoBack] || historyStack_.count == 0) {if (self.panDelegate && [self.panDelegate respondsToSelector:@selector(DL_PanableWebView:panPopGesture:)]) {[self.panDelegate DL_PanableWebView:self panPopGesture:sender];}return;}CGPoint point = [sender translationInView:self];if (sender.state == UIGestureRecognizerStateBegan) {panStartX_ = point.x;}else if (sender.state == UIGestureRecognizerStateChanged){CGFloat deltaX = point.x - panStartX_;if (deltaX > 0) {if ([self canGoBack]) {assert([historyStack_ count] > 0);CGRect rc = self.frame;rc.origin.x = deltaX;self.frame = rc;[self historyView].image = [[historyStack_ lastObject] objectForKey:@"preview"];rc.origin.x = -self.bounds.size.width/2.0f + deltaX/2.0f;[self historyView].frame = rc;}}}else if (sender.state == UIGestureRecognizerStateEnded){CGFloat deltaX = point.x - panStartX_;CGFloat duration = .5f;if ([self canGoBack]) {if (deltaX > self.bounds.size.width/4.0f) {[UIView animateWithDuration:(1.0f - deltaX/self.bounds.size.width)*duration animations:^{CGRect rc = self.frame;rc.origin.x = self.bounds.size.width;self.frame = rc;rc.origin.x = 0;[self historyView].frame = rc;} completion:^(BOOL finished) {CGRect rc = self.frame;rc.origin.x = 0;self.frame = rc;[self goBack];[self historyView].image = nil;}];}else{[UIView animateWithDuration:(deltaX/self.bounds.size.width)*duration animations:^{CGRect rc = self.frame;rc.origin.x = 0;self.frame = rc;rc.origin.x = -self.bounds.size.width/2.0f;[self historyView].frame = rc;} completion:^(BOOL finished) {}];}}}
}-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{BOOL ret = YES;if (originDelegate_ && [originDelegate_ respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) {[originDelegate_ webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler];}BOOL isFragmentJump = NO;if (navigationAction.request.URL.fragment) {NSString *nonFragmentURL = [navigationAction.request.URL.absoluteString stringByReplacingOccurrencesOfString:[@"#" stringByAppendingString:navigationAction.request.URL.fragment] withString:@""];if (webView.URL.absoluteString) {NSString *preNonFragmentURL;if (webView.URL.fragment) {preNonFragmentURL = [webView.URL.absoluteString stringByReplacingOccurrencesOfString:[@"#" stringByAppendingString:webView.URL.fragment] withString:@""];}else{preNonFragmentURL = webView.URL.absoluteString;}isFragmentJump = [nonFragmentURL isEqualToString:preNonFragmentURL];}}BOOL isTopLevelNavigation = [navigationAction.request.mainDocumentURL isEqual:navigationAction.request.URL];BOOL isHTTPOrFile = [navigationAction.request.URL.scheme isEqualToString:@"http"] || [navigationAction.request.URL.scheme isEqualToString:@"https"] || [navigationAction.request.URL.scheme isEqualToString:@"file"];if (ret && !isFragmentJump && isHTTPOrFile && isTopLevelNavigation) {if ((navigationAction.navigationType == UIWebViewNavigationTypeLinkClicked || navigationAction.navigationType == UIWebViewNavigationTypeOther) && [[webView.URL description] length]) {if (![[[historyStack_ lastObject] objectForKey:@"url"] isEqualToString:[self.URL description]]) {UIImage *curPreview = [DL_PanableWebView screenshotOfView:self];[historyStack_ addObject:@{@"preview":curPreview, @"url":[self.URL description]}];}}}decisionHandler(WKNavigationActionPolicyAllow);
}-(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{if (originDelegate_ && [originDelegate_ respondsToSelector:@selector(webView:didStartProvisionalNavigation:)]) {[originDelegate_ webView:webView didStartProvisionalNavigation:navigation];}
}-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{if (originDelegate_ && [originDelegate_ respondsToSelector:@selector(webView:didFinishNavigation:)]) {[originDelegate_ webView:webView didFinishNavigation:navigation];}
}-(void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{if (originDelegate_ && [originDelegate_ respondsToSelector:@selector(webView:didFailNavigation:withError:)]) {[originDelegate_ webView:webView didFailNavigation:navigation withError:error];}
}@end

使用:

	//web会自动处理手势DL_PanableWebView *web = [[DL_PanableWebView alloc]init];[self.view addSubview:web];[web loadRequest:[NSURLRequest requestWithURL:weburl]];[web mas_makeConstraints:^(MASConstraintMaker *make) {make.edges.equalTo(self.view);}];

以上是替换项目中的UIWebView时简单的对照,看网上的资料说还会有很多坑,本人暂时没有遇到,如果有踩到坑的同志,请留言,我会更新上相关内容。

  相关解决方案