欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

iOS開發(fā)WebViewJavascriptBridge通訊原理解析

 更新時間:2022年11月23日 11:23:50   作者:好_好先生  
這篇文章主要為大家介紹了iOS開發(fā)WebViewJavascriptBridge通訊原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

H5頁面具有跨平臺、開發(fā)容易、上線不需要跟隨App的版本等優(yōu)點(diǎn),但H5頁面也有體驗(yàn)不如native好、沒有native穩(wěn)定等問題。所以目前大部分App都是使用Hybrid混合開發(fā)的。

當(dāng)然有了H5頁面就少不了H5與native交互,交互就會用到bridge的能力了。

WebViewJavascriptBridge是一個native與JS進(jìn)行消息互通的第三方庫,本章會簡單解析一下WebViewJavascriptBridge的源碼和實(shí)現(xiàn)原理。

通訊原理

JavaScriptCore

JavaScriptCore作為iOS的JS引擎為原生編程語言O(shè)C、Swift 提供調(diào)用 JS 程序的動態(tài)能力,還能為 JS 提供原生能力來彌補(bǔ)前端所缺能力。 iOS中與JS通訊使用的是JavaScriptCore庫,正是因?yàn)镴avaScriptCore這種起到的橋梁作用,所以也出現(xiàn)了很多使用JavaScriptCore開發(fā)App的框架,比如RN、Weex、小程序、Webview Hybrid等框架。 如圖:

當(dāng)然JS引擎不光有蘋果的JavaScriptCore,谷歌有V8引擎、Mozilla有SpiderMoney

JavaScriptCore本章只簡單介紹,后面主要解析WebViewJavascriptBridge。因?yàn)閡iwebview已經(jīng)不再使用了,所以后面提到的webview都是wkwebview,demo也是以wkwebview進(jìn)行解析。

源碼解析

代碼結(jié)構(gòu)

除了引擎層外,還需要native、h5和WebViewJavascriptBridge三層才能完成一整個信息通路。WebViewJavascriptBridge就是中間那個負(fù)責(zé)通信的SDK。

WebViewJavascriptBridge的核心類主要包含幾個:

  • WebViewJavascriptBridge_JS:是一個JS的字符串,作用是JS環(huán)境的Bridge初始化和處理。負(fù)責(zé)接收native發(fā)給JS的消息,并且把JS環(huán)境的消息發(fā)送給native。
  • WKWebViewJavascriptBridge/WebViewJavascriptBridge:主要負(fù)責(zé)WKWebView和UIWebView相關(guān)環(huán)境的處理,并且把native環(huán)境的消息發(fā)送給JS環(huán)境。
  • WebViewJavascriptBridgeBase:主要實(shí)現(xiàn)了native環(huán)境的Bridge初始化和處理。

初始化

WebViewJavascriptBridge是如何完成初始化的呢,首先要有webview容器,所以要對webview容器進(jìn)行初始化,設(shè)置代理,初始化WebViewJavascriptBridge對象,加載URL。

    WKWebView* webView = [[NSClassFromString(@"WKWebView") alloc] initWithFrame:self.view.bounds];
    webView.navigationDelegate = self;
    [self.view addSubview:webView];
    // 開啟打印
    [WebViewJavascriptBridge enableLogging];
    // 創(chuàng)建bridge對象
    _bridge = [WebViewJavascriptBridge bridgeForWebView:webView];
    // 設(shè)置代理
    [_bridge setWebViewDelegate:self];

這里加載的就是JSBridgeDemoApp這個本地的html文件。

    NSString* htmlPath = [[NSBundle mainBundle] pathForResource:@"JSBridgeDemoApp" ofType:@"html"];
    NSString* appHtml = [NSString stringWithContentsOfFile:htmlPath encoding:NSUTF8StringEncoding error:nil];
    NSURL *baseURL = [NSURL fileURLWithPath:htmlPath];
    [webView loadHTMLString:appHtml baseURL:baseURL];

再看一下JSBridgeDemoApp這個html文件。

