githubDemo:
https://github.com/wangjinshan/JSWebDemo
項目展示
認識UIWebView
UIWebView繼承自uiview 是用來加載網頁的類,可以簡單理解成就是一個view
完整的UIWebView創建
1加載網絡數據
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, 加載本地的數據
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];
加載
加載的方式給了以下三種
/**通過NSURLRequest去加載html界面**/
- (void)loadRequest:(NSURLRequest *)request;
/**加載html格式的字符串,其中的baseUrl下面會介紹**/
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
/**這種方式表示沒見到過,我也不知道是什么,有興趣的可以自己去查查**/
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL;(*很少用*)
-(void)loadRequest:(NSURLRequest *)request 方法即可以去通過網絡連接加載html資源,也可以去加載本地的html資源
// 加載網絡html
NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://shenzoom.com"]];
[self.myWebview loadRequest:request];
- (void) loadHTMLString:(NSString )string baseURL:(nullable NSURL )baseURL方法一般用來加載本地的html界面
-(void)loadHtmlWithString
{
// 加載css
NSURL *cssPath = [[NSBundle mainBundle]URLForResource:@"ShareSDK" withExtension:@"css"];
// 創建css標簽
NSString *css = [NSString stringWithFormat:@"<link href =\"%@\" rel = %@>",cssPath,@"\"stylesheet\""];
NSLog(@"--css--%@",css);
NSString *html =[NSString stringWithFormat:@"<html><head>%@</head><body><p>網頁中的文字</p> <button id=\"login\" onclick=\"login()\">點擊按鈕</button> <img src=\"%@\" alt=""> </body></html>",css,css];
[self.myWebview loadHTMLString:html baseURL:nil];
}
注意:baseURL用來確定htmlString的基準地址,相當于HTML的<base>標簽的作用,定義頁面中所有鏈接的默認地址。具體查看W3C上的base標簽。baseURL是HTML字符串中引用到資源的查找路徑,當HTML中沒有引用外部資源時,可以指定為nil;若引用了外部資源(外部資源:除了html代碼以外,界面中所有的圖片,鏈接都屬于外部資源),一般情況下使用mainBundle的路徑即可。在實際操作中,常常會出現「文本顯示正常,圖片無法顯示」等情況,若HTML文本中引用外部資源都是使用相對路徑,則出現這種問題的原因一般都是baseURL參數錯誤
屬性
// **webView的代理**
@property (nullable, nonatomic, assign) id <UIWebViewDelegate> delegate;
// **內置的scrollView**
@property (nonatomic, readonly, strong) UIScrollView
// *scrollView NS_AVAILABLE_IOS(5_0);
// **URL請求**
@property (nullable, nonatomic, readonly, strong) NSURLRequest *request;
// **是否縮放到適合屏幕大小**
@property (nonatomic) BOOL scalesPageToFit;
// **執行javaScript操作**
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
加載屬性
- (void)reload; //重新加載數據
- (void)stopLoading; //停止加載數據
@property (nonatomic, readonly, getter=isLoading) BOOL loading; //是否正在加載
- (void)goBack; //返回上一級
- (void)goForward; //跳轉下一級
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack; //能否返回上一級
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward; //能否跳轉下一級
多媒體屬性
//YES,自動檢測網頁上的電話號碼,單擊可以撥打,_ 已經廢棄_
@property (nonatomic) BOOL detectsPhoneNumbers NS_DEPRECATED_IOS(2_0, 3_0);
//設置某些數據變為鏈接形式,這個枚舉可以設置如電話號,地址,郵箱等轉化為鏈接
@property (nonatomic) UIDataDetectorTypes dataDetectorTypes NS_AVAILABLE_IOS(3_0);
//設置是否使用內聯播放器播放視頻
@property (nonatomic) BOOL allowsInlineMediaPlayback NS_AVAILABLE_IOS(4_0); // defaults to YES
//設置視頻是否自動播放
@property (nonatomic) BOOL mediaPlaybackRequiresUserAction NS_AVAILABLE_IOS(4_0); // default to YES
//設置音頻播放是否支持ari play功能
@property (nonatomic) BOOL mediaPlaybackAllowsAirPlay NS_AVAILABLE_IOS(5_0); // default to YES
//設置是否將數據加載如內存后渲染界面
@property (nonatomic) BOOL suppressesIncrementalRendering NS_AVAILABLE_IOS(6_0); // default to NO
//設置用戶交互模式
@property (nonatomic) BOOL keyboardDisplayRequiresUserAction NS_AVAILABLE_IOS(6_0);
iOS7.0 新特性
@property (nonatomic) UIWebPaginationMode paginationMode NS_AVAILABLE_IOS(7_0);
這個屬性用來設置一種模式,當網頁的大小超出view時,將網頁以翻頁的效果展示,枚舉如下:
typedef NS_ENUM(NSInteger, UIWebPaginationMode)
{
UIWebPaginationModeUnpaginated, //不使用翻頁效果
UIWebPaginationModeLeftToRight, //將網頁超出部分分頁,從左向右進行翻頁
UIWebPaginationModeTopToBottom, //將網頁超出部分分頁,從上向下進行翻頁
UIWebPaginationModeBottomToTop, //將網頁超出部分分頁,從下向上進行翻頁
UIWebPaginationModeRightToLeft //將網頁超出部分分頁,從右向左進行翻頁
} __TVOS_PROHIBITED;
//設置每一頁的長度
@property (nonatomic) CGFloat pageLength NS_AVAILABLE_IOS(7_0);
//設置每一頁的間距
@property (nonatomic) CGFloat gapBetweenPages NS_AVAILABLE_IOS(7_0);
//獲取分頁數
@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新特性
//是否允許畫中畫播放/調用系統的視頻播放器
@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,進行加載。通過UIWebViewNavigationType可以得到請求發起的原因
如果為webView添加了delegate對象并實現該接口,那么在webView加載任何一個frame之前都會delegate對象的該方法,該方法的返回值用以控制是否允許加載目標鏈接頁面的內容,返回YES將直接加載內容,NO則反之。并且UIWebViewNavigationType枚舉,定義了頁面中用戶行為的分類,包括;
UIWebViewNavigationTypeLinkClicked,0 用戶觸擊了一個鏈接。
UIWebViewNavigationTypeFormSubmitted,1 用戶提交了一個表單。
UIWebViewNavigationTypeBackForward,2, 用戶觸擊前進或返回按鈕。
UIWebViewNavigationTypeReload,3, 用戶觸擊重新加載的按鈕。
UIWebViewNavigationTypeFormResubmitted,4,用戶重復提交表單
UIWebViewNavigationTypeOther,5, 發生其它行為。
*/
// NSLog(@"----request-------%@",request);
NSLog(@"----navigationType-------%ld",(long)navigationType);
return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
//開始加載,可以加上風火輪(也叫菊花)
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//完成加載
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
//加載出錯
}
禁用頁面滾動彈跳
self.myWebview.scrollView.bounces = false; //禁用頁面滾動彈跳
基于UIWebView ios與js交互
ios 原聲界面上加載webview, 然后需要和web界面進行數據交互就需要用到 ios和js 交互的知識, 交互的方法有三種:
1, 今天要講解的基于 UIWebView的 交互,這種方式最主要的思想就是通過網頁的代碼方法進行url的攔截操作,后面細講
2 WKWebView 的第三方庫
3, 蘋果為我們封裝 JavaScriptCore.framework
實例講解
1,配置基本環境
創建 Sample.html ShareSDK.css ShareSDK.js 實現界面布局并加載到本地
代理 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 為例子, 由于前期數據傳輸沒整理好,大家先忽略數據傳遞的問題,文章后面會補充
1, ShareSDK 初始化 ,創建一個 名字叫ShareSDK的對象 并在里面實現 initSDK初始化方法 ,數據暫時將以數組的形式傳遞,后面再補充上對象json傳遞
function ShareSDK()
{
// 初始化sdk, 注意 此處必須寫上this 否則外部調用失敗
this.initSDK = function()
{
//1,平臺的參數
var mobkey = 'iosv1101';
var platformID = new PlatformID();
//平臺數組
var platformArr = [platformID.platformID.WeChat,platformID.platformID.WeChatFavorites,platformID.platformID.WeChatMoments];
//2,微信appkey
var platformConfig = ["wx4868b35061f87885","64020361b8ec4c99936c0e3999a9f249"];
//發送請求
window.location.href = '&initSDK' + '&mobkey'+mobkey +'&platformArr'+ platformArr +'&platformConfig' + platformConfig;
}
}
var $sharesdk = new ShareSDK();
大家先簡單理解這個就是一個 js方法,下面我們要做的就是在 ios上調用這段代碼
我們已經在 viewDidLoad 的方法中加載了網頁 下面我們就簽UIWebView協議 UIWebViewDelegate 并實現代理方法
ios 調用 js代碼
ios 調用js 代碼非常簡單,UIWebView 已經為我們封裝好了方法(stringByEvaluatingJavaScriptFromString),我們將在網頁加載完畢后進行代碼處理,注意必須在網頁加載完畢才能操作,否則無效
// 網頁完成加載
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
// 注意stringByEvaluatingJavaScriptFromString必須保證是在主線線程中完成任務
[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) 這個方法必須在主線程調用,否則崩潰,如下
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 調用js 相當于 給 js 發送一段字符串,然后程序會根據字符串去環境中尋找對應的方法并執行, @"window.$sharesdk.initSDK()" window js是環境中的全局變量, 所有 js環境中方法和屬性 ,都可以通過 window 來進行調用, 此處 window 去找到 sharesdk對象并執行 initSDK()的方法
檢查 方法有沒有被調用 可以在 initSDK(); 進行 alert();進行測試,彈出警告窗口則表示調用成功
js 調用 ios代碼
js調用 ios 也非常的簡單,就是js發送一段請求,然后ios 在代理方法中進行攔截,如下
this.initSDK = function()
{
//發送請求
window.location.href = '要發送的數據';
}
js 中 只要遇到 window.location.href 就會通知 ios 執行回調方法, html 進行 某個時段進行固定跳轉也是經過這個方法, 下面我們就可以到 ios 對這段數據進行解析
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
[self analyzeURL:request.URL];
return YES; // 必須返回 yes
}
以上方法中的 request 就攜帶了 window.location.href = '要發送的數據'; 這個方法中 攜帶的 [要發送的數據], 這是一段 json 數據 ,解析這段json數據 你就可以進行相關的操作了, 到此 ios調用 js is調用ios結束,感覺非常簡單, 但是你發現沒有, 以上的實現并沒有牽扯到數據, 個人認為 交互最蛋疼一點就是數據傳遞, UIWebView 并不友好, 數據傳遞比較蛋疼
js傳遞數據給ios
上面說過 js 調用 ios 是通過 發送請求的方法 (window.location.href = '要發送的數據';) 但是這個數據到底應該怎么寫? 如下
// 測試傳遞對象類型
this.ajsTest = function ()
{
var backJson = {};
var wjs =
{
"name":"神族科技CEO",
"name":"金山",
"city":"上海"
};
backJson["wjs"] = wjs;
var wjsJson = ObjectToJsonString(backJson);
window.location.href ="ajstest://?"+wjsJson; // 注意協議頭必須是小寫 大寫將轉換成小寫
}
通常為了解析方便,我們都會攜帶一個協議,這個協議頭可以隨便寫,然后拼接你要的數據,上面的例子 傳遞的是 一個js 中的對象,我們需要將 對象轉換成 js 格式的字符串, 方法就是
var JsonStringToObject = function (string)
{
try
{
return eval("(" + string + ")");
}
catch (err)
{
return null;
}
};
這樣就可以把js中的對象數據傳給ios使用,ios攔截到的依然是個 json的字符串需要你手動解析
ios傳遞數據給js
ios 傳遞數據給 js 實現和ios 調用js 代碼一樣,只需要將傳遞的數據拼接在后面就可以
[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]]];
/**
* 對象序列化為Json字符串
*
* @param object 任意對象
*
* @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;
}
這樣就是實現了 ios 把一個 ios對象 轉換成json 對象并 傳給 js 使用
到此 基于 UIWebView 的 ios與js交互全部結束
完美的分割線