iOS Tips

2016-05-26 ALEX LIN 更多博文 » 博客 » GitHub »

Tips

原文链接 http://chaosky.me/2016/05/26/iOS-Tips/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


更新日志

工具类

提取assets.car中的图片

iPhone 6 屏幕揭秘

iOS 通过获取iTunes上的APP信息,判断是否提醒升级APP

POST https://itunes.apple.com/CN/lookup?id=978591579

使用POST方法,id 是iTunes connect里APP信息的Apple ID:为APP自动生成的id

Charles 请求 HTTPS 出现 “You may need to configure your browser or application to trust the Charles Root Certificate. See SSL Proxying in the Help menu”

首先确认是否安装了 Charles 的证书,该证书在菜单 Help -> SSL Proxying 下,需要同时安装在 Mac 和 模拟器或者设备上,具体使用方法参见子菜单。其次因为 10.3 以上系统需要你在“证书信任设置”中信任 Charles 的证书。在设备上点击设置 -> 通用 -> 关于本机 -> 证书信任设置 -> 选择 Charles 的证书打开。

<!-- more -->

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默认项
    defaults delete com.apple.dt.Xcode DVTPlugInManagerNonApplePlugIns-Xcode-7.0
  3. 卸载Alcatraz
    rm -rf ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins/Alcatraz.xcplugin
  4. 移除所有通过Alcatraz安装的包
    rm -rf ~/Library/Application\ Support/Alcatraz/
  5. 更新已安装插件的DVTPlugInCompatibilityUUID到7.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 curl -fsSL https://raw.github.com/supermarin/Alcatraz/master/Scripts/install.sh | sh
  9. 重启Xcode
  10. 选择"Load Bundles"启动Xcode

Xcode 安装编译后的.app文件

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

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

示例命令:

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

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

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

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

Your browser does not support the video tag.

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

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

Understanding and Analyzing Application Crash Reports

详情链接:https://developer.apple.com/library/content/technotes/tn2151/_index.html

Xcode Tips Tricks

{% dplayer "url=http://7xooko.com1.z0.glb.clouddn.com/XcodeTipsTricks.mp4" "loop=yes" "theme=#FADFA3" "autoplay=false" "width=233px" %}

Foundation

获取程序沙盒Document目录

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

Objective-C 关键字

cheatsheetobjccd

消息传递

消息传递-how choose

队列组

// 1.创建队列组
dispatch_group_t group =  dispatch_group_create();
// 2.1第一个队列组异步
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});

// 2.2第二个队列组异步
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});

// 3.所有队列组异步线程结束后
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 等前面的异步操作都执行完毕后,回到主线程...
});

Swift 命令行程序接收用户输入

Swift 2.3
// 标准输入设备
let stdin = NSFileHandle.fileHandleWithStandardInput()
let inputData = keyboard.availableData
var inputStr = NSString(data: inputData, encoding: NSUTF8StringEncoding)
// 处理换行符
inputStr = inputStr?.stringByReplacingOccurrencesOfString("\n", withString: "")
// Swift 3.0
readLine()

获取变量内存地址

unsafeAddressOf(_:)

返回类对象的指针,类型为UnsafePointer

函数原型 func unsafeAddressOf(_ object: AnyObject) -> UnsafePointer

示例代码

var str = "Hello, playground"
print(unsafeAddressOf(str))
// 0x00007f859a404ca0

func withUnsafePointer(_: inout T, _: @noescape (UnsafePointer) throws -> Result) Invokes body with an UnsafePointer to arg and returns the result. Useful for calling Objective-C APIs that take "in/out" parameters (and default-constructible "out" parameters) by pointer.

函数原型 func withUnsafePointer(_ arg: inout T, _ body: @noescape (UnsafePointer) throws -> Result) rethrows -> Result

示例代码

struct Point {
    var x: CGFloat, y: CGFloat
}
var point = Point(x: 10, y: 10)
print(withUnsafePointer(&point) {UnsafePointer<Point>($0)})
// 0x0000000115e3f8d8

Quick Swift Tips and Tricks

Swift Quick Tips

UI

UITableView

取消cell的分割线

tableview.separatorStyle = UITableViewCellSeparatorStyleNone;

UITabelViewCell 的高亮状态的取消

-(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;
}

设置分割线的左右偏移量

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

TableView不显示没内容的Cell

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

或者

tableView.separatorColor = [UIColor clearColor];

更改tableviewcell的小对勾的颜色

tableView.tintColor = [UIColor redColor];

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

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

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

隐藏GroupedTableView上边多余的间隔

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

Snip20161017_2

方式一:设置UITableViewHeader
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协议方法
- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    if (section == 0)
        return CGFLOAT_MIN;
    return tableView.sectionHeaderHeight;
}

UINavigationBar

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

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];

NavigationBar透明而不是带模糊的效果

[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就让键盘收起

两种方法:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
   [self.view endEditing:YES];
}
- (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

pod 'SDWebImage'
pod 'SDWebImage/WebP'

使用CocoaPods和YYWebImage

pod 'YYWebImage'
pod 'YYImage/WebP'

UINavigationController

NavigationController中跳转

比如视图控制器,A——>B——>C——>D,现在在D页面中返回时跳转到不在导航控制器栈中的F视图控制器。

NSMutableArray * vcs = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
FViewController *fVC = [[FViewController alloc] init];
[vcs insertObject:fVC atIndex:vcs.count - 1];
self.navigationController.viewControllers = vcs;

常用宏

尺寸宏

#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)

打印宏

//直接替换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

系统宏

// 获取版本
#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)

内存宏

// 使用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

颜色宏

// 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]

其他宏

//方正黑体简体字体定义
#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)

单例宏

#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; \
}

第三方库

键盘遮挡

iOS动画帧速计算类库

KMCGeigerCounter

Gif 播放

FLAnimatedImage

其他

版本号的命名

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

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

每一位版本号的含义

  • <major> 即主版本号,俗称大版本升级。改动到主版本号时,标志着 API 发生了巨大变化,包括但不限于新增特性、修改机制、删除功能, 一般不兼容上一个主版本号
  • <minor> 即次版本号,俗称小版本升级。当我们进行常规的新增或修改功能时,改动次版本号,但是 必须是向前兼容的。这也意味着我们 不能直接删除某个功能。如若必要,我们可以在 changelog 中标记某项功能为「即将删除(Deprecated)」,然后在下一个大版本中将其彻底删除。
  • <patch> 即修订号,俗称 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
  • ……