Chaosky's Notes

iOS Tips

更新日志

工具类

提取assets.car中的图片

iPhone 6 屏幕揭秘

Xcode

iOS开发证书显示“此证书的签发者无效”解决方法

原因:Apple Worldwide Developer Relations Certification Authority Intermediate Certificate 证书过期。

解决办法:

  1. 在『钥匙串访问』的菜单项中选择显示已过期证书,删除已过期证书。
  2. 在Apple PKI网页下载最新的证书:https://developer.apple.com/certificationauthority/AppleWWDRCA.cer ,双击导入即可。

Xcode常见报错锦囊

Xcode只显示iOS Simulator的identifier,没有显示device version

当安装多个版本的Xcode时,有可能会在某个Xcode中出现相同机型相同版本的多个模拟器,如图所示:alt

解决办法:

  1. 退出Xcode.app, iOS Simulator.app等
  2. 执行命令关闭模拟器服务:sudo killall -9 com.apple.CoreSimulator.CoreSimulatorService
  3. 执行命令删除所有已经存在的模拟器:rm -rf ~/Library/Developer/CoreSimulator/Devices
  4. 重启Xcode,就可以看到在~/Library/Developer/CoreSimulator/Devices目录,新生成的模拟器设备。
  5. 这样就不会有重复的模拟器设备了。

如何删除Xcode Downloads中的Components

alt

解决办法:

  1. 退出Xcode.app, iOS Simulator.app等
  2. cd /Library/Developer/CoreSimulator/Profiles/Runtimes,如果已下载,可以在该目录中找到simruntime文件
    alt
  3. 删除对应模拟器版本
  4. 清空目录:rm -rf ~/Library/Developer/CoreSimulator/Devices
  5. 重启Xcode,让Xcode重新生成模拟器设备.

完全卸载 Xcode

终端输入以下命令:sudo /Developer/Library/uninstall-devtools —mode=all

Xcode 6.x的Scheme选项在 OS X El Capitan(10.11)中消失

解决办法:将Xcode的窗口拉长或者全屏就会出现

Xcode 7.1 无法安装Alcatraz插件

  1. 关闭Xcode
  2. 移除之前安装的Xcode默认项

    1
    defaults delete com.apple.dt.Xcode DVTPlugInManagerNonApplePlugIns-Xcode-7.0
  3. 卸载Alcatraz

    1
    rm -rf ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins/Alcatraz.xcplugin
  4. 移除所有通过Alcatraz安装的包

    1
    rm -rf ~/Library/Application\ Support/Alcatraz/
  5. 更新已安装插件的DVTPlugInCompatibilityUUID到7.1

    1
    find ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins -name Info.plist -maxdepth 3 | xargs -I{} defaults write {} DVTPlugInCompatibilityUUIDs -array-add `defaults read /Applications/Xcode.app/Contents/Info DVTPlugInCompatibilityUUID`
  6. 重置Xcode Select
    sudo xcode-select --reset

  7. 打开Xcode
  8. 安装 Alcatraz

    1
    curl -fsSL https://raw.github.com/supermarin/Alcatraz/master/Scripts/install.sh | sh
  9. 重启Xcode

  10. 选择”Load Bundles”启动Xcode

Xcode 安装编译后的.app文件

安装之前需要启动iOS模拟器。

1
$ [Xcode安装路径]/Contents/Developer/usr/bin/simctl install booted [要安装的APP路径]

示例命令:

1
$ /Applications/Xcode.app/Contents/Developer/usr/bin/simctl install booted ~/Desktop/Examine.app

关闭 Xcode 8 终端打印一大堆日志

终端中打印的日志格式类似如下:

1
subsystem: com.apple.UIKit, category: HIDEventFiltered, enable_level: 0, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0

解决办法:Edit Scheme -> Run -> Arguments,在Environment Variables里边添加
OS_ACTIVITY_MODE = Disable

Snip20160920_4
Snip20160920_6

Xcode 7.x 中使用 Xcode 8 的新字体 SF Mono

从安装有 Xcode 8 的路径 /Application/Xcode.app/Contents/SharedFrameworks/DVTKit.framework/Versions/A/Resources/Fonts 下能找到这些字体。

选中所有字体拖拽到 Font Book.app(字体册)中,这样在 Xcode 7 中就可以选择了。

我自己将字体压缩了下,可以从这个地址下载: SF Mono 字体

Xcode Tips Tricks

Xcode Snippets

关于 Xcode Snippets 的介绍,可以通过这篇文章了解,以及如何生成自定义的代码片段。

安装 NShipster 提供的代码片段

仓库地址:https://github.com/Xcode-Snippets

  1. 安装命令行工具:gem install xcodesnippet
  2. 下载代码仓库:git clone https://github.com/Xcode-Snippets/Objective-C.git
  3. 进入该目录添加单条代码片段:xcodesnippet install path/to/source.m
  4. 该目录下有很多代码片段,可以通过命令批量添加:ls -1 | xargs -L1 xcodesnippet install

