WKWebView進階使用 - JS交互(一)

WKWebView基礎(chǔ)

WKWebView的優(yōu)勢

1、更多的支持HTML5的特性
2溺蕉、官方宣稱的高達60fps的滾動刷新率以及內(nèi)置手勢
3、將UIWebViewDelegate與UIWebView拆分成了14類與3個協(xié)議,以前很多不方便實現(xiàn)的功能得以實現(xiàn)观蓄。官方文檔說明
4僵腺、Safari相同的JavaScript引擎
5、占用更少的內(nèi)存
6、增加加載進度屬性:estimatedProgress

基本使用方法

WKWebView有兩個代理delegate,WKUIDelegateWKNavigationDelegate结胀。

WKNavigationDelegate

WKNavigationDelegate主要處理一些跳轉(zhuǎn)赞咙、加載處理操作

WKUIDelegate

WKUIDelegate主要處理JS腳本,確認框糟港,警告框等攀操。

- (void)viewDidLoad {
    [super viewDidLoad];
    webView = [[WKWebView alloc]init];
    [self.view addSubview:webView];
    [webView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.view);
        make.right.equalTo(self.view);
        make.top.equalTo(self.view);
        make.bottom.equalTo(self.view);
    }];
    webView.UIDelegate = self;
    webView.navigationDelegate = self;
    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];
}

加載本地的HTML文件

 /*
     參數(shù)1:index 是要打開的html的名稱
     參數(shù)2:html 是index的后綴名
     參數(shù)3:HtmlFile/app/index 是文件夾的路徑
 */
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"html" inDirectory:@"html"];
NSURL *pathURL = [NSURL fileURLWithPath:filePath];
[_webView loadRequest:[NSURLRequest requestWithURL:pathURL]];  
#pragma mark- WKNavigationDelegate
/*
 WKNavigationDelegate主要處理一些跳轉(zhuǎn)、加載處理操作
 */

//頁面開始加載時調(diào)用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
    NSLog(@"----頁面開始加載");
}

//當(dāng)內(nèi)容開始返回時調(diào)用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
    NSLog(@"----頁面返回內(nèi)容");
}

//頁面加載完成時調(diào)用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    NSLog(@"----頁面加載完成");
}

//頁面加載失敗時調(diào)用
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    NSLog(@"----頁面加載失敗");
}

//接收到服務(wù)器跳轉(zhuǎn)請求之后調(diào)用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
    
    NSLog(@"----接收到服務(wù)器跳轉(zhuǎn)請求之后調(diào)用");
}

//在收到響應(yīng)后秸抚,決定是否跳轉(zhuǎn)
-(void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
     NSLog(@"%@",navigationResponse.response.URL.absoluteString);
    //允許跳轉(zhuǎn)
    decisionHandler(WKNavigationResponsePolicyAllow);
    //不允許跳轉(zhuǎn)
    //decisionHandler(WKNavigationResponsePolicyCancel);
}


//在發(fā)送請求之前速和,決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSLog(@"%@",navigationAction.request.URL.absoluteString);
    //允許跳轉(zhuǎn)
    decisionHandler(WKNavigationActionPolicyAllow);
    //不允許跳轉(zhuǎn)
    //decisionHandler(WKNavigationActionPolicyCancel);
}

#pragma mark- WKUIDelegate

// WKUIDelegate是web界面中有彈出警告框時調(diào)用這個代理方法,主要是用來處理使用系統(tǒng)的彈框來替換JS中的一些彈框的,比如: 警告框, 選擇框, 輸入框等
/**
 webView中彈出警告框時調(diào)用, 只能有一個按鈕
 @param webView webView
 @param message 提示信息
 @param frame 可用于區(qū)分哪個窗口調(diào)用的
 @param completionHandler 警告框消失的時候調(diào)用, 回調(diào)給JS
 */

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
  
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"溫馨提示" message:message preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleCancel handler:nil]];
    [self presentViewController:alert animated:YES completion:nil];
    completionHandler();
    
}

