WKWebView进阶使用 - JS交互(一)

WKWebView基础

WKWebView的优势

1、更多的支持HTML5的特性
2、官方宣称的高达60fps的滚动刷新率以及内置手势
3、将UIWebViewDelegate与UIWebView拆分成了14类与3个协议,以前很多不方便实现的功能得以实现。官方文档说明
4、Safari相同的JavaScript引擎
5、占用更少的内存
6、增加加载进度属性:estimatedProgress

基本使用方法

WKWebView有两个代理delegate,WKUIDelegateWKNavigationDelegate

WKNavigationDelegate

WKNavigationDelegate主要处理一些跳转、加载处理操作

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文件

 /*
     参数1:index 是要打开的html的名称
     参数2:html 是index的后缀名
     参数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主要处理一些跳转、加载处理操作
 */

//页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
    NSLog(@"----页面开始加载");
}

//当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
    NSLog(@"----页面返回内容");
}

//页面加载完成时调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    NSLog(@"----页面加载完成");
}

//页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    NSLog(@"----页面加载失败");
}

//接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
    
    NSLog(@"----接收到服务器跳转请求之后调用");
}

//在收到响应后,决定是否跳转
-(void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
     NSLog(@"%@",navigationResponse.response.URL.absoluteString);
    //允许跳转
    decisionHandler(WKNavigationResponsePolicyAllow);
    //不允许跳转
    //decisionHandler(WKNavigationResponsePolicyCancel);
}


//在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSLog(@"%@",navigationAction.request.URL.absoluteString);
    //允许跳转
    decisionHandler(WKNavigationActionPolicyAllow);
    //不允许跳转
    //decisionHandler(WKNavigationActionPolicyCancel);
}

#pragma mark- WKUIDelegate

// WKUIDelegate是web界面中有弹出警告框时调用这个代理方法,主要是用来处理使用系统的弹框来替换JS中的一些弹框的,比如: 警告框, 选择框, 输入框等
/**
 webView中弹出警告框时调用, 只能有一个按钮
 @param webView webView
 @param message 提示信息
 @param frame 可用于区分哪个窗口调用的
 @param completionHandler 警告框消失的时候调用, 回调给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();
    
}

/** 对应js的confirm方法
 webView中弹出选择框时调用, 两个按钮
 @param webView webView description
 @param message 提示信息
 @param frame 可用于区分哪个窗口调用的
 @param completionHandler 确认框消失的时候调用, 回调给JS, 参数为选择结果: YES or NO
 */

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

// JavaScript调用prompt方法后回调的方法 prompt是js中的输入框 需要在block中把用户输入的信息传入

/** 对应js的prompt方法
 webView中弹出输入框时调用, 两个按钮 和 一个输入框
 
 @param webView webView description
 @param prompt 提示信息
 @param defaultText 默认提示文本
 @param frame 可用于区分哪个窗口调用的
 @param completionHandler 输入框消失的时候调用, 回调给JS, 参数为输入的内容
 */