安装唐巧提供的代码片段

仓库地址:https://github.com/tangqiaoboy/xcode_tool

  1. 下载项目仓库:git clone https://github.com/tangqiaoboy/xcode_tool
  2. cd xcode_tool
  3. ./setup_snippets.sh

需要注意的是唐巧提供的代码片段,以后需要更新时可以直接仓库即可。

Foundation

获取程序沙盒Document目录

1
2
NSArray *paths = NSSearchPathForDictionariesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

UI

UITableView

取消cell的分割线

1
tableview.separatorStyle = UITableViewCellSeparatorStyleNone;

UITabelViewCell 的高亮状态的取消

1
2
3
4
5
6
7
8
9
10
11
12
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
//设置cell的背景是透明的。
cell.backgroundColor = [UIColor clearColor];
//取消cell的高亮状态
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
//使用下面的这个方法会导致cell不能响应点击事件
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}

设置分割线的左右偏移量

1
tableView.separatorInset = UIEdgeInsetsMake(0, 100, 0, 0);

TableView不显示没内容的Cell

1
self.tableView.tableFooterView = [[UIView alloc] init];

或者

1
tableView.separatorColor = [UIColor clearColor];

更改tableviewcell的小对勾的颜色

1
tableView.tintColor = [UIColor redColor];

UITableView 在页面加载完成后滚动到底部

方法原型:- (void)selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition

1
2
3
4
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionBottom];
}

隐藏GroupedTableView上边多余的间隔

第一个 Section 距离屏幕顶端有间隔,解决这个问题有三种方式。

Snip20161017_2

方式一:设置UITableViewHeader
1
2
CGRect frame = CGRectMake(0, 0, 0, CGFLOAT_MIN);
self.tableView.tableHeaderView = [[UIView alloc] initWithFrame:frame];
方式二:Storyboard

在第一个 Section 的上面拖进来一个空 UIView,设置view的height为0.1。
Snip20161017_4
Snip20161017_6

方式三:实现heightForHeaderInSection协议方法
1
2
3
4
5
6
- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if (section == 0)
return CGFLOAT_MIN;
return tableView.sectionHeaderHeight;
}

UINavigationBar

自定义leftBarbuttonItem,左滑返回手势失效

1
2
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:img style:UIBarButtonItemStylePlain target:self action:@selector(onBack:)];
self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;

滑动隐藏NavigationBar

navigationController.hidesBarsOnSwipe = Yes

导航条返回键隐藏title

[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60) forBarMetrics:UIBarMetricsDefault];

1
2
3
[self.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
self.navigationBar.shadowImage = [UIImage new];
self.navigationBar.translucent = YES;

改变导航栏里返回按钮的图标颜色

self.navigationController.navigationBar.tintColor = [UIColor whiteColor];

拉伸图片,图片不变形

UIImage *image = [[UIImage imageNamed:@"xxx"] stretchableImageWithLeftCapWidth:10 topCapHeight:10];

ScrollView莫名其妙不能在viewController划到顶

self.automaticallyAdjustsScrollViewInsets = NO;

怎么点击self.view就让键盘收起

两种方法:

1
2
3
4
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.view endEditing:YES];
}
1
2
3
4
5
6
7
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self becomeFirstResponder];
}

修改状态栏样式

在info.plist文件中添加UIViewControllerBasedStatusBarAppearance设置为NO
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

一句话解决倒计时问题,比如获取验证码倒计时

WKWebView的使用和各种坑的解决方法(OC+Swift)

UIImageView加载WebP格式的图片

WebP格式简介:http://isux.tencent.com/introduction-of-webp.html

WebP格式图片示例:http://7xooko.com1.z0.glb.clouddn.com/1.sm.webp

使用CocoaPods和SDWebImage

1
2
pod 'SDWebImage'
pod 'SDWebImage/WebP'

使用CocoaPods和YYWebImage

1
2
pod 'YYWebImage'
pod 'YYImage/WebP'

常用宏

尺寸宏

1
2
3
4
5
6
7
8
9
10
11
12
13
#define StatusBar_HEIGHT 20
#define NavigationBar_HEIGHT 44
#define NavigationBarIcon 20
#define TabBar_HEIGHT 49
#define TabBarIcon 30
#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)
#define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)

打印宏

1
2
3
4
5
6
//直接替换NSLog
#if DEBUG
#define NSLog(FORMAT, ...) fprintf(stderr,"\n%s:%d %s \t%s\n", [[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, __FUNCTION__, [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
#define NSLog(FORMAT, ...) nil
#endif

系统宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 获取版本
#define IOS_VERSION [[[UIDevice currentDevice] systemVersion] floatValue]
#define CurrentSystemVersion [[UIDevice currentDevice] systemVersion]
// 获取当前语言
#define CurrentLanguage ([[NSLocale preferredLanguages] objectAtIndex:0])
// 判断是真机还是模拟器
#if TARGET_OS_IPHONE
//iPhone Device
#endif
#if TARGET_IPHONE_SIMULATOR
//iPhone Simulator
#endif
// 检查系统版本
#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