/** 對應(yīng)js的confirm方法
 webView中彈出選擇框時調(diào)用, 兩個按鈕
 @param webView webView description
 @param message 提示信息
 @param frame 可用于區(qū)分哪個窗口調(diào)用的
 @param completionHandler 確認框消失的時候調(diào)用, 回調(diào)給JS, 參數(shù)為選擇結(jié)果: YES or NO
 */

- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler {
  
}

// JavaScript調(diào)用prompt方法后回調(diào)的方法 prompt是js中的輸入框 需要在block中把用戶輸入的信息傳入

/** 對應(yīng)js的prompt方法
 webView中彈出輸入框時調(diào)用, 兩個按鈕 和 一個輸入框
 
 @param webView webView description
 @param prompt 提示信息
 @param defaultText 默認提示文本
 @param frame 可用于區(qū)分哪個窗口調(diào)用的
 @param completionHandler 輸入框消失的時候調(diào)用, 回調(diào)給JS, 參數(shù)為輸入的內(nèi)容
 */

- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler {
   
       UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"請輸入" message:prompt preferredStyle:(UIAlertControllerStyleAlert)];
       [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
       textField.placeholder = @"請輸入";
       }];
       UIAlertAction *ok = [UIAlertAction actionWithTitle:@"確定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
       UITextField *tf = [alert.textFields firstObject];
       completionHandler(tf.text);
           
       }];
       UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
       completionHandler(defaultText);
       }];
       [alert addAction:ok];
       [alert addAction:cancel];
       [self presentViewController:alert animated:YES completion:nil];
}

OC與JS交互

JS 調(diào)取OC

方案1: WKUserContentController注冊方法監(jiān)聽

js是例代碼如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title></title>
</head>
<body>
        <table width="80%" align="center" border="0">
            <tr>
                <td width="30%" align="right">姓名:</td>
                <td align="left">
                    <input type="text" name="username">
                </td>
            </tr>
            <tr>
                <td width="30%" align="right">密碼:</td>
                <td align="left">
                    <input type="text" name="password" >
                </td>
            </tr>
            <tr >
                 <td align="right">
                    <input type="button" name="submit" value="提交" onclick="jsCallNativeMethod()">
                </td>
                 <td align="left">
                    <input type="button" name="login" value="登錄" onclick="bf_jsCallNativeMethod()">
                </td>
                <td></td>
           </tr>
        </table>
        <script type="text/javascript">
            function  jsCallNativeMethod(){
                location.href = "js_native://alert";
                
            }
            function nativeCallbackJscMothod(arguments) {
                alert('原生調(diào)用js方法 傳來的參數(shù) = ' + arguments);
            }   
            function bf_jsCallNativeMethod() {
                window.webkit.messageHandlers.bf_jsCallNativeMethod.postMessage('我的參數(shù)222');
            }     
            function add(a , b) {
                return a + b
            }           
        </script>
</body>
</html>

OC代碼如下:

#pragma mark- 懶加載
/*
* WKWebViewConfiguration用來初始化WKWebView的配置。
* WKPreferences配置webView能否使用JS或者其他插件等
* WKUserContentController用來配置JS交互的代碼
* UIDelegate用來控制WKWebView中一些彈窗的顯示(alert剥汤、confirm颠放、prompt)。
* WKNavigationDelegate用來監(jiān)聽網(wǎng)頁的加載情況吭敢,包括是否允許加載碰凶,加載失敗、成功加載等一些列代理方法鹿驼。
*/
- (WKWebView *)webView {
    if (!_webView) {
        //網(wǎng)頁配置文件
        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
        //允許與網(wǎng)頁交互
        configuration.selectionGranularity = YES;
    
        WKPreferences *preferences = [WKPreferences new];
        preferences.javaScriptCanOpenWindowsAutomatically = YES;
        preferences.minimumFontSize = 50;
        configuration.preferences = preferences;
        
        //注冊方法
        WKUserContentController *userContentController = [[WKUserContentController alloc]init];
        //注冊一個name為bf_jsCallNativeMethod的js方法(要記的remove)
        [userContentController addScriptMessageHandler:self name:@"bf_jsCallNativeMethod"];
        
        configuration.userContentController = userContentController;
        
        _webView = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:configuration];
        _webView.navigationDelegate = self;
        _webView.UIDelegate = self;
    }
    return _webView;
}

