iOS Webview 與 app交互

有時候咱們在內嵌的webview中但願點擊一個連接以後,觸發iOS原生事件,而不是webview內頁面跳轉(由於webview的跳轉很生硬,而ajax+js模擬則不如原生segue平滑)。html

有時候咱們但願在頁面內consloe.log('log something')的時候在控制檯裏看到輸出,但手機裏沒有控制檯,因此咱們但願能夠利用xcode的控制檯輸出信息。ios

由於iOS沒有提供API讓咱們直接用html或者js來跟外部交互,因此咱們必須用另一種巧妙的辦法來實現這兩個功能。這種方法能夠知足咱們兩種需求。web

console.log

在html頁面中從新定義console.log:ajax

<script>
// Debug
console = new Object();
console.log = function(log) {
    var iframe = document.createElement("IFRAME");
    iframe.setAttribute("src", "ios-log:#iOS#" + log);
    document.documentElement.appendChild(iframe);
    iframe.parentNode.removeChild(iframe);
    iframe = null;
}
console.debug = console.log;
console.info = console.log;
console.warn = console.log;
console.error = console.log;
</script>

而後在須要捕獲的viewController.m中實現協議:objective-c

 

- (BOOL)webView: (UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    NSString *requestString = [[[request URL] absoluteString] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
    //NSLog(requestString);
    if ([requestString hasPrefix:@"ios-log:"]) {
        NSString* logString = [[requestString componentsSeparatedByString:@":#iOS#"] objectAtIndex:1];
        NSLog(@"UIWebView console: %@", logString);
        return NO;
    }
    return YES;
}

固然前提是webView須要把委託設定爲當前控制器:json

self.webView =[[UIWebView alloc] initWithFrame:CGRectMake(0.0f,0.0f,self.view.bounds.size.width,self.view.bounds.size.height-44)];
self.webView.delegate=self;

原理很簡單,咱們從新定義了console.log函數,還有console.debugconsole.infoconsole.warnconsole.error。當咱們在頁面js中調用console.log的時候,就會建立一個iframe發出請求,請求的協議爲ios-log:,路徑就是咱們的log字符串。發出請求以後,迅速把這個iframe清理掉。xcode

這樣,在webview中咱們發出了一個請求,而後就沒了。外部咱們用objective-c實現了一個協議,就是webview開始發出請求以前就會調用的函數。在這個函數中咱們過濾全部的請求(由於除了ios-log,還有一些「正常」的請求好比http和mailto),當前綴爲ios-log的時候,咱們就NSLog便可。app

if最後的return NO的意思是該webview的請求被捕獲,再也不請求(這個實際上不存在的頁面)。咱們但願一些合法請求(好比http、mailto等)不被捕獲,因此最後if外面丟了一個return YES編輯器

 

webview中的代碼:ide

 

<a href="myapp://somepagename">一個按鈕</a>

很是簡單,myapp這個協議你能夠本身隨便命名,稍後咱們會在objective-c中捕獲它。

仍是要實現該webview的委託controller的協議方法,若是你已經定義這個方法了(就像上面那個例子),你只須要在方法體里加入方法體裏面的內容,不然會提醒你重複定義。

- (BOOL)webView: (UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    NSURL *url = [request URL];
    if ( [[url scheme] isEqualToString:@"myapp"] )
    {
        NSString *slug = [url path];
        [self performSegueWithIdentifier:@"heroSegue" sender:slug];
        return NO;
    }
    return YES;
}

另外我在interface builder中已經拖拽了一個新的控制器,在新的控制器跟導航控制器之間,我直接拖了一個segue,命名id爲heroSegue,因此這裏能夠用performSegueWithIdentifier來調用segue。

如今,仍是在本controller中,咱們實現另外一個委託方法:

/*
 * 頁面轉換時觸發
 */
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"heroSegue"]) {
        NSLog(@"%@",sender);
        [self.webView stopLoading];
        YUGHeroDetailViewController *destViewController = segue.destinationViewController;
        destViewController.heroSlug = (NSString *)sender;
    }
}

也就是說,發生segue變化以前,就會執行這一方法,首先判斷identifier是否是等於heroSegue,若是是,本身的webview再也不載入,目標控制器(也就是即將切換過去的子頁面的控制器)中設置公有屬性heroSlug的值。

而後,咱們在目標頁面的controller的H中定義:

@property (strong) NSString *heroSlug;

最後,在目標頁面中,咱們定義的congroller中的M能拿到heroSlug這個參數。

 

NSLog(@"%@",self.heroSlug);

這樣就能夠了,拿到slug以後,咱們實際上就能夠調用一個本地頁面,帶上slug參數,而後經過ajax的方式讀取遠程頁面或者json數據,這個就不屬於本文內容了。

若是你是新手,在作上面的這些操做的時候可能會漏掉一兩個步驟,編輯器會報錯,這時候仔細閱讀並校對你的代碼。若是實在不行,說明清楚操做和報錯信息,再給我留言。

練習題:原生title的好處是它在字符數較短時是居中的,而字符更長一點時會偏右顯示,更長一些時顯示省略號。那麼webview載入一個ajax數據的頁面的時候,如何在頁面載入成功時,設置原生title?

提示,仍是自定義協議。

相關文章
相關標籤/搜索