目錄:
- 手動(dòng)下載遠(yuǎn)程庫(kù)來(lái)pod install / pod update
- app在后臺(tái)時(shí)keyboard監(jiān)聽(tīng)也會(huì)被觸發(fā)
- 淘口令
- NSCopying實(shí)現(xiàn)
- 如何讓alert顯示在頂層
- 自帶emoji的行間距問(wèn)題
- app如何換色(清明黑白灰)
- UIApplicationDidBecomeActiveNotification
- viewDidLoad觸發(fā)
1. 手動(dòng)下載遠(yuǎn)程庫(kù)來(lái)pod install / pod update
遠(yuǎn)程辦公最怕的就是庫(kù)版本發(fā)生變化,最近一拉最新代碼經(jīng)常就要pod install / pod repo update,每次都會(huì)卡死總要有各種問(wèn)題,解決的方法大概就是github源就用4G丐怯,Google源就用代理+wifi哎垦,但是仍舊有的時(shí)候下不下來(lái)似袁,于是我就在這里說(shuō)一下如何直接從github下載然后讓pod過(guò)去统屈。
首先驱证,如果是卡在了pod repo update躏仇,也就是從遠(yuǎn)端拿這些庫(kù)最新的地址tag之類的恋脚,這個(gè)時(shí)候可以讓能夠過(guò)這一步的同事把/Users/用戶名/.cocoapods/repos/trunk/Specs
整個(gè)文件夾壓縮打包發(fā)給你,直接替換就好焰手,這樣就可以成功的過(guò)update那關(guān)啦糟描。
如果木有同事裝好,可以直接從git上下載最新的:
https://github.com/CocoaPods/Specs
Specs里面存了哪些東西呢书妻?我們以一會(huì)兒想要實(shí)驗(yàn)的庫(kù)libwebp為例吧:
其實(shí)里面就是各個(gè)版本有個(gè)json以及json tag文件船响,我們打開(kāi)json看一下:
{
"name": "libwebp",
"version": "1.1.0",
"summary": "Library to encode and decode images in WebP format.",
"homepage": "https://developers.google.com/speed/webp/",
"authors": "Google Inc.",
"license": {
"type": "BSD",
"file": "COPYING"
},
"source": {
"git": "https://chromium.googlesource.com/webm/libwebp",
"tag": "v1.1.0"
},
"compiler_flags": "-D_THREAD_SAFE",
"requires_arc": false,
"platforms": {
"osx": "10.8",
"ios": "6.0",
"tvos": "9.0",
"watchos": "2.0"
},
"pod_target_xcconfig": {
"USER_HEADER_SEARCH_PATHS": "$(inherited) ${PODS_ROOT}/libwebp/ ${PODS_TARGET_SRCROOT}/"
},
"preserve_paths": "src",
"default_subspecs": [
"webp",
"demux",
"mux"
],
"prepare_command": "sed -i.bak 's/<inttypes.h>/<stdint.h>/g' './src/webp/types.h'",
"subspecs": [
{
"name": "webp",
"source_files": [
"src/webp/decode.h",
"src/webp/encode.h",
"src/webp/types.h",
"src/webp/mux_types.h",
"src/webp/format_constants.h",
"src/utils/*.{h,c}",
"src/dsp/*.{h,c}",
"src/dec/*.{h,c}",
"src/enc/*.{h,c}"
],
"public_header_files": [
"src/webp/decode.h",
"src/webp/encode.h",
"src/webp/types.h",
"src/webp/mux_types.h",
"src/webp/format_constants.h"
]
},
{
"name": "demux",
"dependencies": {
"libwebp/webp": [
]
},
"source_files": [
"src/demux/*.{h,c}",
"src/webp/demux.h"
],
"public_header_files": "src/webp/demux.h"
},
{
"name": "mux",
"dependencies": {
"libwebp/demux": [
]
},
"source_files": [
"src/mux/*.{h,c}",
"src/webp/mux.h"
],
"public_header_files": "src/webp/mux.h"
}
]
}
然后我們就可以去source的git下載相應(yīng)tag的庫(kù)啦
"source": {
"git": "https://chromium.googlesource.com/webm/libwebp",
"tag": "v1.1.0"
},
下載tar.gz或者zip都OK,然后解壓放到我們project的本地pod里面:
注意哦你放進(jìn)去的就是你從庫(kù)倉(cāng)庫(kù)下載下來(lái)的文件,可能和你看到本地這個(gè)庫(kù)的內(nèi)容不一樣见间,沒(méi)關(guān)系的聊闯,你放進(jìn)去再pod install過(guò)了以后就一樣啦。
現(xiàn)在我們實(shí)現(xiàn)了下載需要的版本米诉,并放入本地緩存菱蔬,之后就可以來(lái)看如何pod install啦。在pod install的過(guò)程中史侣,其實(shí)它就是比較了Podfile.lock
文件和本地緩存文件版本差異拴泌,如果有區(qū)別就去遠(yuǎn)端下載,所以現(xiàn)在我們需要讓本地緩存的庫(kù)和podfile.lock
里面一致抵窒,當(dāng)然有一種方法是你把podfile.lock
里面的庫(kù)版本降低到你本地的版本弛针,這樣你也不用從遠(yuǎn)端下載就可以成功pod install啦叠骑,只是這樣的話你需要經(jīng)常stash著這個(gè)變化李皇。
那么本地緩存的版本由什么管理呢?其實(shí)就是項(xiàng)目/Pods/Manifest.lock
文件宙枷,你打開(kāi)會(huì)發(fā)現(xiàn)和Podfile.lock
文件非常一致掉房,只要把自己替換的庫(kù)例如libwebp相關(guān)的內(nèi)容都從Podfile.lock
拷到項(xiàng)目/Pods/Manifest.lock
即可,如果Podfile.lock
里面的版本不是最新的慰丛,你就手動(dòng)改一下版本號(hào)以及spec checksum即可卓囚。
可參考:http://www.reibang.com/p/06f507d2987d
例如podfile.lock里需要改成醬紫,manifest.lock必須相關(guān)的和podfile.lock保持一致诅病,包括checksum的編號(hào):
- libwebp (1.1.0):
- libwebp/demux (= 1.1.0)
- libwebp/mux (= 1.1.0)
- libwebp/webp (= 1.1.0)
- libwebp/demux (1.1.0):
- libwebp/webp
- libwebp/mux (1.1.0):
- libwebp/demux
- libwebp/webp (1.1.0)
SPEC CHECKSUMS:
……
KVOController: d72ace34afea42468329623b3379ab3cd1d286b6
libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
那么CHECKSUMS的編號(hào)是如何生成的呢哪亿?我最開(kāi)始以為是遠(yuǎn)程倉(cāng)庫(kù)的commit號(hào),后來(lái)查了一下看到上面的refer文章才知道是這么生成的:
pod ipc spec /Users/用戶名/.cocoapods/repos/trunk/Specs/1/9/2/libwebp/1.1.0/libwebp.podspec.json | openssl sha1
946cb3063cea9236285f7e9a8505d806d30e07f3
然后你再pod install就可以成功了吼~ (P.S. 想要知道自己卡在哪里贤笆,加上--verbose就可以啦)
2. app在后臺(tái)時(shí)keyboard監(jiān)聽(tīng)也會(huì)被觸發(fā)
這個(gè)問(wèn)題是我昨天晚上QA小姐姐發(fā)現(xiàn)的蝇棉,就是如果我們注冊(cè)了observer監(jiān)聽(tīng)UIKeyboardDidHideNotification
以及UIKeyboardWillShowNotification
,那么即使我們的app在后臺(tái)芥永,當(dāng)我們?cè)谄渌鸻pp例如微信里面調(diào)用了鍵盤篡殷,自己app里面監(jiān)聽(tīng)keyboard的方法仍舊會(huì)被觸發(fā),這樣在兩個(gè)app來(lái)回切換并且有微信鍵盤的時(shí)候就看著很奇怪埋涧。
于是我Google了一下 (https://stackoverflow.com/questions/34409566/keyboardwillshow-gets-called-for-other-apps-keyboards)板辽,解決方案就是在監(jiān)聽(tīng)觸發(fā)的handler函數(shù)里面最好先判斷自己的app是不是active的,如果不是就return不去處理這個(gè)監(jiān)聽(tīng):
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowNotificationHandler) name:UIKeyboardWillShowNotification object:nil];
- (void) keyboardWillShowNotificationHandler {
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive) {
return;
}
……
}
3. 淘口令
各種復(fù)制口令到app然后會(huì)打開(kāi)一個(gè)頁(yè)面棘催,最早感覺(jué)就是淘寶這么做的劲弦,它的是咋實(shí)現(xiàn)的呢?first step是從你的剪貼板拿到你復(fù)制的東西醇坝,second step我猜是把那串神奇的字符串發(fā)給后端瓶您,我們只要pop up一個(gè)webview,內(nèi)容后端會(huì)來(lái)填充頁(yè)面。
所以我們先來(lái)看一下從剪貼板拿你的magic string:
// 程序進(jìn)入前臺(tái)
-(void)applicationWillEnterForeground:(UIApplication*)application {
UIPasteboard* pasteboard= [UIPasteboard generalPasteboard];
NSLog(@"pasteboard: %@", pasteboard.string);
}
我拿拼多多的app來(lái)fake了一下呀袱,然后復(fù)制了一個(gè)口令打開(kāi)拼多多看到的頁(yè)面層級(jí)是這樣的:(神奇的發(fā)現(xiàn)陌陌的反fake比拼多多做的好贸毕。。夜赵。
所以彈出的頁(yè)面是原生的不是web哈~
但是如果你無(wú)網(wǎng)復(fù)制粘貼進(jìn)拼多多就沒(méi)有反應(yīng)明棍,所以雖然不是直接pop一個(gè)web讓后端做,但應(yīng)該是從剪貼板拿到string以后發(fā)給了后端寇僧,后端會(huì)告訴前端彈哪種view摊腋,并把需要的填充數(shù)據(jù)返還。
4. NSCopying實(shí)現(xiàn)
NSCopying的實(shí)現(xiàn)我們需要把屬性一個(gè)一個(gè)的賦值就很煩嘁傀,如果增加了新的屬性還得手動(dòng)記得改NSCopying兴蒸,如果忘了就有可能犯錯(cuò)。
所以其實(shí)比較靠譜的是利用runtime细办,獲取所有屬性橙凳,然后賦值:
+ (nonnull id)copyObject:(nonnull NSObject *)object
{
Class clazz = [object class];
id objCopy = [[clazz alloc] init];
u_int count;
objc_property_t *properties = class_copyPropertyList(clazz, &count);
for (int i = 0; i < count ; i++) {
const char* propertyName = property_getName(properties[I]);
NSString *name = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
id value = [object valueForKey:name];
if([value respondsToSelector:@selector(copyWithZone:)]) {
[objCopy setValue:[value copy] forKey:name];
} else if (value != nil) {
[objCopy setValue:value forKey:name];
}
}
free(properties);
return objCopy;
}
然后如果有些屬性比較特殊,你可以再單獨(dú)處理一下~
5. 如何讓alert顯示在頂層
我們有個(gè)需求要在webview上面顯示一個(gè)alert笑撞,但是如果拿topVC是nil岛啸,所以不能present alert。
如果我們想搞個(gè)alert在任何時(shí)候都能顯示到最頂層可以這么搞茴肥,繼承UIAlertController:
@interface TopLiveAlertController ()
@property (nonatomic, weak) UIWindow *previousKeyWindow;
@property (nonatomic, strong) UIWindow *alertContainerKeyWindow;
@end
@implementation TopLiveAlertController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)showInTop {
self.previousKeyWindow = [UIApplication sharedApplication].keyWindow;
[self.alertContainerKeyWindow removeFromSuperview];
self.alertContainerKeyWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.alertContainerKeyWindow.rootViewController = [UIViewController new];
self.alertContainerKeyWindow.windowLevel = UIWindowLevelAlert + 1;
[self.alertContainerKeyWindow makeKeyAndVisible];
[self.alertContainerKeyWindow.rootViewController presentViewController:self animated:YES completion:nil];
}
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
[super dismissViewControllerAnimated:flag completion:completion];
[self.alertContainerKeyWindow removeFromSuperview];
self.alertContainerKeyWindow = nil;
[self.previousKeyWindow makeKeyAndVisible];
}
@end
- 這里牽扯到一個(gè)小問(wèn)題坚踩,
windowLevel
是啥?
關(guān)于UIWindow可以參考一下這個(gè)http://www.reibang.com/p/1c2ac0fa2e4a
其實(shí)windowLevel就是window的z軸瓤狐,默認(rèn)都是UIWindowLevelNormal (0)的:
UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar API_UNAVAILABLE(tvos);
6. 自帶emoji的行間距問(wèn)題
參考:http://www.reibang.com/p/715181fbc28b
這個(gè)又是一個(gè)很煩人的問(wèn)題瞬铸,就是我們正常的文字如果用YYTextLayout *textLayout = [YYTextLayout layoutWithContainer:container text:attributeString];
獲取高度完全木有問(wèn)題,例如一個(gè)14font的文字可能計(jì)算完的高度是16础锐,但是如果這行包括了emoji嗓节,這行的高度可能算出來(lái)就是24了。
于是如果我們的聊天氣泡的高度是根據(jù)計(jì)算出啦的行高+上下間距來(lái)定的高度郁稍,就會(huì)出現(xiàn)在有emoji的時(shí)候行高突然變多赦政。這個(gè)問(wèn)題我實(shí)在很頭疼,所以換了個(gè)方法來(lái)做耀怜,我們嘗試固定行高恢着,然后不加邊距。這樣其實(shí)無(wú)論是文字還是文字&emoji都要放進(jìn)一樣的高的container~
類似醬紫:
YYTextLinePositionSimpleModifier *modifier = [YYTextLinePositionSimpleModifier new];
modifier.fixedLineHeight = 24;
YYTextContainer *container = [YYTextContainer new];
container.size = CGSizeMake(constrainedWidth, 10000);
container.linePositionModifier = modifier;
YYTextLayout *textLayout = [YYTextLayout layoutWithContainer:container text:attributeString];
return CGSizeMake(textLayout.textBoundingSize.width, textLayout.textBoundingSize.height);
但加入了emoji以后的基線偏移我就實(shí)在解決不了了頭禿财破。掰派。
7. app如何換色(清明黑白灰)
這個(gè)就和我之前好像寫過(guò)一個(gè)主題換色是一個(gè)道理,就也是把顏色都寫到配置文件左痢,然后用啥就用它的key靡羡。但這個(gè)注意UI千萬(wàn)得規(guī)定一個(gè)用色標(biāo)準(zhǔn)系洛、按小哥哥的說(shuō)法就是色庫(kù),要不就很麻煩每次得自己加很多顏色的key value略步。描扯。。
8. UIApplicationDidBecomeActiveNotification
這個(gè)通知大家都很熟悉了趟薄,當(dāng)app的狀態(tài)變?yōu)閍ctive就會(huì)收到绽诚,一個(gè)很熟悉的場(chǎng)景就是我們退到后臺(tái)以及回到前臺(tái)。
但是有一個(gè)比較特殊杭煎,在iOS13的時(shí)候如果我們app已經(jīng)在前臺(tái)恩够,用戶下拉通知欄可以看到其他app的push,這個(gè)時(shí)候會(huì)先resign active羡铲,在become active蜂桶,即使用戶還沒(méi)回到我們app,還在專心的讀push也切。
但是如果你下拉的是右上角的工具欄(有手電筒扑媚、亮度等),則只會(huì)resign active贾费,當(dāng)你上滑把工具欄收起再次看到app的時(shí)候才會(huì)become active钦购。
App的各種狀態(tài)可以參考:http://www.reibang.com/p/ee4add97c96a檐盟,經(jīng)常會(huì)搞不清background和resign active褂萧,其實(shí)就是從active到inactive會(huì)發(fā)resign active通知,如果從inactive到background則是enter background通知葵萎。
9. viewDidLoad觸發(fā)
我以前一直以為viewDidLoad是只有頁(yè)面最開(kāi)始出現(xiàn)的時(shí)候才會(huì)觸發(fā)导犹,但是這周遇到一個(gè)問(wèn)題是當(dāng)VC都快銷毀的時(shí)機(jī)還出發(fā)了viewDidLoad,然后看堆棧是由于VC的getView方法被調(diào)用了羡忘。
但不是每次你調(diào)用vc.view就會(huì)執(zhí)行viewDidLoad哦谎痢,聽(tīng)起來(lái)也很奇怪,應(yīng)該是懶加載的卷雕,因?yàn)樯蟼€(gè)場(chǎng)景可能view已經(jīng)被銷毀了节猿,或者VC剛初始化你就調(diào)用了他的view,所以當(dāng)VC的view第一次nil被讀取的時(shí)候就會(huì)觸發(fā)viewDidLoad吼漫雕。