#pragma mark - WKScriptMessageHandler   

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {

    if ([message.name isEqualToString:@"bf_jsCallNativeMethod"]) {
        NSLog(@"這個是傳過來的參數(shù)%@",message.body);     
        [self.webView evaluateJavaScript:@"nativeCallbackJscMothod('6666')" completionHandler:^(id _Nullable x, NSError * _Nullable error) {
            NSLog(@"執(zhí)行完的x==%@,error = %@",x,error.localizedDescription);
        }];
        
    }
}

/*要記得銷毀是移除*/
- (void)dealloc
{
    NSLog(@"--delloc--");
    //這里需要注意欲低,前面增加過的方法一定要remove掉
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"bf_jsCallNativeMethod"];
}

上面的OC代碼如果認證測試一下就會發(fā)現(xiàn)dealloc并不會執(zhí)行,這樣肯定是不行的畜晰,會造成內(nèi)存泄漏砾莱。原因是[userContentController addScriptMessageHandler:self name:@"bf_jsCallNativeMethod"];這句代碼造成無法釋放內(nèi)存。(PS:我也想到NSTimer中遇到的循環(huán)引用問題凄鼻,之前總結(jié)的文章使用Weak指針還是不能釋放)

改進方案

用一個新的controller來處理,新的controller再繞用delegate繞回來腊瑟。

BFWKDelegateController

#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>

NS_ASSUME_NONNULL_BEGIN

@protocol BFWKDelegate <NSObject>

-(void)bf_userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message;

@end

@interface BFWKDelegateController : UIViewController <WKScriptMessageHandler>

@property (weak , nonatomic) id<BFWKDelegate> delegate;

@end

NS_ASSUME_NONNULL_END


#import "BFWKDelegateController.h"

@interface BFWKDelegateController ()
@end

@implementation BFWKDelegateController

- (void)viewDidLoad {
    [super viewDidLoad];
  
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if ([self.delegate respondsToSelector:@selector(bf_userContentController:didReceiveScriptMessage:)]) {
        [self.delegate bf_userContentController:userContentController didReceiveScriptMessage:message];
    }
}
@end



- (WKWebView *)webView {
    if (!_webView) {
        //網(wǎng)頁配置文件
        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
        //允許與網(wǎng)頁交互
        configuration.selectionGranularity = YES;

        WKPreferences *preferences = [WKPreferences new];
        preferences.javaScriptCanOpenWindowsAutomatically = YES;
        preferences.minimumFontSize = 50;
        configuration.preferences = preferences;
        //中間的Delegate
        BFWKDelegateController *bfDelegateController = [[BFWKDelegateController alloc]init];
        bfDelegateController.delegate = self;
        
        //注冊方法
        WKUserContentController *userContentController = [[WKUserContentController alloc]init];
        //注冊一個name為bf_jsCallNativeMethod的js方法
        [userContentController addScriptMessageHandler:bfDelegateController name:@"bf_jsCallNativeMethod"];

        configuration.userContentController = userContentController;

        _webView = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:configuration];
        _webView.navigationDelegate = self;
        _webView.UIDelegate = self;
    }
    return _webView;
}

/*要記得銷毀是移除*/
- (void)dealloc
{
    NSLog(@"--delloc--");
    //這里需要注意,前面增加過的方法一定要remove掉
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"bf_jsCallNativeMethod"];
}


