WKWebView中的js與原生App的交互

由于業(yè)務需要肖油,最近開發(fā)并總結了關于JavaScript和原生app的交互的一些實現(xiàn)方式。

通常情況下,我們加載一個網(wǎng)頁胶背,然后使用WKWebView展示它。WKWebView自開發(fā)以來就提供了豐富的js和原生的交互能力喘先,比如為了能夠獲取網(wǎng)頁中的一些內(nèi)容钳吟,我們可以使用原生方法執(zhí)行JavaScript代碼來實現(xiàn)信息抓取,然后再將結果回傳到原生環(huán)境窘拯。

至于js如何主動調(diào)用原生方法红且,并攜帶上原生app需要的結果,這里總結了以下幾種方式:

  • 通過截獲js的跳轉請求涤姊,進入原生回調(diào)
  • 通過執(zhí)行js函數(shù)得到返回結果暇番,回到原生方法
  • 通過js腳本注入
    • 官方支持:直接注入帶有消息發(fā)送機制的js腳本
    • 間接注入帶有腳本URL的標簽,方便js代碼的動態(tài)修改


<br />

方法一:截獲js的跳轉請求

比如點擊網(wǎng)頁中一個鏈接砂轻,該鏈接會導致新頁面的加載奔誓。而WKWebView響應加載之前斤吐,會首先截獲這個行為搔涝,然后調(diào)用下面這個WKNavigationDelegate代理方法,讓開發(fā)者來決定是允許還是取消該行為和措。

-[WKWebView webView:decidePolicyForNavigationAction:decisionHandler:]

比如這段js代碼:window.location.庄呈,表示當前的webview將要跳轉到新的網(wǎng)址,如果實現(xiàn)了上面的代理派阱,該行為就會首先被WKWebView攔截下來诬留,只有經(jīng)過了開發(fā)者允許后,才會執(zhí)行新的跳轉請求贫母。

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    
    // 比如只允許加載https:
    NSString *href = navigationAction.request.URL.absoluteString;
    if ([href hasPrefix:@"https://"]) {
        decisionHandler(WKNavigationActionPolicyAllow);
    } else if ([href hasPrefix:@"http://"]) {
        decisionHandler(WKNavigationActionPolicyCancel);
    }
    // ...
}

而這種機制也提供了一個js調(diào)用原生app的機會文兑。比如將js代碼改成:
window.location.href = 'myapp://get_info_' + json.toString()
并且傳遞一個json在鏈接中,那么objc就有機會截取到整個鏈接的信息腺劣,然后解析并取出其中的參數(shù)绿贞,順便取消WKWebView對該鏈接的加載行為,這樣就達到了從js傳遞給原生app信息的目的橘原。

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    
    NSString *href = navigationAction.request.URL.absoluteString;
    if ([href hasPrefix:@"myapp://get_info_"]) {
        // parse and get json, then ...
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

注意如果從js傳遞的信息中帶有中文字符籍铁,那么這些字符首先會被轉義成為URL編碼后的格式涡上,才能進行跳轉請求。例如myapp://get_info_蘋果就成了myapp://get_info_%e8%8b%b9%e6%9e%9c這樣的信息拒名。而原生應用為了得到并且還原這些字符串吩愧,可以使用NSString的UTF8的解碼方式進行中文的還原。

<br />

方法二:通過執(zhí)行js函數(shù)得到返回結果

WKWebView本身自帶了執(zhí)行js的方法:
-[WKWebView evaluateJavaScript:completionHandler:]

該方法不會阻塞用戶在主線程的網(wǎng)頁交互增显,且保證回調(diào)block一定在主線程中雁佳。如果js代碼有返回值的話,那么返回值就可以在block里面獲取到同云。這就要求js的代碼以函數(shù)的形式甘穿,在該作用域中返回一個結果。

比如執(zhí)行以下代碼:

NSString *js = @"function greeting() { return 'hello'; }; greeting();";

[webView evaluateJavaScript:js completionHandler:^(id _Nullable callback, NSError * _Nullable error) {
    NSLog(@"%@", callback); // 輸入hello
}];

可以通過這種方式梢杭,用js抓取網(wǎng)頁的信息温兼,將結果通過函數(shù)返回的形式,告訴原生app武契。

<br />

方法三:官方提供的js腳本注入和原生回調(diào)

這里提到的向WKWebView進行js腳本注入和回調(diào)募判,其實是蘋果提供的自家支持,允許webView在展示一個網(wǎng)頁前咒唆,先運行指定的js腳本届垫,完成特殊的需求。然后在js的腳本中全释,可以通過發(fā)送消息(Post Message)的方法給原生app傳遞結果装处,而原生app只需要注冊一個消息處理機制(Message Handler)即能接收到來自js發(fā)送的消息。舉個例子浸船,我需要加載google.com的時候妄迁,通過js代碼來獲取google的標題信息,把結果傳遞給app李命。

首先寫好js代碼:獲取網(wǎng)頁標題登淘,接著命名一個didFindTitle消息,這里的didFindTitle是自定義的封字,可以根據(jù)不同需求來定義成不同名字黔州,最后發(fā)送消息給app。

var title = document.getElementsByTagName("title")[0].textContent;
window.webkit.messageHandlers.didFindTitle.postMessage(title)

建議把js代碼單獨保存為文件形式阔籽,這里保存為get_title.js文件流妻,加入到Xcode項目中,避免與原生代碼過度混淆笆制;然后需要創(chuàng)建一個WKUserScript腳本對象绅这,作為配置信息添加到WKWebView中去。

// 讀取js腳本
NSString *filePath = [NSBundle.mainBundle pathForResource:@"get_title" ofType:@"js"];
NSString *javaScript = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];

