githubDemo:
https://github.com/wangjinshan/JSWebDemo
項(xiàng)目展示
認(rèn)識(shí)UIWebView
UIWebView繼承自u(píng)iview 是用來(lái)加載網(wǎng)頁(yè)的類,可以簡(jiǎn)單理解成就是一個(gè)view
完整的UIWebView創(chuàng)建
1加載網(wǎng)絡(luò)數(shù)據(jù)
self.webView = [[UIWebView alloc] initWithFrame:self.view.frame];
NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://lianghui.huanqiu.com/2017/roll/2017-03/10304840.html"]];
[self.view addSubview: _webView];
[_webView loadRequest:request];
2, 加載本地的數(shù)據(jù)
NSString *path =[[NSBundle mainBundle]pathForResource:@"JSShareSDKDemo" ofType:@"html"];
NSURL *url =[NSURL URLWithString:path];
NSURLRequest *request =[NSURLRequest requestWithURL:url];
self.webView =[[UIWebView alloc]initWithFrame:self.view.frame];
[self.webView loadRequest:request];
[self.view addSubview:self.webView];
加載
加載的方式給了以下三種
/**通過(guò)NSURLRequest去加載html界面**/
- (void)loadRequest:(NSURLRequest *)request;
/**加載html格式的字符串或粮,其中的baseUrl下面會(huì)介紹**/
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
/**這種方式表示沒(méi)見(jiàn)到過(guò)玉控,我也不知道是什么澳眷,有興趣的可以自己去查查**/
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL;(*很少用*)
-(void)loadRequest:(NSURLRequest *)request 方法即可以去通過(guò)網(wǎng)絡(luò)連接加載html資源捌显,也可以去加載本地的html資源
// 加載網(wǎng)絡(luò)html
NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://shenzoom.com"]];
[self.myWebview loadRequest:request];
- (void) loadHTMLString:(NSString )string baseURL:(nullable NSURL )baseURL方法一般用來(lái)加載本地的html界面
-(void)loadHtmlWithString
{
// 加載css
NSURL *cssPath = [[NSBundle mainBundle]URLForResource:@"ShareSDK" withExtension:@"css"];
// 創(chuàng)建css標(biāo)簽
NSString *css = [NSString stringWithFormat:@"<link href =\"%@\" rel = %@>",cssPath,@"\"stylesheet\""];
NSLog(@"--css--%@",css);
NSString *html =[NSString stringWithFormat:@"<html><head>%@</head><body><p>網(wǎng)頁(yè)中的文字</p> <button id=\"login\" onclick=\"login()\">點(diǎn)擊按鈕</button> <img src=\"%@\" alt=""> </body></html>",css,css];
[self.myWebview loadHTMLString:html baseURL:nil];
}
注意:baseURL用來(lái)確定htmlString的基準(zhǔn)地址指孤,相當(dāng)于HTML的<base>標(biāo)簽的作用,定義頁(yè)面中所有鏈接的默認(rèn)地址顽悼。具體查看W3C上的base標(biāo)簽锣咒。baseURL是HTML字符串中引用到資源的查找路徑,當(dāng)HTML中沒(méi)有引用外部資源時(shí)牺勾,可以指定為nil正罢;若引用了外部資源(外部資源:除了html代碼以外,界面中所有的圖片驻民,鏈接都屬于外部資源)翻具,一般情況下使用mainBundle的路徑即可。在實(shí)際操作中回还,常常會(huì)出現(xiàn)「文本顯示正常裆泳,圖片無(wú)法顯示」等情況,若HTML文本中引用外部資源都是使用相對(duì)路徑柠硕,則出現(xiàn)這種問(wèn)題的原因一般都是baseURL參數(shù)錯(cuò)誤
屬性
// **webView的代理**
@property (nullable, nonatomic, assign) id <UIWebViewDelegate> delegate;
// **內(nèi)置的scrollView**
@property (nonatomic, readonly, strong) UIScrollView
// *scrollView NS_AVAILABLE_IOS(5_0);
// **URL請(qǐng)求**
@property (nullable, nonatomic, readonly, strong) NSURLRequest *request;
// **是否縮放到適合屏幕大小**
@property (nonatomic) BOOL scalesPageToFit;
// **執(zhí)行javaScript操作**
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
加載屬性
- (void)reload; //重新加載數(shù)據(jù)
- (void)stopLoading; //停止加載數(shù)據(jù)
@property (nonatomic, readonly, getter=isLoading) BOOL loading; //是否正在加載
- (void)goBack; //返回上一級(jí)
- (void)goForward; //跳轉(zhuǎn)下一級(jí)
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack; //能否返回上一級(jí)
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward; //能否跳轉(zhuǎn)下一級(jí)
多媒體屬性
//YES工禾,自動(dòng)檢測(cè)網(wǎng)頁(yè)上的電話號(hào)碼,單擊可以撥打,_ 已經(jīng)廢棄_
@property (nonatomic) BOOL detectsPhoneNumbers NS_DEPRECATED_IOS(2_0, 3_0);
//設(shè)置某些數(shù)據(jù)變?yōu)殒溄有问交热幔@個(gè)枚舉可以設(shè)置如電話號(hào)闻葵,地址,郵箱等轉(zhuǎn)化為鏈接
@property (nonatomic) UIDataDetectorTypes dataDetectorTypes NS_AVAILABLE_IOS(3_0);
//設(shè)置是否使用內(nèi)聯(lián)播放器播放視頻
@property (nonatomic) BOOL allowsInlineMediaPlayback NS_AVAILABLE_IOS(4_0); // defaults to YES
//設(shè)置視頻是否自動(dòng)播放
@property (nonatomic) BOOL mediaPlaybackRequiresUserAction NS_AVAILABLE_IOS(4_0); // default to YES
//設(shè)置音頻播放是否支持ari play功能
@property (nonatomic) BOOL mediaPlaybackAllowsAirPlay NS_AVAILABLE_IOS(5_0); // default to YES
//設(shè)置是否將數(shù)據(jù)加載如內(nèi)存后渲染界面
@property (nonatomic) BOOL suppressesIncrementalRendering NS_AVAILABLE_IOS(6_0); // default to NO
//設(shè)置用戶交互模式
@property (nonatomic) BOOL keyboardDisplayRequiresUserAction NS_AVAILABLE_IOS(6_0);
iOS7.0 新特性
@property (nonatomic) UIWebPaginationMode paginationMode NS_AVAILABLE_IOS(7_0);
這個(gè)屬性用來(lái)設(shè)置一種模式癣丧,當(dāng)網(wǎng)頁(yè)的大小超出view時(shí)槽畔,將網(wǎng)頁(yè)以翻頁(yè)的效果展示,枚舉如下:
typedef NS_ENUM(NSInteger, UIWebPaginationMode)
{
UIWebPaginationModeUnpaginated, //不使用翻頁(yè)效果
UIWebPaginationModeLeftToRight, //將網(wǎng)頁(yè)超出部分分頁(yè)坎缭,從左向右進(jìn)行翻頁(yè)
UIWebPaginationModeTopToBottom, //將網(wǎng)頁(yè)超出部分分頁(yè)竟痰,從上向下進(jìn)行翻頁(yè)
UIWebPaginationModeBottomToTop, //將網(wǎng)頁(yè)超出部分分頁(yè),從下向上進(jìn)行翻頁(yè)
UIWebPaginationModeRightToLeft //將網(wǎng)頁(yè)超出部分分頁(yè)掏呼,從右向左進(jìn)行翻頁(yè)
} __TVOS_PROHIBITED;
//設(shè)置每一頁(yè)的長(zhǎng)度
@property (nonatomic) CGFloat pageLength NS_AVAILABLE_IOS(7_0);
//設(shè)置每一頁(yè)的間距
@property (nonatomic) CGFloat gapBetweenPages NS_AVAILABLE_IOS(7_0);
//獲取分頁(yè)數(shù)
@property (nonatomic, readonly) NSUInteger pageCount NS_AVAILABLE_IOS(7_0);
// 未知
@property (nonatomic) UIWebPaginationBreakingMode paginationBreakingMode NS_AVAILABLE_IOS(7_0);
typedef NS_ENUM(NSInteger, UIWebPaginationBreakingMode)
{
UIWebPaginationBreakingModePage,
UIWebPaginationBreakingModeColumn
} __TVOS_PROHIBITED;
iOS9.0新特性
//是否允許畫中畫播放/調(diào)用系統(tǒng)的視頻播放器
@property (nonatomic) BOOL allowsPictureInPictureMediaPlayback NS_AVAILABLE_IOS(9_0);
@property (nonatomic) BOOL allowsLinkPreview NS_AVAILABLE_IOS(9_0); //
UIWebView的代理方法 UIWebViewDelegate
self.myWebview.delegate =self;
//代理方法
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
/**返回YES坏快,進(jìn)行加載。通過(guò)UIWebViewNavigationType可以得到請(qǐng)求發(fā)起的原因
如果為webView添加了delegate對(duì)象并實(shí)現(xiàn)該接口憎夷,那么在webView加載任何一個(gè)frame之前都會(huì)delegate對(duì)象的該方法莽鸿,該方法的返回值用以控制是否允許加載目標(biāo)鏈接頁(yè)面的內(nèi)容,返回YES將直接加載內(nèi)容拾给,NO則反之祥得。并且UIWebViewNavigationType枚舉,定義了頁(yè)面中用戶行為的分類蒋得,包括;
UIWebViewNavigationTypeLinkClicked级及,0 用戶觸擊了一個(gè)鏈接。
UIWebViewNavigationTypeFormSubmitted额衙,1 用戶提交了一個(gè)表單饮焦。
UIWebViewNavigationTypeBackForward,2, 用戶觸擊前進(jìn)或返回按鈕窍侧。
UIWebViewNavigationTypeReload县踢,3, 用戶觸擊重新加載的按鈕。
UIWebViewNavigationTypeFormResubmitted伟件,4,用戶重復(fù)提交表單
UIWebViewNavigationTypeOther硼啤,5, 發(fā)生其它行為。
*/
// NSLog(@"----request-------%@",request);
NSLog(@"----navigationType-------%ld",(long)navigationType);
return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
//開(kāi)始加載斧账,可以加上風(fēng)火輪(也叫菊花)
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//完成加載
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
//加載出錯(cuò)
}
禁用頁(yè)面滾動(dòng)彈跳
self.myWebview.scrollView.bounces = false; //禁用頁(yè)面滾動(dòng)彈跳
基于UIWebView ios與js交互
ios 原聲界面上加載webview, 然后需要和web界面進(jìn)行數(shù)據(jù)交互就需要用到 ios和js 交互的知識(shí), 交互的方法有三種:
1, 今天要講解的基于 UIWebView的 交互,這種方式最主要的思想就是通過(guò)網(wǎng)頁(yè)的代碼方法進(jìn)行url的攔截操作,后面細(xì)講
2 WKWebView 的第三方庫(kù)
3, 蘋果為我們封裝 JavaScriptCore.framework
實(shí)例講解
1,配置基本環(huán)境
創(chuàng)建 Sample.html ShareSDK.css ShareSDK.js 實(shí)現(xiàn)界面布局并加載到本地
代理 UIWebViewDelegate
NSString *path = [[NSBundle mainBundle]pathForResource:@"Sample" ofType:@"html"];
NSURL*htmlURL = [NSURL fileURLWithPath:path];
self.webView =[[UIWebView alloc]initWithFrame:self.view.bounds];
NSURLRequest *request =[NSURLRequest requestWithURL:htmlURL];
[_webView loadRequest:request];
_webView.delegate = self;
[self.view addSubview:_webView];
以下以在html上集成集成 ShareSDK 為例子, 由于前期數(shù)據(jù)傳輸沒(méi)整理好,大家先忽略數(shù)據(jù)傳遞的問(wèn)題,文章后面會(huì)補(bǔ)充
1, ShareSDK 初始化 ,創(chuàng)建一個(gè) 名字叫ShareSDK的對(duì)象 并在里面實(shí)現(xiàn) initSDK初始化方法 ,數(shù)據(jù)暫時(shí)將以數(shù)組的形式傳遞,后面再補(bǔ)充上對(duì)象json傳遞
function ShareSDK()
{
// 初始化sdk, 注意 此處必須寫上this 否則外部調(diào)用失敗
this.initSDK = function()
{
//1,平臺(tái)的參數(shù)
var mobkey = 'iosv1101';
var platformID = new PlatformID();
//平臺(tái)數(shù)組
var platformArr = [platformID.platformID.WeChat,platformID.platformID.WeChatFavorites,platformID.platformID.WeChatMoments];
//2,微信appkey
var platformConfig = ["wx4868b35061f87885","64020361b8ec4c99936c0e3999a9f249"];
//發(fā)送請(qǐng)求
window.location.href = '&initSDK' + '&mobkey'+mobkey +'&platformArr'+ platformArr +'&platformConfig' + platformConfig;
}
}
var $sharesdk = new ShareSDK();
大家先簡(jiǎn)單理解這個(gè)就是一個(gè) js方法,下面我們要做的就是在 ios上調(diào)用這段代碼
我們已經(jīng)在 viewDidLoad 的方法中加載了網(wǎng)頁(yè) 下面我們就簽UIWebView協(xié)議 UIWebViewDelegate 并實(shí)現(xiàn)代理方法
ios 調(diào)用 js代碼
ios 調(diào)用js 代碼非常簡(jiǎn)單,UIWebView 已經(jīng)為我們封裝好了方法(stringByEvaluatingJavaScriptFromString),我們將在網(wǎng)頁(yè)加載完畢后進(jìn)行代碼處理,注意必須在網(wǎng)頁(yè)加載完畢才能操作,否則無(wú)效
// 網(wǎng)頁(yè)完成加載
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
// 注意stringByEvaluatingJavaScriptFromString必須保證是在主線線程中完成任務(wù)
[self.webView stringByEvaluatingJavaScriptFromString:@"window.$sharesdk.initSDK()"];
NSLog(@"----1------%@",[NSThread currentThread]); //線程是1
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_sync(queue, ^{
NSLog(@"----2-----%@",[NSThread currentThread]); //線程還是1
[self.webView stringByEvaluatingJavaScriptFromString:@"window.$sharesdk.ajsTest()"];
});
}
注意 (stringByEvaluatingJavaScriptFromString) 這個(gè)方法必須在主線程調(diào)用,否則崩潰,如下
Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
代碼注釋: ios 調(diào)用js 相當(dāng)于 給 js 發(fā)送一段字符串,然后程序會(huì)根據(jù)字符串去環(huán)境中尋找對(duì)應(yīng)的方法并執(zhí)行, @"window.$sharesdk.initSDK()" window js是環(huán)境中的全局變量, 所有 js環(huán)境中方法和屬性 ,都可以通過(guò) window 來(lái)進(jìn)行調(diào)用, 此處 window 去找到 sharesdk對(duì)象并執(zhí)行 initSDK()的方法
檢查 方法有沒(méi)有被調(diào)用 可以在 initSDK(); 進(jìn)行 alert();進(jìn)行測(cè)試,彈出警告窗口則表示調(diào)用成功
js 調(diào)用 ios代碼
js調(diào)用 ios 也非常的簡(jiǎn)單,就是js發(fā)送一段請(qǐng)求,然后ios 在代理方法中進(jìn)行攔截,如下
this.initSDK = function()
{
//發(fā)送請(qǐng)求
window.location.href = '要發(fā)送的數(shù)據(jù)';
}
js 中 只要遇到 window.location.href 就會(huì)通知 ios 執(zhí)行回調(diào)方法, html 進(jìn)行 某個(gè)時(shí)段進(jìn)行固定跳轉(zhuǎn)也是經(jīng)過(guò)這個(gè)方法, 下面我們就可以到 ios 對(duì)這段數(shù)據(jù)進(jìn)行解析
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
[self analyzeURL:request.URL];
return YES; // 必須返回 yes
}
以上方法中的 request 就攜帶了 window.location.href = '要發(fā)送的數(shù)據(jù)'; 這個(gè)方法中 攜帶的 [要發(fā)送的數(shù)據(jù)], 這是一段 json 數(shù)據(jù) ,解析這段json數(shù)據(jù) 你就可以進(jìn)行相關(guān)的操作了, 到此 ios調(diào)用 js is調(diào)用ios結(jié)束,感覺(jué)非常簡(jiǎn)單, 但是你發(fā)現(xiàn)沒(méi)有, 以上的實(shí)現(xiàn)并沒(méi)有牽扯到數(shù)據(jù), 個(gè)人認(rèn)為 交互最蛋疼一點(diǎn)就是數(shù)據(jù)傳遞, UIWebView 并不友好, 數(shù)據(jù)傳遞比較蛋疼
js傳遞數(shù)據(jù)給ios
上面說(shuō)過(guò) js 調(diào)用 ios 是通過(guò) 發(fā)送請(qǐng)求的方法 (window.location.href = '要發(fā)送的數(shù)據(jù)';) 但是這個(gè)數(shù)據(jù)到底應(yīng)該怎么寫? 如下
// 測(cè)試傳遞對(duì)象類型
this.ajsTest = function ()
{
var backJson = {};
var wjs =
{
"name":"神族科技CEO",
"name":"金山",
"city":"上海"
};
backJson["wjs"] = wjs;
var wjsJson = ObjectToJsonString(backJson);
window.location.href ="ajstest://?"+wjsJson; // 注意協(xié)議頭必須是小寫 大寫將轉(zhuǎn)換成小寫
}
通常為了解析方便,我們都會(huì)攜帶一個(gè)協(xié)議,這個(gè)協(xié)議頭可以隨便寫,然后拼接你要的數(shù)據(jù),上面的例子 傳遞的是 一個(gè)js 中的對(duì)象,我們需要將 對(duì)象轉(zhuǎn)換成 js 格式的字符串, 方法就是
var JsonStringToObject = function (string)
{
try
{
return eval("(" + string + ")");
}
catch (err)
{
return null;
}
};
這樣就可以把js中的對(duì)象數(shù)據(jù)傳給ios使用,ios攔截到的依然是個(gè) json的字符串需要你手動(dòng)解析
ios傳遞數(shù)據(jù)給js
ios 傳遞數(shù)據(jù)給 js 實(shí)現(xiàn)和ios 調(diào)用js 代碼一樣,只需要將傳遞的數(shù)據(jù)拼接在后面就可以
[errorDic setObject:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInteger:[error code]],
@"error_code",
[error userInfo],
@"error_msg",
nil]
forKey:@"error"];
[self.webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"$sharesdk.callBackData('%@')",[self jsonStringFromObject:errorDic]]];
/**
* 對(duì)象序列化為Json字符串
*
* @param object 任意對(duì)象
*
* @return Json字符串
*/
- (NSString *)jsonStringFromObject:(id)object
{
NSString *jsonString = [[NSString alloc]init];
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object
options:NSJSONWritingPrettyPrinted
error:&error];
if (! jsonData) {
NSLog(@"error: %@", error);
} else {
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
NSMutableString *mutStr = [NSMutableString stringWithString:jsonString];
NSRange range = {0,jsonString.length};
[mutStr replaceOccurrencesOfString:@" "withString:@""options:NSLiteralSearch range:range];
NSRange range2 = {0,mutStr.length};
[mutStr replaceOccurrencesOfString:@"\n"withString:@""options:NSLiteralSearch range:range2];
return mutStr;
}
這樣就是實(shí)現(xiàn)了 ios 把一個(gè) ios對(duì)象 轉(zhuǎn)換成json 對(duì)象并 傳給 js 使用
到此 基于 UIWebView 的 ios與js交互全部結(jié)束
完美的分割線