function setupWebViewJavascriptBridge(callback) {
// 第一次調(diào)用這個方法的時候,為false
    if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
// 第一次調(diào)用的時候,為false
    if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
// 把callback對象賦值給對象
    window.WVJBCallbacks = [callback];
// 加載WebViewJavascriptBridge_JS中的代碼
// 相當(dāng)于實(shí)現(xiàn)了一個到https://__bridge_loaded__的跳轉(zhuǎn)
    var WVJBIframe = document.createElement('iframe');
    WVJBIframe.style.display = 'none';
    WVJBIframe.src = 'https://__bridge_loaded__';
    document.documentElement.appendChild(WVJBIframe);
    setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
    }
// 驅(qū)動所有hander的初始化
setupWebViewJavascriptBridge(function(bridge) {
...
}

在JSBridgeDemoApp的script標(biāo)簽下,聲明了一個名為setupWebViewJavascriptBridge的方法,在加載html后直接進(jìn)行了調(diào)用。 setupWebViewJavascriptBridge方法中最核心的代碼是:

創(chuàng)建一個iframe標(biāo)簽,然后加載了鏈接為 https://bridge_loaded 的內(nèi)容。相當(dāng)于在當(dāng)前頁面內(nèi)容實(shí)現(xiàn)了一個到 https://bridge_loaded 的內(nèi)部跳轉(zhuǎn)。 ps:iframe標(biāo)簽用于在網(wǎng)頁內(nèi)顯示網(wǎng)頁,也使用iframe作為鏈接的目標(biāo)。

html文件內(nèi)部實(shí)現(xiàn)了這個跳轉(zhuǎn)后native端是如何監(jiān)聽的呢,在webview的代理里有一個方法:decidePolicyForNavigationAction 這個代理方法的作用是只要有webview跳轉(zhuǎn),就會調(diào)用到這個方法。代碼如下:

// 只要webview有跳轉(zhuǎn),就會調(diào)用webview的這個代理方法
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    if (webView != _webView) { return; }
    NSURL *url = navigationAction.request.URL;
    __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
    // 如果是WebViewJavascriptBridge發(fā)送或者接收消息,則特殊處理。否則按照正常流程處理
    if ([_base isWebViewJavascriptBridgeURL:url]) {
        if ([_base isBridgeLoadedURL:url]) {
            // 是否是 https://__bridge_loaded__ 這種初始化加載消息
            [_base injectJavascriptFile];
        } else if ([_base isQueueMessageURL:url]) {
            // https://__wvjb_queue_message__
            // 處理WEB發(fā)過來的消息
            [self WKFlushMessageQueue];
        } else {
            [_base logUnkownMessage:url];
        }
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    // webview的正常代理執(zhí)行流程
...

從上面的代碼中可以看到,如果監(jiān)聽的webview跳轉(zhuǎn)不是WebViewJavascriptBridge發(fā)送或者接收消息就正常執(zhí)行流程,如果是WebViewJavascriptBridge發(fā)送或者接收消息則對此攔截不跳轉(zhuǎn),并且針對消息進(jìn)行處理。 當(dāng)消息url是https://bridge_loaded 的時候,會去注入WebViewJavascriptBridge_js到JS中:

// 將WebViewJavascriptBrige_JS中的方法注入到webview中并且執(zhí)行
- (void)injectJavascriptFile {
    NSString *js = WebViewJavascriptBridge_js();
    // 把javascript代碼注入webview中執(zhí)行
    [self _evaluateJavascript:js];
    // javascript環(huán)境初始化完成以后,如果有startupMessageQueue消息,則立即發(fā)送消息
    if (self.startupMessageQueue) {
        NSArray* queue = self.startupMessageQueue;
        self.startupMessageQueue = nil;
        for (id queuedMessage in queue) {
            [self _dispatchMessage:queuedMessage];
        }
    }
}

[self _evaluateJavascript:js];就是執(zhí)行webview中的evaluateJavaScript:方法。把JS寫入webview。所以執(zhí)行完此處代碼JS當(dāng)中就有bridge這個對象了。初始化完成。

總結(jié):在加載h5頁面后會調(diào)用setupWebViewJavascriptBridge方法,該方法內(nèi)創(chuàng)建了一個iframe加載內(nèi)容為 https://bridge_loaded ,該消息被decidePolicyForNavigationAction監(jiān)聽到,然后執(zhí)行injectJavascriptFile去讀取WebViewJavascriptBridge_js將WebViewJavascriptBridge對象注入到當(dāng)前h5中。

WebViewJavascriptBridge 對象

整個WebViewJavascriptBridge_js文件其實(shí)就是一個字符串形式的js代碼,里面包含WebViewJavascriptBridge和相關(guān)bridge調(diào)用的方法。

// 初始化Bridge對象,OC可以通過WebViewJavascriptBridge來調(diào)用JS里面的各種方法
window.WebViewJavascriptBridge = {
    registerHandler: registerHandler, // JS中注冊方法
    callHandler: callHandler, // JS中調(diào)用OC的方法
    disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
    _fetchQueue: _fetchQueue, // 把消息轉(zhuǎn)換成JSON串
    _handleMessageFromObjC: _handleMessageFromObjC // OC調(diào)用JS的入口方法
};

WebViewJavascriptBridge對象里核心的方法有:

  • registerHandler:JS中注冊方法
  • callHandler: JS中調(diào)用native的方法
  • _fetchQueue: 把消息轉(zhuǎn)換成JSON字符串
  • _handleMessageFromObjC:native調(diào)用JS的入口方法

當(dāng)初始化完成后,WebViewJavascriptBridge對象和對象里的方法就已經(jīng)存在并且可用了。

JS和native是如何相互傳遞消息的呢?從上面的代碼中可以看到如果JS想要發(fā)送消息給native就會調(diào)用callHandler方法;如果native想要調(diào)用JS方法那JS側(cè)就必須先注冊一個registerHandler方法。

相對應(yīng)的我們看一下native側(cè)是如何與JS傳遞消息的,其實(shí)接口標(biāo)準(zhǔn)是一致的,native調(diào)JS的方法使用callHandler方法:

id data = @{ @"dataFromOC": @"aaaa!" };
    [_bridge callHandler:@"OCToJSHandler" data:data responseCallback:^(id response) {
        NSLog(@"JS回調(diào)的數(shù)據(jù)是:%@", response);
    }];

JS調(diào)native方法在native側(cè)就必須先注冊一個registerHandler方法:

    // 注冊事件(h5調(diào)App)
    [_bridge registerHandler:@"JSTOOCCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
        NSLog(@"JSTOOCCallback called: %@", data);
        responseCallback(@"Response from JSTOOCCallback");
    }];

也就是說native像JS發(fā)送消息的話,JS側(cè)要先注冊該方法registerHandler,native側(cè)調(diào)用callHandler; JS像native發(fā)送消息的話,native側(cè)要先注冊registerHandler,JS側(cè)調(diào)用callHandler。這樣才能完成雙端通信。

如圖:

native向JS發(fā)送消息

現(xiàn)在要從native側(cè)向JS側(cè)發(fā)送一條消息,方法名為:"OCToJSHandler",并且拿到JS的回調(diào),具體實(shí)現(xiàn)細(xì)節(jié)如下:

JS側(cè)

native向JS發(fā)送數(shù)據(jù),首先要在JS側(cè)去注冊這個方法:

bridge.registerHandler('OCToJSHandler', function(data, responseCallback) {
    ...
})

這個registerHandler的實(shí)現(xiàn)在WebViewJavascriptBridge_JS是:

// web端注冊一個消息方法,將注冊的方法存儲起來
function registerHandler(handlerName, handler) {
    messageHandlers[handlerName] = handler;
}

就是將這個注冊的方法存儲到messageHandlers這個map中,key為方法名稱,value為function(data, responseCallback) {}這個方法。

native側(cè)

native側(cè)調(diào)用bridge的callHandler方法,傳參為data和一個callback回調(diào)

id data = @{ @"dataFromOC": @"aaaa!" };
[_bridge callHandler:@"OCToJSHandler" data:data responseCallback:^(id response) {
    NSLog(@"JS回調(diào)的數(shù)據(jù)是:%@", response);
}];

接下來會走到WebViewJavascriptBridgeBase的-sendData: responseCallback: handlerName:方法,該方法中將"data"和"handlerName"存入到一個message字典中,如果存在callback會生成一個callbackId一并存入到message字典中,并且將該回調(diào)存入到responseCallbacks中,key為callbackId,value為這個callback。代碼如下:

// 所有信息存入字典
NSMutableDictionary* message = [NSMutableDictionary dictionary];
if (data) {
    message[@"data"] = data;
}
if (responseCallback) {
    NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId];
    self.responseCallbacks[callbackId] = [responseCallback copy];
    message[@"callbackId"] = callbackId;
}
if (handlerName) {
    message[@"handlerName"] = handlerName;
}
[self _queueMessage:message];

