H5頁面與原生交互項目中經常遇到,今天做個小結。
從iOS原生的角度講,我們可以使用UIWebView、WKWebView組件來展示H5頁面,那么兩者有什么區別呢?
講述UIWebView只是讓大家了解下這個歷史,按現在的情況我們主要使用WKWebView
一、推出時間
UIWebView從iOS2.0推出到iOS12.0廢棄,WKWebView從iOS8.0推出
UIWebView ios(2.0, 12.0) 、WKWebView ios(8.0)
UIWebView
UIKIT_EXTERN API_DEPRECATED("No longer supported; please adopt WKWebView.", ios(2.0, 12.0)) API_UNAVAILABLE(tvos, macos) @interface UIWebView : UIView <NSCoding, UIScrollViewDelegate>
WKWebView
#if TARGET_OS_IPHONE WK_EXTERN API_AVAILABLE(macos(10.10), ios(8.0)) @interface WKWebView : UIView #else WK_EXTERN API_AVAILABLE(macos(10.10), ios(8.0)) @interface WKWebView : NSView #endif
二、性能對比
UIWebView
占用過多內存,且內存峰值更是誇張。說白了就是性能低
WKWebView
1、網頁加載速度快,內存消耗低。
2、更多的支持HTML5的特性,
3、高達60fps的滾動刷新率以及內置手勢,
4、Safari相同的JavaScript引擎(但就這一點性能完勝),
5、增加了進度屬性estimatedProgress等特性
三、交互
UIWebView
1、OC調用JS
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
2、JS調用OC
<1>iOS原生攔截URL
攔截到指定URL做響應操作
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType API_DEPRECATED("No longer supported.", ios(2.0, 12.0));
<2>使用JavaScriptCore實現(要注意循環引用的問題)
JSDelegate
#import <Foundation/Foundation.h> #import <JavaScriptCore/JavaScriptCore.h> /** * H5會通過對象Bridge調用協議中的方法 */ @protocol JSDelegate <JSExport> // 關閉頁面 - (void)finishPage; @end
JSObject(中間對象,解決循環引用的問題,不直接讓WebViewController遵守JSDelegate的原因)
#import <Foundation/Foundation.h> #import "JSDelegate.h" #import "WebViewController.h" @interface JSObject : NSObject <JSDelegate> @property(nonatomic, weak) WebViewController *h5VC; @end
- (void)finishPage { dispatch_async(dispatch_get_main_queue(), ^{ [self.h5VC pop]; }); }
WebViewController
@property (strong, nonatomic) JSContext *jsContext;
// 獲取JSContext,並注入對象 - (void)creatJscontext { // 獲取js上下文(JSContext) self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; // 遵守JSExport協議的對象(符合JSExport的對象都將轉換為JavaScript包裝器對象) JSObject *obj = [[JSObject alloc] init]; obj.h5VC = self; // 注入交互對象Bridge,H5通過Bridge對象調用JSExport中的方法 self.jsContext[@"Bridge"] = obj; }
H5端將通過Bridge.finishPage()調用原生方法
備注:
JSContext
/*! @interface @discussion A JSContext is a JavaScript execution environment. All JavaScript execution takes place within a context, and all JavaScript values are tied to a context. */ JS_EXPORT API_AVAILABLE(macos(10.9), ios(7.0)) @interface JSContext : NSObject
UIWebView什么時機創建JSContext環境
什么時候UIWebView
會創建JSContext環境
,分兩種方式,
第一在渲染網頁時遇到<script標簽
時,就會創建JSContext環境
去運行JavaScript代碼
。
第二就是使用方法[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]
去獲取JSContext環境
時,
這時無論是否遇到<script標簽
,都會去創造出來一個JSContext環境
,而且和遇到<script標簽
再創造環境是同一個。
JSExport
All objects that conform to JSExport convert to JavaScript wrapper objects, even if they subclass classes that would otherwise behave differently. For example, if a subclass of NSString conforms to JSExport, it converts to JavaScript as a wrapper object rather than a JavaScript string. */ @protocol JSExport

/* @abstract Evaluates the given JavaScript string. @param javaScriptString The JavaScript string to evaluate. @param completionHandler A block to invoke when script evaluation completes or fails. @discussion The completionHandler is passed the result of the script evaluation or an error. */ - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
2、JS調用OC
<1>iOS原生攔截URL
/*! @abstract Decides whether to allow or cancel a navigation. @param webView The web view invoking the delegate method. @param navigationAction Descriptive information about the action triggering the navigation request. @param decisionHandler The decision handler to call to allow or cancel the navigation. The argument is one of the constants of the enumerated type WKNavigationActionPolicy. @discussion If you do not implement this method, the web view will load the request or, if appropriate, forward it to another application. */ - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
<2>使用MessageHandler(WKScriptMessageHandler)實現
WKWebView 初始化時,有一個參數叫configuration,
它是WKWebViewConfiguration類型的參數,而WKWebViewConfiguration有一個屬性叫userContentController,
它又是WKUserContentController類型的參數。
WKUserContentController對象有一個方法- addScriptMessageHandler:name:,我把這個功能簡稱為MessageHandler。
添加或者刪除消息處理
/*! @abstract Adds a script message handler. @param scriptMessageHandler The message handler to add. @param name The name of the message handler. @discussion Adding a scriptMessageHandler adds a function window.webkit.messageHandlers.<name>.postMessage(<messageBody>) for all frames. */ - (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name; /*! @abstract Removes a script message handler. @param name The name of the message handler to remove. */ - (void)removeScriptMessageHandlerForName:(NSString *)name;
收到H5發送的消息
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @class WKScriptMessage; @class WKUserContentController; /*! A class conforming to the WKScriptMessageHandler protocol provides a method for receiving messages from JavaScript running in a webpage. */ @protocol WKScriptMessageHandler <NSObject> @required /*! @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. */ - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message; @end
H5端調用
window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
綜上所述:
最優的方案就是使用WKWebView,交互使用MessageHandler
參考:
https://www.jianshu.com/p/94bd66874dba
https://www.jianshu.com/p/939db6215436
https://www.jianshu.com/p/f896d73c670a
https://blog.csdn.net/lyyo_cd/article/details/84304864
https://www.jianshu.com/p/79e329ff8953
https://blog.csdn.net/u011619283/article/details/52135988