#pragma mark - 中轉(zhuǎn)的Delegate
- (void)bf_userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
   
    NSLog(@"name:%@ body:%@ frameInfo:%@",message.name,message.body,message.frameInfo);
}

在運行一下块蚌,當(dāng)前頁面銷毀的時候可以執(zhí)行到dealloc代碼啦

注意點:
1闰非、addScriptMessageHandler要和removeScriptMessageHandlerForName配套出現(xiàn),否則會造成內(nèi)存泄漏匈子。
2河胎、h5只能傳一個參數(shù),如果需要多個參數(shù)就需要用字典或者json組裝虎敦。

方案2:通過攔截WKNavigationDelegate代理

通過攔截WKNavigationDelegate的代理方法游岳,然后匹配目標(biāo)字符串

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(**void** (^)(WKNavigationActionPolicy))decisionHandler;

具體實例代碼如下:通過匹配是否是以js_native://alert開頭的字符串

//在發(fā)送請求之前,決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    
    NSURL *url = navigationAction.request.URL;
    if ([[url absoluteString] hasSuffix:@"js_native://alert"]) {
        [self handleJSMessage];
        //不允許跳轉(zhuǎn)
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    //允許跳轉(zhuǎn)
    decisionHandler(WKNavigationActionPolicyAllow);
    
}

#pragma mark - Private

-(void)handleJSMessage {
    [_webView evaluateJavaScript:@"nativeCallbackJscMothod('123')" completionHandler:^(id _Nullable x, NSError * _Nullable error) {
        NSLog(@"x = %@, error = %@", x, error.localizedDescription);
    }];
}

方案3:蘋果原生API:JavaScriptCore (iOS7.0+ 使用)

主要的兩個類:

  • JSContext : JS上下文(運行環(huán)境)其徙,可用對象方法去執(zhí)行JS代碼(evaluateScript)胚迫,可通過上下文對象去獲取JS里的數(shù)據(jù)(上下文對象[key]),并用JSValue對象接收
  • JSValue : 用于接收JSContext對象獲取的數(shù)據(jù)唾那,可以是任意對象访锻,方法。

實例代碼如下:

    JSContext *jsContent = [[JSContext alloc]init];
    jsContent[@"add"] = ^(int a, int b){
        NSLog(@"a+b = %d",a + b);
    };
    [jsContent evaluateScript:@"add(20,30)"];

注意:js代碼要先被執(zhí)行,才能通過上下文獲取

說明:JSValue對其對應(yīng)的JS值和其所屬的JSContext對象都是強引用的關(guān)系期犬。因為JSValue需要這兩個東西來執(zhí)行JS代碼河哑,所以JSValue會一直持有著它們。
所以龟虎,在用block時璃谨,需考慮循環(huán)引用問題

  • 不要在Block中直接使用JSValue:建議把JSValue當(dāng)做參數(shù)傳到Block中,而不是直接在Block內(nèi)部使用鲤妥,這樣Block就不會強引用JSValue了
  • 不要在Block中直接使用JSContext:可以使用[JSContext currentContext] 方法來獲取當(dāng)前的Context
    1佳吞、block方式:

用block定義js函數(shù),并執(zhí)行(OC調(diào)用執(zhí)行js棉安,而js是調(diào)用的oc block)

- (void)jsCallOCBlock
{

JSContext *ctx = [[JSContext alloc] init];

//OC中NSBlock對應(yīng)js中Function object
ctx[@"goto"] = ^(NSString *parmStr){

   //block內(nèi)不要直接使用ctx底扳,會循環(huán)引用(ctx已經(jīng)引用block),若外部有JSValue贡耽,也不能在block內(nèi)直接調(diào)用(JSValue強持有了ctx )

    //獲取JS調(diào)用參數(shù)
    NSLog(@"parmStr:%@",parmStr);

    //可以直接獲取所有參數(shù)
    NSArray *arguments = [JSContext currentArguments];
    NSLog(@"%@",arguments[0]);
};

//JS執(zhí)行代碼,調(diào)用goto方法衷模,并傳入?yún)?shù)school
NSString *jsCode = @"goto('school')";

//執(zhí)行
[ctx evaluateScript:jsCode];
}