將message存儲到隊(duì)列等待執(zhí)行,執(zhí)行該條message時會先將message進(jìn)行序列化,序列化完成后將message拼接到字符串WebViewJavascriptBridge._handleMessageFromObjC('%@');中,然后執(zhí)行_evaluateJavascript執(zhí)行該js方法。

// 把OC消息序列化、并且轉(zhuǎn)化為JS環(huán)境的格式,然后在主線程中調(diào)用_evaluateJavascript
- (void)_dispatchMessage:(WVJBMessage*)message {
    NSString *messageJSON = [self _serializeMessage:message pretty:NO];
    NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
    [self _evaluateJavascript:javascriptCommand];
}

_handleMessageFromObjC方法會將messageJSON傳遞給_dispatchMessageFromObjC進(jìn)行處理。 首先將messageJSON進(jìn)行解析,根據(jù)handlerName取出存儲在messageHandlers中的方法。如果該message中存在callbackId,將callbackId作為參數(shù)生成一個回調(diào)放到responseCallback中。 代碼如下:

function _doDispatchMessageFromObjC() {
// 解析發(fā)送過來的JSON
    var message = JSON.parse(messageJSON);
    var messageHandler;
    var responseCallback;
    // 主動調(diào)用
    // 如果有callbackid
    if (message.callbackId) {
    // 將callbackid當(dāng)做callbackResponseId再返回回去
        var callbackResponseId = message.callbackId;
        responseCallback = function(responseData) {
        // 把消息從JS發(fā)送到OC,執(zhí)行具體的發(fā)送操作
            _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
            };
        // 獲取JS注冊的函數(shù),取出消息里的handlerName
        var handler = messageHandlers[message.handlerName];
        // 調(diào)用JS中的對應(yīng)函數(shù)處理
        handler(message.data, responseCallback);
            }
    }