// 生成WKUserScript對象
WKUserScript *script = [[WKUserScript alloc] initWithSource:javaScript
                                              injectionTime:WKUserScriptInjectionTimeAtDocumentStart
                                           forMainFrameOnly:YES];

// 生成WKWebViewConfiguration配置信息
WKWebViewConfiguration *configuration = [WKWebViewConfiguration new];
[configuration.userContentController addUserScript:script]; // 添加腳本
[configuration.userContentController addScriptMessageHandler:self name:@"didGetTitle"]; // 注冊消息處理器

// 生成帶有自定義配制的WKWebView
self.webView = [[WKWebView alloc] initWithFrame:frame configuration:configuration];

接下來需要實現(xiàn)WKScriptMessageHandler協(xié)議的一個方法:

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    // 匹配消息名稱项贺,這里需要匹配的是didFindTitle
    if ([message.name isEqualToString:@"didFindTitle"]) {
        NSString *title = message.body; // 獲取發(fā)送的消息內(nèi)容
        // ...
    }
}

運行后君躺,在網(wǎng)頁顯示之前峭判,就能獲得網(wǎng)頁的標題了。

腳本注入時機

關于腳本注入的時機棕叫,只有WKUserScriptInjectionTimeAtDocumentStartWKUserScriptInjectionTimeAtDocumentEnd可以選擇林螃,以下是文檔解釋:

  • Start: Inject the script after the document element has been created, but before any other content has been loaded. 意思是網(wǎng)頁中的元素標簽創(chuàng)建剛出來的時候,但是還沒有內(nèi)容俺泣。該時機適合通過注入腳本來添加元素標簽等操作疗认。(注意:此時<head>和<body>等標簽都還沒有出現(xiàn))
  • End: Inject the script after the document has finished loading, but before any subresources may have finished loading. 意思是網(wǎng)頁中的元素標簽已經(jīng)加載好了內(nèi)容,但是網(wǎng)頁還沒有渲染出來伏钠。該時機適合通過注入腳本來獲取元素標簽內(nèi)容等操作横漏。(如果注入的js代碼跟修改元素標簽有關的話,這就是合適的時機)

在demo中熟掂,我試圖在兩個時機分別在DOM樹中各插入一個元素缎浇,結果如下:

dom-tree.jpg

使用消息處理的注意事項

  1. 注意copy關鍵字

從文檔中可以看到,創(chuàng)建WKWebView的時候赴肚,會傳入一個WKWebViewConfiguration對象作為網(wǎng)頁的配置信息(沒有傳的話就是默認的配置)素跺,該配置對象在加入到webView以后會被其拷貝,意味著之后修改配置信息的話將會被webView無視誉券。

即便如此指厌,WKWebViewConfiguration自己的屬性對象并沒有實現(xiàn)NSCopying,比如WKUserContentController踊跟,這就意味著它們的屬性是可以修改踩验,并且改完還是奏效的:

// 獲取WKWebView的配置,這里得到一個copy的對象
WKWebViewConfiguration *configuration = self.webView.configuration;
// 從copy來的配置中商玫,得到userContentController箕憾,這里的引用和copy前的配置引用的是同一個對象
WKUserContentController *userContentController = configuration.userContentController;
// 添加腳本等
[userContentController addUserScript:script];
[userContentController addScriptMessageHandler:self name:@"didFindTitle"];