2、JSExport 協(xié)議
需要在JS中生成OC對應(yīng)的類菇爪,然后再通過JS調(diào)用算芯。

方法:
通過自定義一個遵循JSExport的協(xié)議柒昏,把需要被JS訪問的OC類中的屬性凳宙,方法暴露給JS使用

步驟:
1、自定義協(xié)議职祷,協(xié)議遵循JSExport的協(xié)議氏涩,協(xié)議中的屬性和方法就是OC對象要暴露給JS,讓JS可以直接調(diào)用

#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>

@protocol PersonJSExport <JSExport>

@property (nonatomic, strong) NSString *address;

//無參數(shù)方法
- (void)play;

//因為JS函數(shù)命名規(guī)則和OC規(guī)則不一樣有梆,所以當(dāng)有多個參數(shù)時是尖,可以使用OC提供了一個宏*JSExportAs*,指定JS應(yīng)該生成什么樣的函數(shù)來對應(yīng)OC的方法泥耀。
//不使用JSExportAs指定關(guān)聯(lián)也可以正常調(diào)用饺汹,后面直接接多個參數(shù)

//多參數(shù)方法

//不指定關(guān)聯(lián)
- (void)play:(NSString *)address time:(NSString *)time;
//指定關(guān)聯(lián)
JSExportAs(gotoSchool, - (void)goToSchoolWithSchoolName:(NSString *)name address:(NSString *)address);

@end

2、自定義一個遵循第一步創(chuàng)建的協(xié)議的OC對象痰催,實現(xiàn)協(xié)議的方法

例如:Person對象

3兜辞、JS調(diào)用對象協(xié)議聲明的方法

- (void)jsCallOCClass
{
// 創(chuàng)建Person對象,Person對象必須遵守JSExport協(xié)議
Person *p = [[Person alloc] init];
p.address = @"aaa";

JSContext *ctx = [[JSContext alloc] init];
// 在JS中生成Person對象person,并且擁有p內(nèi)部的值
ctx[@"person"] = p;

// 執(zhí)行JS代碼
//NSString *jsCode = @"person.play()";
NSString *jsCode = @"person.play('北京天安門','now')";
//NSString *jsCode = @"person.gotoSchool('實驗中學(xué)','廣州')";

[ctx evaluateScript:jsCode];
}

另外夸溶,若要調(diào)用OC系統(tǒng)的類逸吵,例如UIView
需要同樣創(chuàng)建協(xié)議缝裁,只是在第三步用runtime給系統(tǒng)類UIView添加創(chuàng)建的協(xié)議
class_addProtocol([UIView class], @protocol(UIViewlJSExport));

方案4: 三方庫 WebViewJavaScripteBridge

OC 調(diào)取JS

方案1: WKWebView直接執(zhí)行 evaluateJavaScript 方法

//原生回調(diào)JS
[self.webView evaluateJavaScript:@"nativeCallbackJscMothod('%@')" completionHandler:^(**id** **_Nullable** s, NSError * **_Nullable** error) {
      NSLog(@"執(zhí)行完成啦");
}];

方案2:蘋果原生API:JavaScriptCore (iOS7.0+ 使用)

oc獲取js變量,注:js代碼要先被執(zhí)行韩脑,才能通過上下文獲取