handler方法其實(shí)就是名為"OCToJSHandler"的方法,這時就走到了registerHandler里的那個function(data, responseCallback) {}方法了。我們看一下方法內(nèi)部的具體實(shí)現(xiàn):

bridge.registerHandler('OCToJSHandler', function(data, responseCallback) {
    // OC中傳過來的數(shù)據(jù)
    log('從OC傳過來的數(shù)據(jù)是:', data)
    // JS返回?cái)?shù)據(jù)
    var responseData = { 'dataFromJS':'bbbb!' }
    responseCallback(responseData)
})

data就是從native傳過來的數(shù)據(jù),responseCallback就是保存的回調(diào),然后又生成了新數(shù)據(jù)作為參數(shù)給到了這個回調(diào)。

responseCallback的實(shí)現(xiàn)是:

responseCallback = function(responseData) {
    // 把消息從JS發(fā)送到OC,執(zhí)行具體的發(fā)送操作
    _doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
};

將該方法的handlerName、生成的callbackResponseId(也就是callbackId)以及JS返回的數(shù)據(jù)一起給到_doSend方法。

_doSend方法將message存儲到sendMessageQueue消息列表中,并使用messagingIframe加載了一次https://wvjb_queue_message。

// 把消息從JS發(fā)送到OC,執(zhí)行具體的發(fā)送操作
    function _doSend(message, responseCallback) {
    // 把消息放入消息列表
    sendMessageQueue.push(message);
    // 發(fā)出js對oc的調(diào)用,讓webview執(zhí)行跳轉(zhuǎn)操作,可以在decidePolicyForNavigationAction:中攔截到j(luò)s發(fā)給oc的消息
    messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
    }

這時webview的監(jiān)聽方法decidePolicyForNavigationAction監(jiān)聽到了https://wvjb_queue_message 消息后還是執(zhí)行WebViewJavascriptBridge._fetchQueue()去取數(shù)據(jù),取到數(shù)據(jù)后根據(jù)responseId當(dāng)初在_responseCallbacks中存儲的callback,然后執(zhí)行callback、移除responseCallbacks中的數(shù)據(jù)。到此為止,整個native向JS發(fā)送消息的過程就完成了。

