iOS开发的奇技淫巧

最近好像这个话题很热啊,我不生产奇技淫巧我是大自然的搬运工

结构体简便初始化

// 不加(CGRect)强转也不会warning
CGRect rect1 = {1, 2, 3, 4};
CGRect rect2 = {.origin.x=5, .size={10, 10}}; // {5, 0, 10, 10}
CGRect rect3 = {1, 2}; // {1, 2, 0, 0}

三元条件表达式的两元使用

NSString *string = inputString ?: @"default";
NSString *string = inputString ? inputString : @"default"; // 等价

到这里让我想起了一个笑话

C有一个鲜为人知的运算符叫”趋向于”, 写作“–>”。比如说如果要实现一个倒数的程序,我们可以定义一个变量x,然后让它趋向与0:

#include <stdio.h>  
int main(int argc, char** argv) {
int x = 10;
while (x --> 0) {
printf("%d ", x);
}
return 0;
}

会打印出:
9 8 7 6 5 4 3 2 1 0

数组的下标初始化

const int numbers[] = {
[1] = 3,
[2] = 2,
[3] = 1,
[5] = 12306
};
// {0, 3, 2, 1, 0, 12306}

这个特性可以用来做枚举值和字符串的映射

typedef NS_ENUM(NSInteger, XXType){
XXType1,
XXType2
};
const NSString *XXTypeNameMapping[] = {
[XXType1] = @"Type1",
[XXType2] = @"Type2"
};

可变参数类型的block

一个block像下面一样声明:

void(^block1)(void);
void(^block2)(int a);
void(^block3)(NSNumber *a, NSString *b);

如果block的参数列表为空的话,相当于可变参数(不是void)

void(^block)(); // 返回值为void,参数可变的block
block = block1; // 正常
block = block2; // 正常
block = block3; // 正常
block(@1, @"string"); // 对应上面的block3
block(@1); // block3的第一个参数为@1,第二个为nil

这样,block的主调和回调之间可以通过约定来决定block回传回来的参数是什么,有几个。如一个对网络层的调用:

- (void)requestDataWithApi:(NSInteger)api block:(void(^)())block
{
if (api == 0) {
block(1, 2);
}
else if (api == 1) {
block(@"1", @2, @[@"3", @"4", @"5"]);
}
}

主调者知道自己请求的是哪个Api,那么根据约定,他就知道block里面应该接受哪几个参数:

[server requestDataWithApi:0 block:^(NSInteger a, NSInteger b){
// ...
}];
[server requestDataWithApi:1 block:^(NSString *s, NSNumber *n, NSArray *a){
// ...
}];

这个特性在Reactive Cocoa的-combineLatest:reduce:等类似方法中已经使用的相当好了。

+ (RACSignal *)combineLatest:(id<NSFastEnumeration>)signals reduce:(id (^)())reduceBlock;

小括号内联复合表达式

A compound statement enclosed in parentheses原谅我的渣翻译- -,来自《gcc官方对此的说明》,源自gcc对c的扩展,如今被clang继承。

RETURN_VALUE_RECEIVER = ({
// Do whatever you want
RETURN_VALUE; // 返回值
});

于是乎可以发挥想象力了:

self.backgroundView = ({
UIView *view = [[UIView alloc] initWithFrame:self.view.bounds];
view.backgroundColor = [UIColor redColor];
view.alpha = 0.8f;
view;
});

有点像block和内联函数的结合体,它最大的意义在于将代码整理分块,将同一个逻辑层级的代码包在一起;同时对于一个无需复用小段逻辑,也免去了重量级的调用函数,如:

self.result = ({
double result = 0;
for (int i = 0; i <= M_2_PI; i+= M_PI_4) {
result += sin(i);
}
result;
});

这样使得代码量增大时层次仍然能比较明确。

PS: 返回值和代码块结束点必须在结尾

快捷的收键盘代码

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.view endEditing:YES];
}

去掉导航条返回键 title

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

改变uitextfield placeholder的颜色和位置

- (void) drawPlaceholderInRect:(CGRect)rect {
[[UIColor blueColor] setFill];
[self.placeholder drawInRect:rect withFont:self.font lineBreakMode:UILineBreakModeTailTruncation alignment:self.textAlignment];
}

电话 xxxx转xxxx

一般客服电话经常会出现需要转接分机的情况,比如 10010转6666 的形式,只需要如下的 URL scheme 就可以了:

tel:10010,6666

拨打的时候会首先拨通 10010,接通之后会紧接着按下6666四个数字

参考

『专题』iOS&Xcode奇技淫巧
iOS开发的一些奇巧淫技
iOS开发的一些奇巧淫技2
objc非主流代码技巧