iOS 知識小集不知不覺已經更新了 15 期,也許有些同窗會問,前 14 期在哪裏能找到呢?天天一條iOS知識小集在哪裏能第一時間看到呢?我有必要說明一下,咱們的文章會在公衆號上首發,其它平臺只會發部分文章,並且更新都會比公衆號晚。天天早晨 7:30左右,會在小集微信羣和微博發出天天一條iOS知識小集和公衆號文章。若是你想關注咱們的公衆號或加小集微信羣(2號羣還差13個名額就滿了),請看文章結尾。javascript
這周公衆號發佈的文章:css
本期知識小集:html
截止目前爲止iOS知識小集已經發布183條,我想看看前端
做者: halohilyjava
在 iOS 開發中,自定義的彈層組件很是常見,好比分享框、自定義的 actionSheet 組件等。有的場景下,會選擇使用 UIViewController 類型來實現,這時定製這個視圖的出現、隱藏動畫很是方便。然而,有時候須要選擇輕量級的 UIView 類型來實現。這時該怎麼定製它的出現、隱藏動畫呢?這裏提供一個思路:git
使用 UIView 的
willMoveToSuperview:
和didMoveToSuperview
這組方法,它們會在UIView
做爲subView 被添加到其餘 UIView 中時調用。這裏須要注意,自身調用removeFromSuperview
方法時,一樣會觸發這組方法,只不過這時的參數會是一個 nil。github
提供一個例子來講明:一個選擇 UIView 類型實現的自定義 actionSheet 的出入動畫,交互基本和微信一致。web
#pragma mark - show & dismiss
- (void)didMoveToSuperview {
if (self.superview) {
[UIView animateWithDuration:0.35 delay:0 usingSpringWithDamping:0.9 initialSpringVelocity:10 options:UIViewAnimationOptionCurveEaseIn animations:^{
_backgroundControl.alpha = 1;
self.actionSheetTable.frame = CGRectMake(0, SCREEN_HEIGHT - _sheetHeight, SCREEN_WIDTH, _sheetHeight);
} completion:^(BOOL finished) {
[super didMoveToSuperview];
}];
}
}
- (void)hideSelf {
[UIView animateWithDuration:0.35 delay:0 usingSpringWithDamping:0.9 initialSpringVelocity:10 options:UIViewAnimationOptionCurveEaseIn animations:^{
_backgroundControl.alpha = 0;
self.actionSheetTable.frame = CGRectMake(0, SCREEN_HEIGHT, SCREEN_WIDTH, _sheetHeight);
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}
複製代碼
做者: Vong_HUST數據庫
一般咱們會遇到這種需求,一個視圖除了須要響應子視圖的點擊事件,其它空白地方但願能將點擊事件透傳到,好比自定義了一個「導航欄」,除了左右兩邊按鈕,但願其它部分點擊可以透傳到底下的視圖。這個時候咱們能夠經過複寫 hitTest
方法,具體實現以下。編程
@implementation PassthroughView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (self.hidden || self.alpha < FLT_EPSILON || self.userInteractionEnabled) {
return [super hitTest:point withEvent:event];
}
UIView *targetView = nil;
for (UIView *subview in [[self subviews] reverseObjectEnumerator]) {
if ((targetView = [subview hitTest:[subview convertPoint:point fromView:self] withEvent:event])) {
break;
}
}
return targetView;
}
@end
複製代碼
以上代碼便可實現,只響應子視圖的事件,而非子視圖區域部分的交互事件則透傳到響應鏈中的下一個響應者。
若是你有其它更好方式,也能夠分享出來,一塊兒交流下。
做者: Lefe_x
上次的小集中,我主要討論瞭如何調試 WebView ,小集發出後 @折騰範兒_味精
提供了另外一種方法來調試 WebView。我以爲有必要再擴展一下,原話是這樣的:
真說方便仍是植入一個 webview console 在 debug 環境,能夠在黑盒下不連電腦不連 safari 調 dom,調 js,另外在開發期間 Xcode 斷點 run 的時候,js hook console.log console.alert,接管 window.onerror 全都改 bridge NSLog 輸出,也會方便點。
短短几句話,信息量很大,私下向味精學習了下,這裏總結一下。寫完這個小集特地讓味精看了下,以爲有必要再補充下第二種調試技巧,但中途踩了幾個坑,一直到23:30左右才搞定。
第一,把 WebView 用來調試的 log、alert、error 顯示到 NA ,在調試時會方便很多。作 WebView 與端交互的時候,主要用 window.webkit.messageHandlers.xxx.postMessage(params);
來給端發消息,也就是說 WebView 想給端發消息的時候直接調用這個方法便可,端會經過 WKScriptMessageHandler
的代理方法來接收消息,而此時端根據和 WebView 約定的規則進行通訊便可。
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
複製代碼
而添加調試信息,無非就是給 WebView 添加了 log、alert、error 這些消息的 bridge,這樣當 WebView 給端發送消息後,端根據和 WebView 約定的規則解析 log、alert、error 爲端對應的事件,好比 log 直接調用端的 NSLog
,alert 調用端的 UIAlertController
。
第二,黑盒下調試 WebView,無需鏈接電腦和 safari 便可調試 DOM,這個能夠參考小程序的 vConsole 或者 eruda 。能夠直接在 WebView 中接入,或者在端中接入。這裏以在端中接入 eruda 爲例,這裏踩到幾個坑:
1.有些頁面顯示不出來,估計是故意屏蔽掉的,味精特地使用 JSBox 試了下其它頁面,發現百度等都不能夠顯示調試按鈕,而掘金是能夠的;
2.使用本地的頁面也顯示不出來,這是 webview 跨域安全方面的考慮,file 協議下會禁止 js css html 以部分 file,部分網絡的方式加載。
下面這段代碼直接在 webview 加載完成後執行便可。
NSString *js = @"(function() {var script = document.createElement('script');script.type = 'text/javascript';script.src = 'https://xteko.blob.core.windows.net/neo/eruda-loader.js';document.body.appendChild(script);})();";
[self.webView evaluateJavaScript:js completionHandler: nil];
複製代碼
做者: halohily
最近負責下載組件的開發,對於如何設計一個下載模塊有一些粗淺體會,今天分享一下我採用的方案,但願可以拋磚引玉。另外,最近會出兩篇主題爲「下載組件的設計」和「與 Hybrid 相關的下載方案」的長文,歡迎關注 「 知識小集 」公衆號。
「下載」做爲一個須要本地結構化、持久化存儲的場景,使用數據庫是比較天然的選擇。因此,咱們首先拆分出一個數據庫模塊,用來存儲下載記錄。主要字段爲下載任務的信息,如 url、文件大小、時間戳等,以及最重要的文件本地存儲路徑。這一層能夠在接口設計上認真思慮,好比僅涉及當前業務邏輯,而不涉及具體的數據庫操做,至關因而較 FMDB 等數據庫組件來講更高層的抽象。後期須要更換底層數據庫引擎時,本層封裝無需改動,是比較理想的實現。
數據庫是用來存儲下載記錄的,那麼所下載的具體文件呢?天然就須要一個文件管理模塊,在這個模塊裏,負責根據文件 url 生成本地的存儲路徑,以及進行文件校驗、存儲、移除等操做。
所要下載的文件,咱們能夠按體積、類型等進行區分。對於網絡請求的結果這類簡短內容,我抽象出了一個緩存管理器,用來完成網絡請求、圖片等內容的緩存。網絡請求的 JSON 格式結果,能夠選擇 YYCache
、EGOCache
等緩存框架。而圖片的緩存,則能夠選擇專一圖片緩存的 YYWebImage
、SDWebImage
等框架。
對於體積較大的文件,天然須要一個專一大文件下載的模塊。這個模塊不關注具體的文件類型,不關注具體的業務場景,它只須要文件 url 、文件管理模塊生成的本地目標路徑,完成下載任務便可。
在以上通用模塊的基礎上,有一個業務層的封裝,它負責根據提交的下載任務,協調調用各基礎組件。舉個例子,一個下載任務包括一個視頻文件、一個網絡請求結果、三張圖片。本模塊在收到任務後,首先解析出以上的任務具體結構。使用文件管理模塊,根據視頻文件 url 生成本地存儲目標路徑,調用大文件下載器完成下載,此爲一個子任務。對於網絡請求結果,調用緩存模塊,進行緩存,此爲一個子任務。對於三張圖片,使用圖片緩存器完成緩存,此爲一個子任務。三個子任務均完成,使用數據庫模塊,對下載記錄、媒體文件記錄等進行存儲。除此以外,本模塊還負責對外提供下載中任務、已下載任務等數據。
做者: KANGZUBIN
前兩週咱們發了一個小集「iOS 自帶九宮格拼音鍵盤與 Emoji 表情之間的坑」,介紹瞭如何解決因爲輸入框限制 Emoji 表情的輸入致使中文拼音也沒法輸入的問題。
後面咱們又有了新需求:對輸入框已輸入的文本字數進行實時統計,並在界面上顯示剩餘字數,且不能讓所輸入的文本超過最大限制長度。但這個簡單的功能仍然有很多小坑。
在上一個小集中,咱們講到,對於 iOS 系統自帶的鍵盤,有時候它在輸入框中填入的是佔位字符(已被高亮選中起來),等用戶選中鍵盤上的候選詞時,再替換爲真正輸入的字符,以下:
這會帶來一個問題:好比輸入框限定最多隻能輸入 10 位,當已經輸入 9 個漢字的時候,使用系統拼音鍵盤則第 10 個字的拼音就打不了(由於剩餘的 1 位沒法輸入完整的拼音)。
怎麼辦呢?上面提到,輸入框中的拼音會被高亮選中起來,因此咱們能夠根據 UITextField
的 markedTextRange
屬性判斷是否存在高亮字符,若是有則不進行字數統計和字符串截斷操做。咱們經過監聽 UIControlEventEditingChanged
事件來對輸入框內容的變化進行相應處理,以下:
[self.textField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
複製代碼
- (void)textFieldDidChange:(UITextField *)textField {
// 判斷是否存在高亮字符,若是有,則不進行字數統計和字符串截斷
UITextRange *selectedRange = textField.markedTextRange;
UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
if (position) {
return;
}
// maxWowdLimit 爲 0,不限制字數
if (self.maxWowdLimit == 0) {
return;
}
// 判斷是否超過最大字數限制,若是超過就截斷
if (textField.text.length > self.maxWowdLimit) {
textField.text = [textField.text substringToIndex:self.maxWowdLimit];
}
// 剩餘字數顯示 UI 更新
}
複製代碼
對於 UITextView
的處理也是相似的。
另外,對於「字數」的定義是不少種理解:在 Objective-C 中字符串 NSString
的長度 length
,對於一箇中文漢字和一個英文字母都是 1;但若是咱們要按字節來統計和限制,同一字符在不一樣編碼下所佔的字節數也是不一樣的;另外有時咱們要統計的是所輸入文本的單詞個數,而不是字符串的長度,因此咱們須要根據不一樣的使用場景進行分析。
coldlight_hh
或 wsy9871
,請註明 iOS 入羣
;coldlight_hh
或者 bob5201215
,請註明 Flutter 入羣;wsy9871
或 coldlight_hh
,請註明 前端入羣
wsy9871
或 coldlight_hh
,請註明 PWA入羣
kangzubin
**,請註明 小程序入羣
上面技術羣羣規比較嚴,主要以討論技術爲主,發廣告等行爲都一概踢出羣。另外,咱們的微信羣不是真正的「討論羣」,爲了避免浪費你們看消息的時間,除技術問題或很小白的問題,你們不會討論。
某天「 知識小集 · 前端修行室 」進來個妹子,而後羣就炸了,控制不住你們的情緒,因此專門開了個「 知識小集 · 吐槽吹水羣 」
,這個羣暫時不對外開放,能夠加入技術羣后再拉羣。