内存宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 使用ARC和不使用ARC
#if __has_feature(objc_arc)
//compiling with ARC
#else
// compiling without ARC
#endif
#pragma mark - common functions
#define RELEASE_SAFELY(__POINTER) { [__POINTER release]; __POINTER = nil; }
//释放一个对象
#define SAFE_DELETE(P) if(P) { [P release], P = nil; }
#define SAFE_RELEASE(x) [x release];x=nil

颜色宏

1
2
3
4
5
6
7
8
9
10
11
12
// rgb颜色转换(16进制->10进制)
#define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
// RGB颜色
#define RGBCOLOR(r, g, b) [UIColor colorWithRed:(r)/255.0f green:(g)/255.0f blue:(b)/255.0f alpha:1]
#define RGBACOLOR(r, g, b, a) [UIColor colorWithRed:(r)/255.0f green:(g)/255.0f blue:(b)/255.0f alpha:(a)]
// 背景色
#define BACKGROUNDCOLOR [UIColor colorWithRed:242.0/255.0 green:236.0/255.0 blue:231.0/255.0 alpha:1.0]
// 清除背景色
#define CLEARCOLOR [UIColor clearColor]

其他宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//方正黑体简体字体定义
#define FONT(F) [UIFont fontWithName:@"FZHTJW--GB1-0" size:(F)]
//定义一个API
#define BaseAPIURL @"http://xxxxx/"
//登陆API
#define APILogin [APIURL stringByAppendingString:@"Login"]
//设置View的tag属性
#define VIEWWITHTAG(_OBJECT, _TAG) [_OBJECT viewWithTag : _TAG]
//G-C-D
#define BACK(block) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block)
#define MAIN(block) dispatch_async(dispatch_get_main_queue(),block)
//NSUserDefaults 实例化
#define USER_DEFAULT [NSUserDefaults standardUserDefaults]
//由角度获取弧度 有弧度获取角度
#define degreesToRadian(x) (M_PI * (x) / 180.0)
#define radianToDegrees(radian) (radian*180.0)/(M_PI)

单例宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#define SYNTHESIZE_SINGLETON_FOR_CLASS(classname) \
\
static classname *shared##classname = nil; \
\
+ (classname *)shared##classname \
{ \
@synchronized(self) \
{ \
if (shared##classname == nil) \
{ \
shared##classname = [[self alloc] init]; \
} \
} \
\
return shared##classname; \
} \
\
+ (id)allocWithZone:(NSZone *)zone \
{ \
@synchronized(self) \
{ \
if (shared##classname == nil) \
{ \
shared##classname = [super allocWithZone:zone]; \
return shared##classname; \
} \
} \
\
return nil; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return self; \
}

第三方库

键盘遮挡

IQKeyboardManager

iOS动画帧速计算类库

KMCGeigerCounter

Gif 播放

FLAnimatedImage

其他

版本号的命名

根据国际主流的惯例,使用「语义化版本(Semantic Versioning)」的命名方式,有时简称 SemVer。

语义化版本号(以下简称「版本号」)的格式是:\.\.\ 。如:1.4.15、6.2.0。

每一位版本号的含义

  • \ 即主版本号,俗称大版本升级。改动到主版本号时,标志着 API 发生了巨大变化,包括但不限于新增特性、修改机制、删除功能, 一般不兼容上一个主版本号
  • \ 即次版本号,俗称小版本升级。当我们进行常规的新增或修改功能时,改动次版本号,但是 必须是向前兼容的。这也意味着我们 不能直接删除某个功能。如若必要,我们可以在 changelog 中标记某项功能为「即将删除(Deprecated)」,然后在下一个大版本中将其彻底删除。
  • \ 即修订号,俗称 bug 修复。顾名思义,如果仅仅为了修复或调整一些小问题,我们就只改动修订号。

注意事项

  • 版本号前不要加 v。
  • 不要在数字前补 0。错误示例:01.12.03。
  • 每一位版本号按照 +1 的速度递增,不要在版本号之间跳跃。
  • 主版本号停留在 0 的版本号,即 0.x.x 应当视作还在内部开发阶段的代码。如果代码有公共 API,此时不宜对外公开。
  • 1.0.0 的版本号用于界定公共 API 的形成。
  • 当次版本号递增时,修订号归零;当主版本号递增时,次版本号、修订号归零。
  • 进行新的开发时,版本号从 0.1.0 开始。
  • 如果不小心把一个不兼容的改版当成了次版本号发行,应当发行一个新的次版本号来更正这个问题并且恢复向下兼容。注意 不能去修改已发行的版本。

一个典型的版本号发展示例

  • 0.1.0
  • 0.1.1
  • 0.1.2
  • 0.2.0
  • 1.0.0
  • 1.1.0
  • 1.1.1
  • ……
天下大事,必作于细!