iOS开发——网络篇——UIWebview基本使用,NSInvocation(封装类),NSMethodSignature(签名),JavaScript,抛异常,消除警告

一、UIWebView简介


1、UIWebView
什么是UIWebView
UIWebView是iOS内置的浏览器控件
系统自带的Safari浏览器就是通过UIWebView实现的

UIWebView不但能加载远程的网页资源,还能加载绝大部分的常见文件
html\htm
pdf、doc、ppt、txt
mp4
… …

UIWebView常用的加载资源的方法
- (void)loadRequest:(NSURLRequest *)request;


2、键盘工具条显示中文

 

二、常用属性和方法


重新加载(刷新)
- (void)reload;

停止加载
- (void)stopLoading;

回退
- (void)goBack;

前进
- (void)goForward;

需要进行检测的数据类型
@property(nonatomic) UIDataDetectorTypes dataDetectorTypes


是否能回退
@property(nonatomic,readonly,getter=canGoBack) BOOL canGoBack;

是否能前进
@property(nonatomic,readonly,getter=canGoForward) BOOL canGoForward;

是否正在加载中
@property(nonatomic,readonly,getter=isLoading) BOOL loading;

是否伸缩内容至适应屏幕当前尺寸
@property(nonatomic) BOOL scalesPageToFit;


三、代理


监听UIWebView的加载过程
成为UIWebView的代理,遵守UIWebViewDelegate协议,就能监听UIWebView的加载过程
开始发送请求(加载数据)时调用这个方法
- (void)webViewDidStartLoad:(UIWebView *)webView;

请求完毕(加载数据完毕)时调用这个方法
- (void)webViewDidFinishLoad:(UIWebView *)webView;

请求错误时调用这个方法
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;


UIWebView在发送请求之前,都会调用这个方法,如果返回NO,代表停止加载请求,返回YES,代表允许加载请求

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;

 

四、NSInvocation,NSMethodSignature(签名)

//    Signature签名: 在创建NSInvocation的时候, 必须传递一个签名对象
//    签名对象的作用 : 用于获取参数的个数和方法的返回值
    // 注意点: 创建签名对象的时候不是使用NSMethodSignature类创建 \
    而是方法属于谁就用谁来创建
    NSMethodSignature *signature = [ViewController instanceMethodSignatureForSelector:@selector(sendMessageWithNumber:andContent:status:)];
    
//    NSInvocation; 用来包装方法和对应的对象, 它可以存储方法的名称,对应的对象 ,对应的参数
    // 1.创建一个NSInvocation对象
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    
    invocation.target = self; // 保存方法所属的对象
    // 给invocation设置的方法, 必须和签名中的方法一致
    invocation.selector = @selector(sendMessageWithNumber:andContent:status:); // 保存方法名称
    
    // 第一个参数: 需要给指定方法传递的值
    //           + 第一个参数需要接收一个指针, 也就是传递值的时候需要传递地址
    // 第二个参数: 需要给指定方法的第几个参数传值
    NSString *number = @"10086";
    // 注意: 设置参数的索引时不能从0开始, 因为0已经被self占用, 1已经被_cmd占用
    [invocation setArgument:&number atIndex:2];
    
    NSString *content = @"love";
    [invocation setArgument:&content atIndex:3];
    
    NSString *status = @"success";
    [invocation setArgument:&status atIndex:4];
    
    // 2.调用NSInvocation对象的invoke方法
    // 只要调用invocation的invoke方法, 就代表需要执行 \
    NSInvocation对象中指定对象的指定方法, 并且传递指定的参数
    [invocation invoke];

 

五、invocation封装

这分类用于实现多个参数的方法

#import "NSObject+performSelector.h"

@implementation NSObject (performSelector)