這個例子說明WKUserContentController對象可以在WKWebView創(chuàng)建好以后再次添加新配置,不過官方還是建議使用WKWebView的時候决帖,最好是先配置好了再使用厕九,而不是使用的時候修改配置蓖捶。

  1. 注意addScriptMessageHandler:導致的引用循環(huán)

addScriptMessageHandler:方法會將作為參數(shù)的消息處理目標對象建立強引用關系地回,就像使用NSTimer一樣,很容易陷入引用循環(huán)俊鱼。

[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"xxx"]

當然解決方案也很容易刻像,就像NSTimer使用過后調(diào)用[timer invalidate]一樣,調(diào)用相應的移除方法并闲,即可打破循環(huán)引用细睡。

[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"xxx"];

<br />

動態(tài)修改js腳本

上面提到的js腳本,如果放在app的bundle中隨著發(fā)布版本來更新的話帝火,自然是可行的溜徙。但是哪個產(chǎn)品經(jīng)理不希望js腳本能即改即用(其實互聯(lián)網(wǎng)產(chǎn)品經(jīng)理希望任何東西都能支持動態(tài)配置~)湃缎,為了實現(xiàn)這個需求,可以有兩個方案:

  1. 每次檢查js的腳本更新蠢壹,如有更新嗓违,將更新后的js腳本下載到app,確保使用最新的腳本
  2. 在網(wǎng)頁中注入包含腳本URL的標簽图贸,讓網(wǎng)頁自己去加載最新的js腳本

第一種方案的實現(xiàn)蹂季,就是下載文件到設備的文件目錄,使用新文件替代舊文件疏日。
下面主要介紹第二種方案偿洁,通常網(wǎng)頁元素中會包含這樣的標簽:
<script type="text/javascript" src="xxx.js"></script>

意思是網(wǎng)頁會通過這里提供的URL路徑來加載腳本文件。這樣的話沟优,就可以不用直接注入實際的js代碼涕滋,而是注入一個包含腳本URL的script標簽,讓網(wǎng)頁通過URL的方式來獲取腳本挠阁,于是腳本的代碼就可以隨時動態(tài)修改了何吝,改完部署到服務器上,也無需改動URL地址鹃唯。當然了爱榕,如果載入自家前端做的網(wǎng)頁,那么這個標簽完全可以寫在網(wǎng)頁中坡慌,但如果是加載任何一個網(wǎng)頁的話黔酥,就只能通過注入的方式來進行統(tǒng)一處理。

舉例:比如前端寫好一個js文件洪橘,用于抓取網(wǎng)頁的標題跪者,如下:

function grabTitle() {
    return document.getElementsByTagName("title")[0].textContent;
}

把該文件命名為grab_title.js,然后上傳至服務器上熄求,假設訪問路徑定為https://your_host/grab_title.js渣玲。至于為什么推薦https而不是http,因為瀏覽器會阻止向https的網(wǎng)頁中運行來自不安全的js弟晚,從而導致即使注入成功也不會執(zhí)行js代碼的窘境忘衍。

接下來實現(xiàn)iOS端,這里給WKUserScript添加一個插入script標簽和屬性的擴展方法卿城,即創(chuàng)建和向網(wǎng)頁中插入script標簽枚钓。

typedef NS_ENUM(NSInteger, HTMLScriptTagPosition) {
    HTMLScriptTagPositionHead, // 將節(jié)點插入在<head>中
    HTMLScriptTagPositionBody, // 將節(jié)點插入在<body>中
};

@interface WKUserScript (ScriptURL)

// 通過傳入一個或者多個腳本URL,來生成一個WKUserScript對象
- (instancetype)initWithScriptURLs:(NSArray <NSString *> *)scriptURLs toPosition:(HTMLScriptTagPosition)position forMainFrameOnly:(BOOL)forMainFrameOnly;

@end

@implementation WKUserScript (ScriptURL)

- (instancetype)initWithScriptURLs:(NSArray <NSString *> *)scriptURLs toPosition:(HTMLScriptTagPosition)position forMainFrameOnly:(BOOL)forMainFrameOnly {
    
    NSMutableString *javaScript = [NSMutableString string];
    
    NSString *targetElement = nil;
    switch (position) {
        case HTMLScriptTagPositionHead: targetElement = @"head"; break;
        case HTMLScriptTagPositionBody: targetElement = @"body"; break;
    }
    
    for (NSUInteger i = 0; i < scriptURLs.count; i++) {
        NSString *scriptURL = scriptURLs[i];
        
        // 創(chuàng)建<script>標簽
        [javaScript appendFormat:@"var scriptElement%@ = document.createElement('script');", @(i)];
        [javaScript appendFormat:@"scriptElement%@.setAttribute(\"type\", \"text/javascript\");", @(i)];
        [javaScript appendFormat:@"scriptElement%@.setAttribute(\"src\", \"%@\");", @(i), scriptURL];
        
        // 將<script>插入到<head>或者<body>中去
        [javaScript appendFormat:@"var targetElement = document.getElementsByTagName('%@')[0];", targetElement];
        [javaScript appendFormat:@"targetElement.appendChild(scriptElement%@);", @(i)];
    }
    
    return [self initWithSource:javaScript.copy
                  injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
               forMainFrameOnly:forMainFrameOnly];
}