- (void)ocGetJSVar
{
//定義JS代碼
NSString *jsCode = @"var a = 'a'";

//創(chuàng)建JS運行環(huán)境
JSContext *ctx = [[JSContext alloc] init];

//!!!:執(zhí)行JS代碼---先執(zhí)行,后面才能獲取
[ctx evaluateScript:jsCode];

//獲取變量
JSValue *value = ctx[**@“a"**];

//JSValue轉(zhuǎn)NSString
NSString *valueStr = value.toString;

//打印結(jié)果:a
NSLog(@"%@",valueStr);
}

oc調(diào)用js方法允懂,并獲取返回結(jié)果

- (void)ocCallJSFunc
{
NSString *jsCode = @"function say(str){"
" return str; "
"}";

// 創(chuàng)建JS運行環(huán)境
JSContext *ctx = [[JSContext alloc] init];

// 執(zhí)行JS代碼
[ctx evaluateScript:jsCode];

//!!!:執(zhí)行JS代碼---先執(zhí)行衩匣,后面才能獲取
JSValue *say = ctx[@"say"];

// OC調(diào)用JS方法,獲取方法返回值
JSValue *result = [say callWithArguments:@[@"hello world!"]];

// 打印結(jié)果:hello world!
NSLog(@"%@",result);
}

方案3:三方庫 WebViewJavaScripteBridge

參考:
JavaScriptCore深入淺出
OC和JS調(diào)用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末生百,一起剝皮案震驚了整個濱河市柄延,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌搜吧,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摆昧,死亡現(xiàn)場離奇詭異蜒程,居然都是意外死亡,警方通過查閱死者的電腦和手機忌锯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門领炫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來偶垮,“玉大人帝洪,你說我怎么就攤上這事〉” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵频祝,是天一觀的道長泌参。 經(jīng)常有香客問我常空,道長,這世上最難降的妖魔是什么漓糙? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮昆禽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捡硅。我一直安慰自己盗棵,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布喷屋。 她就那樣靜靜地躺著瞭恰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪是牢。 梳的紋絲不亂的頭發(fā)上陕截,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天批什,我揣著相機與錄音,去河邊找鬼乳规。 笑死合呐,一個胖子當(dāng)著我的面吹牛暮的,可吹牛的內(nèi)容都是我干的淌实。 我是一名探鬼主播猖腕,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼恨闪!你這毒婦竟也來了倘感?” 一聲冷哼從身側(cè)響起咙咽,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤钧敞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后溉苛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡炊昆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了视乐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敢茁。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡佑淀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出彰檬,到底是詐尸還是另有隱情,我是刑警寧澤逢倍,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布较雕,位于F島的核電站,受9級特大地震影響扣典,放射性物質(zhì)發(fā)生泄漏慎玖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望图柏。 院中可真熱鬧,春花似錦裁着、人聲如沸扔罪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至棘捣,卻和暖如春评疗,著一層夾襖步出監(jiān)牢的瞬間茵烈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工胧华, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宙彪,地道東北人有巧。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像篮迎,于是被迫代替她去往敵國和親示姿。 傳聞我的和親對象是個殘疾皇子逊笆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355

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

  • 原創(chuàng):知識進階型文章無私奉獻难裆,為國為民,創(chuàng)作不易褂痰,請珍惜症虑,之后會持續(xù)更新,不斷完善個人比較喜歡做筆記和寫總結(jié)谍憔,畢竟...
    時光啊混蛋_97boy閱讀 2,319評論 0 7
  • 隨著H5技術(shù)的興起,在iOS開發(fā)過程中骗污,難免會遇到原生應(yīng)用需要和H5頁面交互的問題沈条。其中會涉及方法調(diào)用及參數(shù)傳值等...
    Chris_js閱讀 3,075評論 1 8
  • WKWebView需要iOS9或更高版本 優(yōu)點1.多進程,在app的主進程之外執(zhí)行2.使用更快的Nitro Jav...
    小瓶子Zgp閱讀 2,009評論 0 3
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹 對...
    cosWriter閱讀 11,103評論 1 32
  • 跟原生開發(fā)相比,H5的開發(fā)相對來一個成熟的框架和團隊來講在開發(fā)速度和開發(fā)效率上有著比原生很大的優(yōu)勢月而,至少不用等待審...
    大沖哥閱讀 1,846評論 0 7