- (id)performSelector:(SEL)aSelector withObjects:(NSArray *)objects
{
    //  1.创建签名对象
    NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:aSelector];
    
    // 判断传入的方法是否存储, 如果方法不存在签名对象为nil
    if (signature == nil) {
        // 传入的方法不存在
        NSString *info = [NSString stringWithFormat:@" -[%@ %@]:  unrecognized selector sent to instance", [self class], NSStringFromSelector(aSelector)];
        // 抛异常
        @throw [[NSException alloc] initWithName:@"一个牛B的错误" reason:info userInfo:nil];
    }
    
    // 2.创建一个NSInvocation对象
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    
     // 3.保存方法所属的对象
    invocation.target = self;
    
    // 给invocation设置的方法, 必须和签名中的方法一致
    //  4.保存方法名称
    invocation.selector = aSelector;
    
    // 5.设置参数
    /*
     当前如果直接遍历参数数组来设置参数, 会存在问题
     如果参数数组元素多余参数个数, 那么就会报错
     */
    NSUInteger arguments =  signature.numberOfArguments - 2;
    /*
     如果直接遍历参数值的个数, 会存在问题
     如果参数的个数大于了参数值的个数, 那么数组会越界
     */
    NSUInteger objectsCount = objects.count;
    /*
     参数和参数值, 谁少就遍历谁
     */
    NSUInteger count = MIN(arguments, objectsCount);
    
    for (int i = 0; i < count; i++) {
        NSObject *obj = objects[i];
        // 处理数组参数中NSNull问题
        if ([obj isKindOfClass:[NSNull class]]) {
            obj = nil;
        }
        [invocation setArgument:&obj atIndex:i + 2];
    }
    
    // 6.调用NSInvocation对象的invoke方法
    [invocation invoke];
    
    id res = nil;
    // 判断当前方法是否有返回值
//    NSLog(@"ReturnType = %zd", signature.methodReturnLength);
    if ( signature.methodReturnLength != 0) {
        // 7.获取返回值
        // getReturnValue方法会将会去到的方法返回值赋值给传入的对象
        [invocation getReturnValue:&res];
    }
    
    return res;
}
@end

若要给参数传空值

[self performSelector:@selector(sendMessageWithNumber:andContent:) withObjects:@[[NSNull null], @"love"]];

 

六、JavaScript


1、OC中调用JavaScipt
如何在OC中调用JavaScript代码
使用UIWebView的stringByEvaluatingJavaScriptFromString方法即可

// [self.webView stringByEvaluatingJavaScriptFromString:@"showTitle();"];

 

2、JavaScipt中调用OC方法

比如要在这个网页中调用oc的方法

<html>
    <!--描述网页信息-->
    <head>
        <meta charset="UTF-8"/>
        <title>hello world</title>
        <script>
            function show()
            {
                alert(1);
            }
        
            function showTitle()
            {
                alert(document.title);
            }
        
            function repost()
            {
                location.href = "http://www.520it.com";
            }
            function sum()
            {
                return 1 + 1;
            }
        
            function btnClick()
            {
                location.href = "xmg://sendMessageWithNumber_andContent_?10086&love";
            }
        </script>
    </head>
    <!--网页具体内容-->
    <body>
        电话号码: 13554499311</br>
        邮箱: 97606813@qq.com</br>
        
        <button style = "background: red; height: 150px; width: 150px;" onclick = "btnClick();">哥是按钮</button>
    </body>
</html>

注意看function btnClick()这个方法

固定格式:

//后面跟的是方法名和参数

:用_代替

方法名和参数之间用?隔开

每个参数之间用&隔开

 

比如说现在有这四个oc方法

- (void)call
{
    NSLog(@"%s", __func__);
}

// callWithNumber:
- (void)callWithNumber:(NSString *)number
{
    NSLog(@"打电话给%@", number);
}

//sendMessageWithNumber:andContent:
- (void)sendMessageWithNumber:(NSString *)number andContent:(NSString *)content
{
    NSLog(@"发信息给%@, 内容是%@", number, content);
}


- (void)sendMessageWithNumber:(NSString *)number andContent:(NSString *)content status:(NSString *)status
{
    NSLog(@"发信息给%@, 内容是%@, 发送的状态是%@", number, content, status);
}

 

要在JS跳转网页,实现下面这个代理方法

// 每次发送请求前都会调用
// 利用该方法作为JS和OC之间的桥梁
// 在OC代理方法中通过判断自定义协议头, 决定是否是JS调用OC方法
// 在OC代理方法中通过截取字符串, 获取JS想调用的OC方法名称

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;

 

没有参数的情况(消除异常)

 