- (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 调取OC

方案1: WKUserContentController注册方法监听

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('原生调用js方法 传来的参数 = ' + arguments);
            }   
            function bf_jsCallNativeMethod() {
                window.webkit.messageHandlers.bf_jsCallNativeMethod.postMessage('我的参数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用来监听网页的加载情况,包括是否允许加载,加载失败、成功加载等一些列代理方法。
*/
- (WKWebView *)webView {
    if (!_webView) {
        //网页配置文件
        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
        //允许与网页交互
        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(@"这个是传过来的参数%@",message.body);     
        [self.webView evaluateJavaScript:@"nativeCallbackJscMothod('6666')" completionHandler:^(id _Nullable x, NSError * _Nullable error) {
            NSLog(@"执行完的x==%@,error = %@",x,error.localizedDescription);
        }];
        
    }
}

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

上面的OC代码如果认证测试一下就会发现dealloc并不会执行,这样肯定是不行的,会造成内存泄漏。原因是[userContentController addScriptMessageHandler:self name:@"bf_jsCallNativeMethod"];这句代码造成无法释放内存。(PS:我也想到NSTimer中遇到的循环引用问题,之前总结的文章使用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) {
        //网页配置文件
        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
        //允许与网页交互
        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 - 中转的Delegate
- (void)bf_userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
   
    NSLog(@"name:%@ body:%@ frameInfo:%@",message.name,message.body,message.frameInfo);
}

在运行一下,当前页面销毁的时候可以执行到dealloc代码啦

注意点:
1、addScriptMessageHandler要和removeScriptMessageHandlerForName配套出现,否则会造成内存泄漏。
2、h5只能传一个参数,如果需要多个参数就需要用字典或者json组装。

方案2:通过拦截WKNavigationDelegate代理

通过拦截WKNavigationDelegate的代理方法,然后匹配目标字符串

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

具体实例代码如下:通过匹配是否是以js_native://alert开头的字符串

//在发送请求之前,决定是否跳转
- (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];
        //不允许跳转
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    //允许跳转
    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上下文(运行环境),可用对象方法去执行JS代码(evaluateScript),可通过上下文对象去获取JS里的数据(上下文对象[key]),并用JSValue对象接收
  • JSValue : 用于接收JSContext对象获取的数据,可以是任意对象,方法。

实例代码如下:

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

注意:js代码要先被执行,才能通过上下文获取

说明:JSValue对其对应的JS值和其所属的JSContext对象都是强引用的关系。因为JSValue需要这两个东西来执行JS代码,所以JSValue会一直持有着它们。
所以,在用block时,需考虑循环引用问题

  • 不要在Block中直接使用JSValue:建议把JSValue当做参数传到Block中,而不是直接在Block内部使用,这样Block就不会强引用JSValue了
  • 不要在Block中直接使用JSContext:可以使用[JSContext currentContext] 方法来获取当前的Context
    1、block方式:

用block定义js函数,并执行(OC调用执行js,而js是调用的oc block)

- (void)jsCallOCBlock
{

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

//OC中NSBlock对应js中Function object
ctx[@"goto"] = ^(NSString *parmStr){

   //block内不要直接使用ctx,会循环引用(ctx已经引用block),若外部有JSValue,也不能在block内直接调用(JSValue强持有了ctx )

    //获取JS调用参数
    NSLog(@"parmStr:%@",parmStr);

    //可以直接获取所有参数
    NSArray *arguments = [JSContext currentArguments];
    NSLog(@"%@",arguments[0]);
};

//JS执行代码,调用goto方法,并传入参数school
NSString *jsCode = @"goto('school')";

//执行
[ctx evaluateScript:jsCode];
}

2、JSExport 协议
需要在JS中生成OC对应的类,然后再通过JS调用。

方法:
通过自定义一个遵循JSExport的协议,把需要被JS访问的OC类中的属性,方法暴露给JS使用

步骤:
1、自定义协议,协议遵循JSExport的协议,协议中的属性和方法就是OC对象要暴露给JS,让JS可以直接调用

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

@protocol PersonJSExport <JSExport>

@property (nonatomic, strong) NSString *address;

//无参数方法
- (void)play;

//因为JS函数命名规则和OC规则不一样,所以当有多个参数时,可以使用OC提供了一个宏*JSExportAs*,指定JS应该生成什么样的函数来对应OC的方法。
//不使用JSExportAs指定关联也可以正常调用,后面直接接多个参数

//多参数方法

//不指定关联
- (void)play:(NSString *)address time:(NSString *)time;
//指定关联
JSExportAs(gotoSchool, - (void)goToSchoolWithSchoolName:(NSString *)name address:(NSString *)address);

@end

2、自定义一个遵循第一步创建的协议的OC对象,实现协议的方法

例如:Person对象

3、JS调用对象协议声明的方法

- (void)jsCallOCClass
{
// 创建Person对象,Person对象必须遵守JSExport协议
Person *p = [[Person alloc] init];
p.address = @"aaa";

JSContext *ctx = [[JSContext alloc] init];
// 在JS中生成Person对象person,并且拥有p内部的值
ctx[@"person"] = p;

// 执行JS代码
//NSString *jsCode = @"person.play()";
NSString *jsCode = @"person.play('北京天安门','now')";
//NSString *jsCode = @"person.gotoSchool('实验中学','广州')";

[ctx evaluateScript:jsCode];
}

另外,若要调用OC系统的类,例如UIView
需要同样创建协议,只是在第三步用runtime给系统类UIView添加创建的协议
class_addProtocol([UIView class], @protocol(UIViewlJSExport));

方案4: 三方库 WebViewJavaScripteBridge

OC 调取JS

方案1: WKWebView直接执行 evaluateJavaScript 方法

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

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

oc获取js变量,注:js代码要先被执行,才能通过上下文获取

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

//创建JS运行环境
JSContext *ctx = [[JSContext alloc] init];

//!!!:执行JS代码---先执行,后面才能获取
[ctx evaluateScript:jsCode];

//获取变量
JSValue *value = ctx[**@“a"**];

//JSValue转NSString
NSString *valueStr = value.toString;

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

oc调用js方法,并获取返回结果

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

// 创建JS运行环境
JSContext *ctx = [[JSContext alloc] init];

// 执行JS代码
[ctx evaluateScript:jsCode];

//!!!:执行JS代码---先执行,后面才能获取
JSValue *say = ctx[@"say"];

// OC调用JS方法,获取方法返回值
JSValue *result = [say callWithArguments:@[@"hello world!"]];

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

方案3:三方库 WebViewJavaScripteBridge

参考:
JavaScriptCore深入浅出
OC和JS调用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容

  • 原创:知识进阶型文章无私奉献,为国为民,创作不易,请珍惜,之后会持续更新,不断完善个人比较喜欢做笔记和写总结,毕竟...
    时光啊混蛋_97boy阅读 2,088评论 0 6
  • 随着H5技术的兴起,在iOS开发过程中,难免会遇到原生应用需要和H5页面交互的问题。其中会涉及方法调用及参数传值等...
    Chris_js阅读 2,939评论 1 8
  • WKWebView需要iOS9或更高版本 优点1.多进程,在app的主进程之外执行2.使用更快的Nitro Jav...
    小瓶子Zgp阅读 1,871评论 0 3
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,037评论 1 32
  • 跟原生开发相比,H5的开发相对来一个成熟的框架和团队来讲在开发速度和开发效率上有着比原生很大的优势,至少不用等待审...
    大冲哥阅读 1,755评论 0 7