@end

使用擴展方法創(chuàng)建WKUserScript對象瑟押,將其注入到網(wǎng)頁中即可:

NSString *scriptURL = @"https://your_host/grab_title.js";
NSString *requestURL = [scriptURL stringByAppendingFormat:@"?%@", @(arc4random() % 999)];
WKUserScript *script = [[WKUserScript alloc] initWithScriptURLs:@[requestURL] toPosition:HTMLScriptTagPositionBody forMainFrameOnly:YES];

如果注入成功搀捷,那么在網(wǎng)頁的<body>最后會出現(xiàn)一個新的節(jié)點<script type="text/javascript" src="https://your_host/grab_title.js?123"></script>,這里特別在URL后面添加了一個隨機數(shù)多望,本意是為了防止下一次請求相同URL的時候嫩舟,瀏覽器會默認使用上次的緩存結果氢烘。當然你可以通過代碼將webview設為“無痕跡瀏覽”,或者手動清理緩存文件來實現(xiàn)每次都能下載并使用最新的grab_title.js家厌,但是我感覺做法都不如直接用隨機數(shù)方案解決起來的方便威始。

當網(wǎng)頁加載完成后,會正常下載了定義好的grabTitle()函數(shù)像街,這時候就可以通過webview執(zhí)行js方法

[webView evaluateJavaScript:@"grabTitle();" completionHandler:^(NSString *title, NSError * _Nullable error) {
    NSLog(@"title = %@", title);
}];

以后前端更新了js文件黎棠,只要保持調(diào)用函數(shù)的名稱和返回值格式一致,那么iOS端就不用做升級維護,并且無需擔心因為緩存問題而無法使用最新的js腳本。當然這樣的腳本還是越輕量越好香椎。

注入腳本URL的注意事項

  • 腳本中的js向原生app的回調(diào)方案可以是多樣化的,只需要事先約定好一個策略:可以是發(fā)送消息和消息處理随静;也可以是函數(shù)定義;還可以讓原生app截獲js跳轉請求等等吗讶。定好以后就統(tǒng)一規(guī)范燎猛,在未來的腳本更新中依然保持一致的回調(diào)方式
  • script標簽位置通常在<head>或者<body>中,否則會有bug照皆。放在<head>中會優(yōu)先下載腳本重绷,同時阻塞后面的HTML元素解析,影響網(wǎng)頁加載速度膜毁;而放在<body>中則會等到頁面元素解析完成以后昭卓,才會加載腳本;這里有文檔列出了script其他的屬性可供參考
  • 及時用瀏覽器的網(wǎng)頁檢查器來調(diào)試瘟滨,查看注入后的網(wǎng)頁元素是否符合預期候醒,注入后的js是否正常工作。方法是:
    1. 使用iPhone真機或模擬器運行app杂瘸,加載網(wǎng)頁倒淫,完成注入工作
    2. 保持該網(wǎng)頁在屏幕中的展示,不要退出败玉;如果是真機敌土,確保設備與Mac連接
    3. 在Mac上運行Safari,在菜單中選擇“開發(fā)” -> "xxx's iPhone"(真機) / "Simulator"(模擬器)-> 選擇該頁面(鼠標移動到該選項的時候绒怨,頁面會顯示高亮狀態(tài))
    4. 在網(wǎng)頁檢查器中纯赎,選擇“元素”可以查看網(wǎng)頁標簽和內(nèi)容,在“控制臺”可以調(diào)試js代碼
  • 如果注入標簽成功南蹂,但是網(wǎng)頁卻沒有加載腳本代碼,看看是不是瀏覽器會阻止在https的網(wǎng)頁中加載不安全的http的URL念恍。我本人就是在前端同事的幫助下六剥,使用Safari網(wǎng)絡檢查到了以下的警告:[blocked] The page at https://en.m.wikipedia.org/wiki/Main_Page was not allowed to run insecure content from http://ougg9cexh.bkt.clouddn.com/grab_title.js.晚顷,所以建議使用https的腳本URL