NSString *schem = @"xmg://";
    NSString *urlStr = request.URL.absoluteString;
    if ([urlStr hasPrefix:schem]) {
        NSLog(@"需要调用OC方法");
        // 1.从URL中获取方法名称
        // xmg://call
        NSString *methodName = [urlStr substringFromIndex:schem.length];
        NSLog(@"%@", methodName);
        // 2.调用方法
        SEL sel = NSSelectorFromString(methodName);
        // 忽略警告信息的作用范围开始
#pragma clang diagnostic push
        // 下面这一行代码是用于指定需要忽略的警告信息
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [self performSelector:sel withObject:nil];
        // 忽略警告信息的作用范围结束
#pragma clang diagnostic pop
        return NO;
    }

 

 

 

一个参数的情况

 

NSString *schem = @"xmg://";
    NSString *urlStr = request.URL.absoluteString;
    if ([urlStr hasPrefix:schem]) {
        NSLog(@"需要调用OC方法");
        // 1.从URL中获取方法名称
        // xmg://callWithNumber_?10086
        NSString *subPath = [urlStr substringFromIndex:schem.length];
        // 注意: 如果指定的用于切割的字符串不存在, 那么就会返回整个字符串
        NSArray *subPaths = [subPath componentsSeparatedByString:@"?"];
        // 2.获取方法名称
        NSString *methodName = [subPaths firstObject];
        methodName = [methodName stringByReplacingOccurrencesOfString:@"_" withString:@":"];
        NSLog(@"%@", methodName);
        // 2.调用方法
        SEL sel = NSSelectorFromString(methodName);
        NSString *parmas = nil;
        if (subPaths.count == 2) {
            parmas = [subPaths lastObject];
        }
        [self performSelector:sel withObject:parmas];
        return NO;

 

 

 

两个参数的情况

 

NSString *schem = @"xmg://";
    NSString *urlStr = request.URL.absoluteString;
    if ([urlStr hasPrefix:schem]) {
        NSLog(@"需要调用OC方法");
        // 1.从URL中获取方法名称
        // xmg://sendMessageWithNumber_andContent_?10086&love
        NSString *subPath = [urlStr substringFromIndex:schem.length];
        // 注意: 如果指定的用于切割的字符串不存在, 那么就会返回整个字符串
        NSArray *subPaths = [subPath componentsSeparatedByString:@"?"];
        // 2.获取方法名称
        NSString *methodName = [subPaths firstObject];
        methodName = [methodName stringByReplacingOccurrencesOfString:@"_" withString:@":"];
        NSLog(@"%@", methodName);
        // 2.调用方法
        SEL sel = NSSelectorFromString(methodName);
        
        // 3.处理参数
        NSString *parma = nil;
        if (subPaths.count == 2) {
            parma = [subPaths lastObject];
            // 3.截取参数
            NSArray *parmas = [parma componentsSeparatedByString:@"&"];
            [self performSelector:sel withObject:[parmas firstObject] withObject:[parmas lastObject]];
            return NO;
        }
        [self performSelector:sel withObject:parma];
        return NO;
    }

 

 

 

三个参数的情况

 

// xmg://
    NSString *schem = @"xmg://";
    NSString *urlStr = request.URL.absoluteString;
    if ([urlStr hasPrefix:schem]) {
        NSLog(@"需要调用OC方法");
        // 1.从URL中获取方法名称
        // sendMessageWithNumber_andContent_?10086&love
        NSString *subPath = [urlStr substringFromIndex:schem.length];
        // 注意: 如果指定的用于切割的字符串不存在, 那么就会返回整个字符串
        // sendMessageWithNumber_andContent_
        // 10086&love
        NSArray *subPaths = [subPath componentsSeparatedByString:@"?"];
        // 2.获取方法名称
        NSString *methodName = [subPaths firstObject];
        methodName = [methodName stringByReplacingOccurrencesOfString:@"_" withString:@":"];
        NSLog(@"%@", methodName);
        // 2.调用方法
        SEL sel = NSSelectorFromString(methodName);
        // 3.处理参数
        NSArray *parmas =  nil;
        if (subPaths.count == 2) {
            parmas = [[subPaths lastObject] componentsSeparatedByString:@"&"];
        }
        [self performSelector:sel withObjects:parmas];
        return NO;
    }
    
    return YES;

 

posted @ 2015-08-26 17:13  Mr.陳  阅读(1010)  评论(0编辑  收藏  举报