總結(jié):

  • JS中先調(diào)用registerHandler將方法存儲到messageHandlers中
  • native調(diào)用callHandler:方法,將消息內(nèi)容存儲到message中,回調(diào)存儲到responseCallbacks中。
  • 將message消息序列化通過_evaluateJavascript方法執(zhí)行_handleMessageFromObjC
  • 將message解析,通過message.handlerName從messageHandlers取出該方法;根據(jù)message.callbackId生成回調(diào)
  • 執(zhí)行該方法,回調(diào)

JS向native發(fā)送消息

從JS向native發(fā)消息其實(shí)和native向JS發(fā)消息的接口層面是差不多的。

native側(cè)

native側(cè)首先要注冊一個JSTOOCCallback方法

[_bridge registerHandler:@"JSTOOCCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
    responseCallback(@"Response from JSTOOCCallback");
}];

該方法也同樣是將該方法的callback存儲起來,存儲到messageHandlers當(dāng)中,key就是方法名"JSTOOCCallback",value就是callback。

JS側(cè)

JS側(cè)會調(diào)用callHandler方法:

// 調(diào)用oc中注冊的那個方法
bridge.callHandler('JSTOOCCallback', {'foo': 'bar'}, function(response) {
    log('JS 取到的回調(diào)是:', response)
})

這個callHandler方法同樣會調(diào)用_doSend方法:將callback存儲到responseCallbacks中,key為callbakid;將消息存儲到sendMessageQueue中;messagingIframe執(zhí)行https://wvjb_queue_message

native的decidePolicyForNavigationAction方法監(jiān)聽到該消息后同樣通過WebViewJavascriptBridge._fetchQueue()去取消息。

根據(jù)callbackId創(chuàng)建一個responseCallback,根據(jù)message的handlerName從messageHandlers取出該回調(diào),然后執(zhí)行:

WVJBResponseCallback responseCallback = NULL;
NSString* callbackId = message[@"callbackId"];
if (callbackId) {
    responseCallback = ^(id responseData) {
        if (responseData == nil) {
            responseData = [NSNull null];
        }
        WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
        [self _queueMessage:msg];
    };
} else {
    responseCallback = ^(id ignoreResponseData) {
        // Do nothing
    };
}
WVJBHandler handler = self.messageHandlers[message[@"handlerName"]];
handler(message[@"data"], responseCallback);

調(diào)用完這個方法后,該消息已經(jīng)收到,然后將回調(diào)的內(nèi)容回調(diào)給JS。 通過上面的代碼可以看到,回調(diào)JS的內(nèi)容就是callbackId和responseData生成的message,調(diào)用_queueMessage方法。

_queueMessage方法上面已經(jīng)看過了,就是序列化消息、加入隊(duì)列、執(zhí)行WebViewJavascriptBridge._handleMessageFromObjC('%@');方法。

JS收到該消息后,處理返回的消息,從responseCallbacks中根據(jù)message中的responseId取出callback并且執(zhí)行。最后刪除responseCallbacks中的數(shù)據(jù),JS向native發(fā)送數(shù)據(jù)就完成了。

小結(jié):

  • native側(cè)調(diào)用registerHandler方法注冊方法,方法名為JSTOOCCallback,將消息存儲到messageHandlers中,key為方法名,value為callback。
  • JS側(cè)調(diào)用callHandler方法:將responseCallback存儲到responseCallbacks中;將message存儲到sendMessageQueue中;messagingIframe執(zhí)行 http://wvjb_queue_message
  • native側(cè)監(jiān)聽到該消息后調(diào)用WebViewJavascriptBridge._fetchQueue()去取數(shù)據(jù)
  • 根據(jù)handlerName從messageHandlers中取出該callback;根據(jù)callbackId創(chuàng)建callback對象作為參數(shù)放到handlerName的方法中;執(zhí)行該回調(diào)。

總結(jié)

綜上,WebViewJavascriptBridge的核心流程就分析完了,最核心的點(diǎn)是JS通過加載iframe來通知native側(cè);native側(cè)通過evaluateJavaScript方法去執(zhí)行JS。