<br />

通過腳本注入來修改網(wǎng)頁

舉個例子,我加載一篇網(wǎng)頁文章疗疟,文章默認是Helvetica字體该默,排版比較規(guī)范,但是我希望實際顯示成menlo字體策彤,看起來更符合程序員的喜好栓袖,那么可以通過js腳本注入的方法,在網(wǎng)頁載入前店诗,改變CSS的樣式裹刮。腳本的格式如下:

var styleElement = document.createElement('style');
document.documentElement.appendChild(styleElement);
styleElement.textContent = 'body { font-family : menlo !important; };';

意思是通過js添加一個style標簽,來設置body的字體為menlo庞瘸,并且設置了高優(yōu)先級捧弃。如果執(zhí)行的話,下面這段結構就會加入到網(wǎng)頁元素中去擦囊。

<style> 
body {
    font-family : menlo !important;
};
</style>

有了腳本以后违霞,創(chuàng)建WKUserScript等來實現(xiàn)注入,這里就不重復了瞬场,但是需要注意:既然是修改元素內(nèi)容买鸽,那么注入時機就得等到所有元素標簽加載完成后,即WKUserScriptInjectionTimeAtDocumentEnd

運行后贯被,不出別的問題的話癞谒,網(wǎng)頁上的字體就被修改了。

總結

以上分享了一些js向原生app交互的方式刃榨,總結起來可以這么說:

  • js腳本可以保存在app中弹砚,還可以通過遠程下載,或URL鏈接的形式獲取
  • js腳本可以通過evaluate方法執(zhí)行枢希,也可以首先被注入在頁面中桌吃,等需要的時候在執(zhí)行
  • js腳本帶著參數(shù)調(diào)用原生app時,可以等著跳轉請求時被app截獲苞轿,可以通過函數(shù)返回值直接給到結果茅诱,也可以通過發(fā)送消息機制;根據(jù)不同的需求制定出不同方案就好
  • js腳本在服務器端更新后搬卒,如果app使用URL的方式讀取腳本瑟俭,需要避免讀取本地的緩存結果

最后貼個demo地址

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末契邀,一起剝皮案震驚了整個濱河市摆寄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖微饥,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逗扒,死亡現(xiàn)場離奇詭異,居然都是意外死亡欠橘,警方通過查閱死者的電腦和手機矩肩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肃续,“玉大人黍檩,你說我怎么就攤上這事∈济” “怎么了刽酱?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長疼蛾。 經(jīng)常有香客問我肛跌,道長,這世上最難降的妖魔是什么察郁? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任衍慎,我火速辦了婚禮,結果婚禮上皮钠,老公的妹妹穿的比我還像新娘稳捆。我一直安慰自己,他們只是感情好麦轰,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布乔夯。 她就那樣靜靜地躺著,像睡著了一般款侵。 火紅的嫁衣襯著肌膚如雪末荐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天新锈,我揣著相機與錄音甲脏,去河邊找鬼。 笑死妹笆,一個胖子當著我的面吹牛块请,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拳缠,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼墩新,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了窟坐?” 一聲冷哼從身側響起海渊,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绵疲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后切省,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體最岗,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡帕胆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年朝捆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片懒豹。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡芙盘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出脸秽,到底是詐尸還是另有隱情儒老,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布记餐,位于F島的核電站驮樊,受9級特大地震影響,放射性物質發(fā)生泄漏片酝。R本人自食惡果不足惜囚衔,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雕沿。 院中可真熱鬧练湿,春花似錦、人聲如沸审轮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疾渣。三九已至篡诽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間榴捡,已是汗流浹背杈女。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留薄疚,地道東北人碧信。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像街夭,于是被迫代替她去往敵國和親砰碴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言 關于UIWebView的介紹猖辫,相信看過上文的小伙伴們酥泞,已經(jīng)大概清楚了吧,如果有問題啃憎,歡迎提問芝囤。 本文是本系列...
    Dark_Angel閱讀 28,882評論 67 291
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,163評論 25 707
  • 通過學習,你將會學習以下幾個方面的內(nèi)容: **什么是WKWebView以及它和UIWebView的區(qū)別是什么 **...
    SOI閱讀 11,634評論 18 42
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫辛萍、插件悯姊、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,105評論 4 62
  • 你才能主宰自己的人生,干嘛要那么流氓的說別人沒有給自己光鮮的舒適區(qū)贩毕,嫁禍于人悯许。別人也有自己的生活,與你何干辉阶。即便...
    羽上驚鴻閱讀 354評論 3 1