iOS開發——網絡篇——UIWebview基本使用,NSInvocation(封裝類),NSMethodSignature(簽名),JavaScript,拋異常,消除警告

1、UIWebView簡介


一、UIWebView
什麼是UIWebView
UIWebView是iOS內置的瀏覽器控件
系統自帶的Safari瀏覽器就是經過UIWebView實現的html

UIWebView不但能加載遠程的網頁資源,還能加載絕大部分的常見文件
html\htm
pdf、doc、ppt、txt
mp4
… …web

UIWebView經常使用的加載資源的方法
- (void)loadRequest:(NSURLRequest *)request;數組


二、鍵盤工具條顯示中文瀏覽器

 

2、經常使用屬性和方法


從新加載(刷新)
- (void)reload;工具

中止加載
- (void)stopLoading;post

回退
- (void)goBack;atom

前進
- (void)goForward;lua

須要進行檢測的數據類型
@property(nonatomic) UIDataDetectorTypes dataDetectorTypesurl


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

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

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

是否伸縮內容至適應屏幕當前尺寸
@property(nonatomic) BOOL scalesPageToFit;


3、代理


監聽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;

 

4、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];

 

5、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"]];

 

6、JavaScript


一、OC中調用JavaScipt
如何在OC中調用JavaScript代碼
使用UIWebView的stringByEvaluatingJavaScriptFromString方法便可

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

 

二、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;
相關文章
相關標籤/搜索