從整個SDK來看,設(shè)計(jì)的非常好,值得借鑒學(xué)習(xí):

  • 使用外觀模式統(tǒng)一調(diào)用接口,比如初始化WebViewJavascriptBridge的時候,不需要關(guān)心使用方使用的是UIWebView還是WKWebView,內(nèi)部已經(jīng)處理好了。
  • 接口統(tǒng)一,不管是native側(cè)還是JS側(cè),調(diào)用方法就是callHandler、注冊方法就是registerHandler,不需要關(guān)注內(nèi)部實(shí)現(xiàn),使用非常方便。
  • 代碼簡潔,邏輯清晰,層次分明。從類的分布就能很清晰的看出各自的功能是什么。
  • 職責(zé)單一,比如decidePolicyForNavigationAction方法只負(fù)責(zé)監(jiān)聽事件、_fetchQueue是負(fù)責(zé)把消息轉(zhuǎn)換成JSON字符串返回、_doSend是發(fā)送消息到native、_dispatchMessageFromObjC是負(fù)責(zé)處理從OC返回的消息等。雖然decidePolicyForNavigationAction也能接收消息,但這樣就不會這么精簡了。
  • 擴(kuò)展性好,目前decidePolicyForNavigationAction雖然只有初始化和發(fā)消息兩個事件,如果有其他事件還可以再擴(kuò)展,這也得益于方法設(shè)計(jì)的職責(zé)單一,擴(kuò)展對原有方法影響會很小。

以上就是iOS開發(fā)WebViewJavascriptBridge通訊原理解析的詳細(xì)內(nèi)容,更多關(guān)于iOS WebViewJavascriptBridge通訊的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • IOS HTTP請求的常見狀態(tài)碼總結(jié)

    IOS HTTP請求的常見狀態(tài)碼總結(jié)

    這篇文章主要介紹了IOS HTTP請求的常見狀態(tài)碼總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • 清除WKWebView cookies的方法

    清除WKWebView cookies的方法

    下面小編就為大家?guī)硪黄宄齏KWebView cookies的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • iOS創(chuàng)建對象的不同姿勢詳解

    iOS創(chuàng)建對象的不同姿勢詳解

    這篇文章主要介紹了iOS創(chuàng)建對象的不同姿勢,文中介紹的很詳細(xì),對大家具有一定的參考價值,有需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-02-02
  • iOS實(shí)現(xiàn)簡單計(jì)算器小功能

    iOS實(shí)現(xiàn)簡單計(jì)算器小功能

    這篇文章主要為大家詳細(xì)介紹了iOS實(shí)現(xiàn)簡單計(jì)算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • IOS  AFNetworking的Post失敗及requestSerializer的正確使用

    IOS AFNetworking的Post失敗及requestSerializer的正確使用

    這篇文章主要介紹了IOS AFNetworking的Post失敗及requestSerializer的正確使用的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • iOS實(shí)現(xiàn)轉(zhuǎn)場動畫的3種方法示例

    iOS實(shí)現(xiàn)轉(zhuǎn)場動畫的3種方法示例

    這篇文章主要給大家介紹了關(guān)于iOS實(shí)現(xiàn)轉(zhuǎn)場動畫的3種方法,文中通過示例代碼以及圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • iOS UIBezierPath實(shí)現(xiàn)餅狀圖

    iOS UIBezierPath實(shí)現(xiàn)餅狀圖

    這篇文章主要為大家詳細(xì)介紹了iOS UIBezierPath實(shí)現(xiàn)餅狀圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-03-03
  • iOS 請求權(quán)限封裝類的實(shí)例代碼

    iOS 請求權(quán)限封裝類的實(shí)例代碼

    下面小編就為大家分享一篇iOS 請求權(quán)限封裝類的實(shí)例代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01
  • iOS UIPickerView的簡單封裝示例

    iOS UIPickerView的簡單封裝示例

    這篇文章主要給大家介紹了關(guān)于iOS UIPickerView的簡單封裝的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對各位iOS開發(fā)者們具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • IOS 播放系統(tǒng)提示音使用總結(jié)(AudioToolbox)

    IOS 播放系統(tǒng)提示音使用總結(jié)(AudioToolbox)

    這篇文章主要介紹了IOS 播放系統(tǒng)提示音使用總結(jié)(AudioToolbox)的相關(guān)資料,需要的朋友可以參考下
    2017-05-05

最新評論