iOS與HTML混編,最好的例子就是網易新聞詳情頁的實現,圖片與文字可以根據服務器返回的數據隨意排版,很方便
本文主要講WKWebView,與UIWebView和JavaScriptCore孰優孰劣我就不比較了,
網易新聞頁實現流程我就不重復寫了,還有其他好的文章放在最后,
但感覺大多說的不細,對沒有web開發經驗的來說有點難懂,所以本文會把我寫的一些簡單的js代碼拿出來,對沒有web基礎的來說更加清晰
本文的基礎知識主要為:
1.點擊html按鈕,oc的控制器dismiss掉
2.點擊oc按鈕,js切換頁面
3.點擊html按鈕,js切換頁面
4.點擊html的div標簽,js調用oc來present一個新的controller,上面有個返回按鈕,點擊可以返回webView的控制器
點擊獲取源代碼
效果圖就不放了,創建幾個文件把代碼全部復制一下就可以看到效果了
ViewController中有個按鈕,點擊即可跳轉到WebVC.
WebVC的.m文件,其中值得注意的是WKDelegate,是解決WKWebView循環引用問題的代理
#import "WebVC.h"
#import <WebKit/WebKit.h>
#import "WKDelegateController.h"
@interface WebVC ()<WKUIDelegate,WKNavigationDelegate,WKDelegate>
@property (strong, nonatomic) WKWebView *webView;
@property (strong, nonatomic) WKUserContentController *userContent;
@property (weak, nonatomic) UIButton *backBtn;
@end
@implementation WebVC
- (void)dealloc
{
NSLog(@"無循環引用");
//這里需要注意,前面增加過的方法一定要remove掉。
//addScriptMessageHandler要和removeScriptMessageHandlerForName配套出現,否則會造成內存泄漏。
[self.userContent removeScriptMessageHandlerForName:@"ocMethod"];
[self.userContent removeScriptMessageHandlerForName:@"presentMethod"];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor lightGrayColor];
//創建配置
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
//創建UserContentController(提供javaScript向webView發送消息的方法)
self.userContent = [[WKUserContentController alloc] init];
//添加消息處理,
//注意: addScriptMessageHandler后面的參數指代的是需要遵守WKScriptMessageHandler協議,結束時需要移除
//但無論在哪里移除都會發現dealloc并不會執行,這樣肯定是不行的,會造成內存泄漏
//試了下用weak指針還是不能釋放,不知道是什么原因
//因此參考百度上的寫法是用一個新的controller來處理,新的controller再繞用delegate繞回來,即使沒移除也會走dealloc了
WKDelegateController * delegateController = [[WKDelegateController alloc]init];
delegateController.delegate = self;
[self.userContent addScriptMessageHandler:delegateController name:@"ocMethod"];
[self.userContent addScriptMessageHandler:delegateController name:@"presentMethod"];
//將UserContent設置到配置文件中
config.userContentController = self.userContent;
//配置webView
self.webView = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:config];
self.webView.UIDelegate = self;
self.webView.navigationDelegate = self;
[self.view addSubview:self.webView];
//加載本地html
NSURL *url = [[NSBundle mainBundle] URLForResource:@"index" withExtension:@"html"];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:urlRequest];
//添加一個返回按鈕
UIButton *backBtn = [[UIButton alloc]initWithFrame:CGRectMake(10, 35, 50, 50)];
[backBtn setTitle:@"返回" forState:UIControlStateNormal];
[backBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[backBtn addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
self.backBtn = backBtn;
[self.webView addSubview:backBtn];
}
//這里就是使用高端配置,js調用oc的處理地方。我們可以根據name和body,進行橋協議的處理。
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
NSString *messageName = message.name;
if ([@"ocMethod" isEqualToString:messageName])
{
id messageBody = message.body;
NSLog(@"%@",messageBody);
//點擊html按鈕,讓當前webView頁面dismiss掉
[self dismissViewControllerAnimated:YES completion:nil];
}
else if([@"presentMethod" isEqualToString:messageName])
{
id messageBody = message.body;
NSLog(@"%@",messageBody);
//彈出一個新的紅色背景的控制器
UIViewController *newVC = [[UIViewController alloc]init];
newVC.view.backgroundColor = [UIColor redColor];
//添加一個返回按鈕,返回webView
CGRect rect = CGRectMake(100,100,100,50);
UIButton *button = [[UIButton alloc]initWithFrame:rect];
[button setTitle:@"返回" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:20];
[button addTarget:self action:@selector(dismissNewVC) forControlEvents:UIControlEventTouchUpInside];
[newVC.view addSubview:button];
[self presentViewController:newVC animated:YES completion:nil];
}
}
//返回方法
-(void)dismissNewVC
{
[self dismissViewControllerAnimated:YES completion:nil];
}
//點擊oc按鈕,調用js方法
-(void)back
{
//第一種:直接調用
//無論web頁面跳轉多少次,只要按鈕存在,js都可以生效
[self.webView evaluateJavaScript:@"function sayHello(){ \
alert('jack') \
} \
sayHello()"
completionHandler:^(id _Nullable result, NSError * _Nullable error) {
}];
//第二種:直接調用html文件中的js代碼
//注意:這種方式只有在第一個web頁面js才能生效,跳轉到第二個web頁面就無效了
//因為頁面跳轉后,就不是我們引入的本地的html頁面了,自然也就引入不了我們本地的js代碼
//不過也正常,我們一般只需要在第一個頁面添加一個返回按鈕,dismiss掉這個webView,其他的功能都可以用html的按鈕實現
[self.webView evaluateJavaScript:@"hello()"completionHandler:^(id _Nullable result, NSError * _Nullable error) {
}];
//第三種:調用html文件中引入的js文件的js代碼
//注意:js效果與第二種相同
[self.webView evaluateJavaScript:@"back()"completionHandler:^(id _Nullable result, NSError * _Nullable error) {
}];
}
//wkWebView直接調用js的彈窗是無效的,需要攔截js中的alert,用oc的方式展現出來
//該方法中的message參數就是我們JS代碼中alert函數里面的參數內容
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
NSLog(@"----------%@",message);
UIAlertController *alertView = [UIAlertController alertControllerWithTitle:@"js的彈窗" message:message preferredStyle:UIAlertControllerStyleAlert];
[alertView addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
//一定要調用下這個block,否則會崩
//API說明:The completion handler to call after the alert panel has been dismissed
completionHandler();
}]];
[self presentViewController:alertView animated:YES completion:nil];
}
@end
WKDelegateController.h文件:
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
@class WKDelegateController;
@protocol WKDelegate <NSObject>
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
@end
@interface WKDelegateController : UIViewController <WKScriptMessageHandler>
@property (weak , nonatomic) id<WKDelegate> delegate;
@end
WKDelegateController.m文件
#import "WKDelegateController.h"
@interface WKDelegateController ()
@end
@implementation WKDelegateController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
if ([self.delegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)])
{
[self.delegate userContentController:userContentController didReceiveScriptMessage:message];
}
}
index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>js與oc互調</title>
<link href="index.css" rel="stylesheet">
</head>
<body>
<button onclick="clickBtn()"> 點擊按鈕跳轉到baidu </button>
<br>
<button onclick="dismiss()"> 點擊按鈕dismiss回去 </button>
<!--導航-->
<div >
<ul>
<div class="time" onclick="clickTime()">點擊我present一個新的controller</div>
<li><a >baidu</a></li>
<li><a href="#" >about</a></li>
<li><a href="#" >services</a></li>
<li><a href="#" >gallery</a></li>
<li><a href="#" >contact</a></li>
</ul>
</div>
<!--引入的js函數文件-->
<script src="index.js" type="text/javascript"></script>
<!--直接寫的js函數-->
<script type="text/javascript">
function hello(){
alert("你好!");
}
</script>
</body>
</html>
index.js文件
//確認js加載成功
window.onload = function (){
alert(0);
}
//點擊html按鈕,調用js方法
function clickBtn(){
window.location.;
}
//點擊html按鈕,調用oc方法
function dismiss(){
window.webkit.messageHandlers.ocMethod.postMessage("我點擊html按鈕,調用oc的方法,讓webView消失");
}
//點擊html的div標簽,調用oc方法
function clickTime(){
window.webkit.messageHandlers.presentMethod.postMessage("我點擊div標簽,調用oc的方法,彈出一個控制器");
}
//點擊oc按鈕,要調用的js方法
function back() {
window.location.;
}
最后是無關緊要的index.css文件
*{
padding = 0;
margin = 0;
}
body{
padding:100px 0 0 0;
text-align:center;
background-color:lightgray;
font-size:40px;
}
button{
font-size:50px;
}
li{
list-style-type:none
}
a{
font-size:50px;
}
比較好的鏈接,也介紹了一些第三方框架:
這個實現網易新聞詳情頁寫的很詳細,所以我就不寫了.但是注意這里遺漏了解決循環引用問題,練習的時候要注意:<a href="http://www.lxweimin.com/p/75f3abd40cc1">iOS-使用WKWebview實現新聞詳情頁</a>
<a href="http://www.lxweimin.com/p/1f2dc3d3090a">iOS WKWebView導致ViewController不調用dealloc方法</a>
WKWebView并不會去NSHTTPCookieStorage中讀取cookie,因此導致cookie丟失解決辦法:<a href="http://www.lxweimin.com/p/4fa8c4eb1316">IOS進階之WKWebView</a>
講UIWebView與JavaScriptCore細的文章:
<a href="http://www.lxweimin.com/p/d19689e0ed83">iOS下JS與原生OC互相調用(總結)</a>
<a >iOS JavaScriptCore使用</a>
<a href="http://www.lxweimin.com/p/84a6b1ac974a">iOS H5容器的一些探究(一):UIWebView和WKWebView的比較和選擇</a>