[iOS] 七七八八的小姿势(5)

目录:

  1. 手动下载远程库来pod install / pod update
  2. app在后台时keyboard监听也会被触发
  3. 淘口令
  4. NSCopying实现
  5. 如何让alert显示在顶层
  6. 自带emoji的行间距问题
  7. app如何换色(清明黑白灰)
  8. UIApplicationDidBecomeActiveNotification
  9. viewDidLoad触发

1. 手动下载远程库来pod install / pod update

远程办公最怕的就是库版本发生变化,最近一拉最新代码经常就要pod install / pod repo update,每次都会卡死总要有各种问题,解决的方法大概就是github源就用4G,Google源就用代理+wifi,但是仍旧有的时候下不下来,于是我就在这里说一下如何直接从github下载然后让pod过去。

首先,如果是卡在了pod repo update,也就是从远端拿这些库最新的地址tag之类的,这个时候可以让能够过这一步的同事把/Users/用户名/.cocoapods/repos/trunk/Specs整个文件夹压缩打包发给你,直接替换就好,这样就可以成功的过update那关啦。

如果木有同事装好,可以直接从git上下载最新的:
https://github.com/CocoaPods/Specs

Specs里面存了哪些东西呢?我们以一会儿想要实验的库libwebp为例吧:


libwebp的各个版本

其实里面就是各个版本有个json以及json tag文件,我们打开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下载相应tag的库啦

"source": {
  "git": "https://chromium.googlesource.com/webm/libwebp",
  "tag": "v1.1.0"
},
git库

下载tar.gz或者zip都OK,然后解压放到我们project的本地pod里面:


工程里面的pod目录

注意哦你放进去的就是你从库仓库下载下来的文件,可能和你看到本地这个库的内容不一样,没关系的,你放进去再pod install过了以后就一样啦。

现在我们实现了下载需要的版本,并放入本地缓存,之后就可以来看如何pod install啦。在pod install的过程中,其实它就是比较了Podfile.lock文件和本地缓存文件版本差异,如果有区别就去远端下载,所以现在我们需要让本地缓存的库和podfile.lock里面一致,当然有一种方法是你把podfile.lock里面的库版本降低到你本地的版本,这样你也不用从远端下载就可以成功pod install啦,只是这样的话你需要经常stash着这个变化。

那么本地缓存的版本由什么管理呢?其实就是项目/Pods/Manifest.lock文件,你打开会发现和Podfile.lock文件非常一致,只要把自己替换的库例如libwebp相关的内容都从Podfile.lock拷到项目/Pods/Manifest.lock即可,如果Podfile.lock里面的版本不是最新的,你就手动改一下版本号以及spec checksum即可。

可参考:https://www.jianshu.com/p/06f507d2987d

例如podfile.lock里需要改成酱紫,manifest.lock必须相关的和podfile.lock保持一致,包括checksum的编号:

  - 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的编号是如何生成的呢?我最开始以为是远程仓库的commit号,后来查了一下看到上面的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在后台时keyboard监听也会被触发

这个问题是我昨天晚上QA小姐姐发现的,就是如果我们注册了observer监听UIKeyboardDidHideNotification以及UIKeyboardWillShowNotification,那么即使我们的app在后台,当我们在其他app例如微信里面调用了键盘,自己app里面监听keyboard的方法仍旧会被触发,这样在两个app来回切换并且有微信键盘的时候就看着很奇怪。

于是我Google了一下 (https://stackoverflow.com/questions/34409566/keyboardwillshow-gets-called-for-other-apps-keyboards),解决方案就是在监听触发的handler函数里面最好先判断自己的app是不是active的,如果不是就return不去处理这个监听:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowNotificationHandler) name:UIKeyboardWillShowNotification object:nil];

- (void) keyboardWillShowNotificationHandler {
  if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive) {
    return;
  }
  ……
}

3. 淘口令

各种复制口令到app然后会打开一个页面,最早感觉就是淘宝这么做的,它的是咋实现的呢?first step是从你的剪贴板拿到你复制的东西,second step我猜是把那串神奇的字符串发给后端,我们只要pop up一个webview,内容后端会来填充页面。

所以我们先来看一下从剪贴板拿你的magic string:

// 程序进入前台
-(void)applicationWillEnterForeground:(UIApplication*)application {
  UIPasteboard* pasteboard= [UIPasteboard generalPasteboard];
  NSLog(@"pasteboard: %@", pasteboard.string);
}

我拿拼多多的app来fake了一下,然后复制了一个口令打开拼多多看到的页面层级是这样的:(神奇的发现陌陌的反fake比拼多多做的好。。。


口令拼多多页面层级

所以弹出的页面是原生的不是web哈~

但是如果你无网复制粘贴进拼多多就没有反应,所以虽然不是直接pop一个web让后端做,但应该是从剪贴板拿到string以后发给了后端,后端会告诉前端弹哪种view,并把需要的填充数据返还。


4. NSCopying实现

NSCopying的实现我们需要把属性一个一个的赋值就很烦,如果增加了新的属性还得手动记得改NSCopying,如果忘了就有可能犯错。

所以其实比较靠谱的是利用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;
}

然后如果有些属性比较特殊,你可以再单独处理一下~


5. 如何让alert显示在顶层

我们有个需求要在webview上面显示一个alert,但是如果拿topVC是nil,所以不能present alert。

如果我们想搞个alert在任何时候都能显示到最顶层可以这么搞,继承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
  • 这里牵扯到一个小问题,windowLevel是啥?

关于UIWindow可以参考一下这个https://www.jianshu.com/p/1c2ac0fa2e4a

其实windowLevel就是window的z轴,默认都是UIWindowLevelNormal (0)的:

UIKIT_EXTERN const UIWindowLevel UIWindowLevelNormal;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelAlert;
UIKIT_EXTERN const UIWindowLevel UIWindowLevelStatusBar API_UNAVAILABLE(tvos);

6. 自带emoji的行间距问题

参考:https://www.jianshu.com/p/715181fbc28b

这个又是一个很烦人的问题,就是我们正常的文字如果用YYTextLayout *textLayout = [YYTextLayout layoutWithContainer:container text:attributeString];获取高度完全木有问题,例如一个14font的文字可能计算完的高度是16,但是如果这行包括了emoji,这行的高度可能算出来就是24了。

于是如果我们的聊天气泡的高度是根据计算出啦的行高+上下间距来定的高度,就会出现在有emoji的时候行高突然变多。这个问题我实在很头疼,所以换了个方法来做,我们尝试固定行高,然后不加边距。这样其实无论是文字还是文字&emoji都要放进一样的高的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以后的基线偏移我就实在解决不了了头秃。。


7. app如何换色(清明黑白灰)

这个就和我之前好像写过一个主题换色是一个道理,就也是把颜色都写到配置文件,然后用啥就用它的key。但这个注意UI千万得规定一个用色标准、按小哥哥的说法就是色库,要不就很麻烦每次得自己加很多颜色的key value。。。


8. UIApplicationDidBecomeActiveNotification

这个通知大家都很熟悉了,当app的状态变为active就会收到,一个很熟悉的场景就是我们退到后台以及回到前台。

但是有一个比较特殊,在iOS13的时候如果我们app已经在前台,用户下拉通知栏可以看到其他app的push,这个时候会先resign active,在become active,即使用户还没回到我们app,还在专心的读push。

但是如果你下拉的是右上角的工具栏(有手电筒、亮度等),则只会resign active,当你上滑把工具栏收起再次看到app的时候才会become active。

App的各种状态可以参考:https://www.jianshu.com/p/ee4add97c96a,经常会搞不清background和resign active,其实就是从active到inactive会发resign active通知,如果从inactive到background则是enter background通知。


9. viewDidLoad触发

我以前一直以为viewDidLoad是只有页面最开始出现的时候才会触发,但是这周遇到一个问题是当VC都快销毁的时机还出发了viewDidLoad,然后看堆栈是由于VC的getView方法被调用了。

但不是每次你调用vc.view就会执行viewDidLoad哦,听起来也很奇怪,应该是懒加载的,因为上个场景可能view已经被销毁了,或者VC刚初始化你就调用了他的view,所以当VC的view第一次nil被读取的时候就会触发viewDidLoad吼。

viewDidLoad调用栈
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容