CGRectContainsPoint 用法
UILongPressGestureRecognizer -> longPressed
长按手势重心 判断是否被view 包含
CGPoint point = [longPressed locationInView:self]; // 计算相对坐标(触摸手势的重心)
|
|
CGRectContainsPoint 用法
UILongPressGestureRecognizer -> longPressed
长按手势重心 判断是否被view 包含
CGPoint point = [longPressed locationInView:self]; // 计算相对坐标(触摸手势的重心)
|
|
在开发过程中,有时需要对用户输入的类型做判断,最常见是在注册页面即用户名和密码,直接上代码
|
|
一个约束优先级解说
http://www.cnblogs.com/siasyl/p/6775055.html
|
|
Masonry 动画 更新或者重置动画
然后用UIView 动画
|
|
Masonry的更新约束 mas_updateConstraints 更新约束 但是约束的相对实例不能变
mas_makeConstraints 删除以前的约束用新的约束
要求:
当键盘挡住输入框时,输入框自动向上弹到键盘上方。
实现:
这里需要使用到Masonry的另外一个方法mas_updateConstraints。这个方法用于更新控件约束。
具体的实现方式可以下载Demo来看,这里只贴出键盘弹出时的处理代码:
swift
|
|
|
|
由于业务需要,有时候需要动态加载Cell,考虑一些方案后采取把Cell高度设置为0.01的方法。但是约束为-10 的话会有错误信息(据说ios7 会崩溃) ,解决方法是降低优先级
|
|
From: http://www.jianshu.com/p/a24dd8638d28
个人喜欢用纯代码写东西,其中用到最多的就是Masonry,我整理一些使用过程中一些点,方便以后使用.(基本的语法就不说了)
mas_equalTo
,而相对于某个控件,或者某个控件的某个约束,我会使用equalTo
,如:make.size.mas_equalTo(CGSizeMake(100, 100));
make.center.equalTo(weakSelf.view);
setNeedsLayout
:告知页面需要更新,但是不会立刻开始更新。执行后会立刻调用layoutSubviews。layoutIfNeeded
:告知页面布局立刻更新。所以一般都会和setNeedsLayout
一起使用。如果希望立刻生成新的frame需要调用此方法,利用这点一般布局动画可以在更新布局后直接使用这个方法让动画生效。layoutSubviews
:系统重写布局setNeedsUpdateConstraints
:告知需要更新约束,但是不会立刻开始updateConstraintsIfNeeded
:告知立刻更新约束updateConstraints
:系统更新约束- (void)updateViewConstraints
ViewController的View在更新视图布局时,会先调用ViewController的updateViewConstraints 方法。我们可以通过重写这个方法去更新当前View的内部布局,而不用再继承这个View去重写-updateConstraints方法。我们在重写这个方法时,务必要调用 super 或者 调用当前View的 -updateConstraints 方法。
// 防止block中的循环引用
__weak typeof(self) weakSelf = self;
UIView view = [UIView new];
view.backgroundColor = [UIColor brownColor];
[self.view addSubview:view];
//使用mas_makeConstraints添加约束
[view mas_makeConstraints:^(MASConstraintMaker make) {
// 添加大小约束(make就是要添加约束的控件view)
make.size.mas_equalTo(CGSizeMake(200, 200));
// 添加居中约束(居中方式与self相同)
make.center.equalTo(weakSelf.view);
}];
UIView* blackView = [UIView new];
blackView.backgroundColor = [UIColor blackColor];
[self.view addSubview:blackView];
[blackView mas_makeConstraints:^(MASConstraintMaker *make) {
//添加约束大小
make.size.mas_equalTo(CGSizeMake(100, 100));
//在 左,上 添加约束 (左、上约束都是20)
make.left.and.top.mas_equalTo(20);
}];
UIView* grayView = [UIView new];
grayView.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:grayView];
[grayView mas_makeConstraints:^(MASConstraintMaker *make) {
// 大小、上边距约束与黑色view相同
make.size.and.top.equalTo(blackView);
// 添加右边距约束(这里的间距是有方向性的,左、上边距约束为正数,右、下边距约束为负数)
make.right.mas_equalTo(-20);
}];
(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
__weak typeof(self) weakSelf = self;
_textField = [UITextField new];
_textField.backgroundColor = [UIColor redColor];
[self.view addSubview:_textField];
[_textField mas_makeConstraints:^(MASConstraintMaker *make) {
//left,right,centerx,y 不能共存只能有其二
make.left.mas_equalTo(20);
// make.right.mas_equalTo(-60);
make.centerX.equalTo(weakSelf.view);
make.height.mas_equalTo(40);
make.bottom.mas_equalTo(0);
}];
// 注册键盘通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrameNotification:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHideNotification:) name:UIKeyboardWillHideNotification object:nil];
}
(void)keyboardWillChangeFrameNotification:(NSNotification *)notification {
// 获取键盘基本信息(动画时长与键盘高度)
NSDictionary userInfo = [notification userInfo];
CGRect rect = [userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGFloat keyboardHeight = CGRectGetHeight(rect);
CGFloat keyboardDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// 修改下边距约束
[_textField mas_updateConstraints:^(MASConstraintMaker make) {
make.bottom.mas_equalTo(-keyboardHeight);
}];
// 更新约束
[UIView animateWithDuration:keyboardDuration animations:^{
[self.view layoutIfNeeded];
}];
}
(void)keyboardWillHideNotification:(NSNotification *)notification {
// 获得键盘动画时长
NSDictionary *userInfo = [notification userInfo];
CGFloat keyboardDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// 修改为以前的约束(距下边距0)
[_textField mas_updateConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(0);
}];
// 更新约束
[UIView animateWithDuration:keyboardDuration animations:^{
[self.view layoutIfNeeded];
}];
}
(void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event {
[super touchesBegan:touches withEvent:event];
[self.view endEditing:YES];
}
array 的 mas_distributeViewsAlongAxis withFixedSpacing
变化的是控件 长度或宽度
定义一个存放三个控件的数组NSArray *array;
array = @[greenView,redView,blueView];
数组里面的元素不能小于2个,要不会报错 views to distribute need to bigger than one
直接调用下面的方法:
- (void)getHorizontalone
{
//方法一,array 的 mas_distributeViewsAlongAxis
/**
* 多个控件固定间隔的等间隔排列,变化的是控件的长度或者宽度值
*
* @param axisType 轴线方向
* @param fixedSpacing 间隔大小
* @param leadSpacing 头部间隔
* @param tailSpacing 尾部间隔
*/
// MASAxisTypeHorizontal 水平
// MASAxisTypeVertical 垂直
[arrayList mas_distributeViewsAlongAxis:MASAxisTypeHorizontal
withFixedSpacing:20
leadSpacing:5
tailSpacing:5];
[arrayList mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(60);
make.height.mas_equalTo(100);
}];
}
array de mas_distributeViewsAlongAxis withFixedItemLength
控件size不变,变化的是间隙
- (void)getVertical
{
/**
* 多个固定大小的控件的等间隔排列,变化的是间隔的空隙
*
* @param axisType 轴线方向
* @param fixedItemLength 每个控件的固定长度或者宽度值
* @param leadSpacing 头部间隔
* @param tailSpacing 尾部间隔
*/
[arrayList mas_distributeViewsAlongAxis:MASAxisTypeVertical
withFixedItemLength:60
leadSpacing:40
tailSpacing:10];
[arrayList mas_makeConstraints:^(MASConstraintMaker *make) {
// make.top.mas_equalTo(100);
// make.height.mas_equalTo(100);
make.left.mas_equalTo(20);
make.right.mas_equalTo(-20);
}];
}
NSArray+MASAdditions
中multiplier
实现等间距 for (NSUInteger i = 0; i < 4; i++) {
UIView *itemView = [self getItemViewWithIndex:i];
[_containerView addSubview:itemView];
[itemView mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.and.height.equalTo(@(ITEM_SIZE));
make.centerY.equalTo(_containerView.mas_centerY);
make.centerX.equalTo(_containerView.mas_right).multipliedBy(((CGFloat)i + 1) / ((CGFloat)ITEM_COUNT + 1));
}];
}
UIView *lastSpaceView = [UIView new];
lastSpaceView.backgroundColor = [UIColor greenColor];
[_containerView1 addSubview:lastSpaceView];
[lastSpaceView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.top.and.bottom.equalTo(_containerView1);
}];
for (NSUInteger i = 0; i < ITEM_COUNT; i++) {
UIView *itemView = [self getItemViewWithIndex:i];
[_containerView1 addSubview:itemView];
[itemView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.and.width.equalTo(@(ITEM_SIZE));
make.left.equalTo(lastSpaceView.mas_right);
make.centerY.equalTo(_containerView1.mas_centerY);
}];
UIView *spaceView = [UIView new];
spaceView.backgroundColor = [UIColor greenColor];
[_containerView1 addSubview:spaceView];
[spaceView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(itemView.mas_right).with.priorityHigh(); // 降低优先级,防止宽度不够出现约束冲突
make.top.and.bottom.equalTo(_containerView1);
make.width.equalTo(lastSpaceView.mas_width);
}];
lastSpaceView = spaceView;
}
[lastSpaceView mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(_containerView1.mas_right);
}];
和面方法4一样,利用spaceView来实现
UIView* bgView = [[UIView alloc]init];
bgView.backgroundColor = [UIColor yellowColor];
[self.view addSubview:bgView];
[bgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.right.mas_equalTo(0);
make.top.mas_equalTo(@100);
make.height.mas_equalTo(@100);
}];
listText = @[@"北京",@"地大吴波啊",@"你大爷",@"我们的爱哎哎"];
UIView *lastSpaceView = nil;
for(int i = 0 ; i < listText.count; i ++)
{
UILabel* label = [UILabel new];
label.text = listText[i];
label.backgroundColor = RANDOMCOLOR;
[bgView addSubview:label];
UIView* lineView = [UIView new];
lineView.backgroundColor = [UIColor redColor];
[bgView addSubview:lineView];
[label mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.mas_equalTo(0);
if (lastSpaceView)
{
NSLog(@"存在 lastView");
make.left.equalTo(lastSpaceView.mas_right).mas_offset(@20);
}else
{
NSLog(@"不存在存在 lastView");
make.left.equalTo(bgView.mas_left);
}
make.height.equalTo(bgView);
}];
lastSpaceView = label;
[lineView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.and.bottom.mas_equalTo(0);
make.width.mas_equalTo(1);
make.left.mas_equalTo(label.mas_right).mas_offset(@10);
}];
}
UIView* bgView = [UIView new];
bgView.backgroundColor = [UIColor purpleColor];
[self.view addSubview:bgView];
UILabel* titleLab = [UILabel new];
titleLab.backgroundColor = [UIColor redColor];
titleLab.textAlignment = NSTextAlignmentCenter;
titleLab.font = [UIFont systemFontOfSize:15.f];
titleLab.text = @"曹操——《短歌行》";
[bgView addSubview:titleLab];
UILabel* contentLab = [UILabel new];
contentLab.numberOfLines = 0 ;
contentLab.textAlignment = NSTextAlignmentCenter;
contentLab.backgroundColor = [UIColor brownColor];
contentLab.font = [UIFont systemFontOfSize:13.f];
contentLab.text = @" 对酒当歌,人生几何? 譬如朝露,去日苦多。\n 慨当以慷,忧思难忘。 何以解忧?唯有杜康。\n 青青子衿,悠悠我心。 但为君故,沉吟至今。\n 呦呦鹿鸣,食野之苹。 我有嘉宾,鼓瑟吹笙。\n 明明如月,何时可掇? 忧从中来,不可断绝。\n 越陌度阡,枉用相存。 契阔谈宴,心念旧恩。\n 月明星稀,乌鹊南飞。 绕树三匝,何枝可依?\n 山不厌高,海不厌深。 周公吐哺,天下归心。";
[bgView addSubview:contentLab];
//思路: 父视图的上间距等于title的上间距,父视图的下间距等于content的下间距
__weak typeof(self) weakSelf = self;
[bgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_offset(@30);
make.right.mas_offset(@-30);
make.centerY.equalTo(weakSelf.view);
}];
[titleLab mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.right.mas_equalTo(@0);
}];
[contentLab mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.mas_equalTo(@0);
make.top.equalTo(titleLab.mas_bottom).mas_offset(@10);
make.bottom.equalTo(bgView);
}];
文/栋飞
//一些扒的别人的记录
//width >= 200 && width <= 400 make.width.greaterThanOrEqualTo(@200); make.width.lessThanOrEqualTo(@400)
.priority
允许你指定一个精确的优先级,数值越大优先级越高.最高1000..priorityHigh
等价于 UILayoutPriorityDefaultHigh
.优先级值为 750..priorityMedium
介于高优先级和低优先级之间,优先级值在 250~750之间..priorityLow
等价于 UILayoutPriorityDefaultLow
, 优先级值为 250.
make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow();
make.top.equalTo(label.mas_top).with.priority(600);
//使 centerX和 centerY = button1make.center.equalTo(button1)
//使 centerX = superview.centerX - 5, centerY = superview.centerY + 10 make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))
make.width.equalTo(superview).multipliedBy(0.25);
Masonry框架初次使用遇到的坑 frame为0
From: http://www.jianshu.com/p/ad9c075a7547
实现自适应布局的一个非常方便的方法就是使用Masonry框架,然而使用Masonry布局的时候,并不能立刻反应到frame
的改变上,比如:
UIView *parent = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
UIImageView *child = [UIView alloc] init];
[parent addSubview:child];
[child mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeMake(20,20));
make.top.left.mas_equalTo(50);
}];
NSLog(@"%@",redView);
打印结果:
** <UIImageView: 0x7fb222605550; frame = (0 0; 0 0); layer = <CALayer: 0x7fb22260b3a0>>**
可以发现,虽然使用Masonry进行布局和约束,但是子视图child
的frame
仍然为(0, 0, 0 ,0).
而这时候如果有需求要设置child的形状为圆形,就得知道它的frame,像下面这样写肯定不会设置成功的:
[child mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeMake(20,20));
make.top.left.mas_equalTo(50);
}];
child.layer.cornerRadius = child.bounds.size.width/2;
child.layer.masksToBounds = YES; //设置头像为圆形
因为这时候的frame还是0。我想会不会是因为block中的处理是放在另一个线程中异步进行的,block还没执行完就已经走到了下面使用frame的代码,(一阵狂喜,好聪明。。。),所以马上把代码改写:
[child mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeMake(20,20));
make.top.left.mas_equalTo(50);
child.layer.cornerRadius = child.bounds.size.width/2;
child.layer.masksToBounds = YES; //设置头像为圆形
}];
然而并没有什么卵用。。。
没办法只能问谷歌了,然后找到了Masonry约束下获取frame
的方法:
使用masonry的实质还是调用了ios7以后的autolayout,如果要更新frame
,需要调用layoutIfNeeded
函数进行布局,然后所约束的控件才会按照约束条件,生成当前布局相应的frame
和bounds
。这样就可以利用这两个属性来进行图片圆角剪裁。而调用layoutIfNeeded
的目的是让系统调用layoutSubviews
方法,我们也可以直接在这个方法里获取frame,因为这时候开始layout subviews,Masonry已经计算出了真实的frame。
下面附上关于autolayout更新几个方法的区别:
setNeedsLayout:告知页面需要更新,但是不会立刻开始更新。执行后会立刻调用layoutSubviews。
layoutIfNeeded:告知页面布局立刻更新。所以一般都会和setNeedsLayout一起使用。如果希望立刻生成新的frame需要调用此方法,利用这点,动画可以在更新布局后直接使用这个方法让动画生效。
layoutSubviews:系统重写布局
setNeedsUpdateConstraints:告知需要更新约束,但是不会立刻开始
updateConstraintsIfNeeded:告知立刻更新约束
updateConstraints:系统更新约束
From: http://www.cnblogs.com/gfxxbk/p/5827301.html
1、Masonry概述
用优雅的代码方式编写Autolayout
省去了苹果官方恶心的Autolayout代码
大大提高了开发效率
2、常用方法
这个方法只会添加新的约束
|
|
这个方法会将以前的所有约束删掉,添加新的约束
|
|
这个方法将会覆盖以前的某些特定的约束
|
|
3、约束类型
width(宽)\height(高)\size(大小)
// 宽度约束
make.width.mas_equalTo(100);
// 高度约束
make.height.mas_equalTo(100);
// 大小约束(与上面两句等价)
make.size.mas_equalTo(CGSizeMake(100, 100));
left\leading(左边界)\right\trailing(右边界)\top(顶部边界)\bottom(底部边界)
// 左边(leading类似)
make.left.mas_equalTo(self.view).offset(50);
// 右边(trailing类似)
make.right.equalTo(self.view).offset(-20);
// 顶部
make.top.equalTo(self.view).offset(20);
// 底部
make.bottom.mas_equalTo(self.view).offset(-50);
center\centerX\centerY
// 居中(水平+垂直)
// 尺寸是父控件的一半
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(self.view).multipliedBy(0.5);
make.center.mas_equalTo(self.view); // 与下面两句代码等价
// make.centerX.mas_equalTo(self.view);
// make.centerY.mas_equalTo(self.view);
}];
edges
// UIEdgeInsets 内边距
make.edges.mas_equalTo(self.view).insets(UIEdgeInsetsMake(50, 50, 50, 50));
4、mas_前缀修饰与不修饰的区别
默认情况下:
mas_equalTo有自动包装功能,比如自动将20包装为@20
equalTo没有自动包装功能
mas_equalTo的功能强于 > equalTo,可以一直使用mas_equalTo
默认情况下:
width是make对象的一个属性,用来添加宽度约束用的,表示对宽度进行约束
mas_width是一个属性值,用来当做equalTo的参数,表示某个控件的宽度属性
mas_height、mas_centerX以此类推
如果添加了下面的宏,那么 mas_equalTo 和 equalTo 就没有区别
#define MAS_SHORTHAND_GLOBALS // 注意:这个宏一定要添加到#import “Masonry.h”前面
如果添加了下面的宏,mas_width也可以写成width
#define MAS_SHORTHAND
//define this constant if you want to use Masonry without the 'mas_' prefix
#define MAS_SHORTHAND
//define this constant if you want to enable auto-boxing for default syntax
#define MAS_SHORTHAND_GLOBALS
#import "Masonry.h"
- (void)viewDidLoad {
[super viewDidLoad];
// 蓝色控件
UIView *blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
[self.view addSubview:blueView];
// 红色控件
UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
// 添加约束
CGFloat margin = 20;
CGFloat height = 50;
[blueView makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view.left).offset(margin);
make.right.equalTo(redView.left).offset(-margin);
make.bottom.equalTo(self.view.bottom).offset(-margin);
make.height.equalTo(height);
make.top.equalTo(redView.top);
make.bottom.equalTo(redView.bottom);
make.width.equalTo(redView.width);
}];
[redView makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.view.right).offset(-margin);
}];
}
5、可有可无的用法
以下方法都仅仅是为了提高可读性,可有可无
with
使用情况示例代码
// 尺寸限制:100x100
// 位置:粘着父控件右下角,间距是20
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {
// 宽度约束
make.width.equalTo(@100);
// 高度约束
make.height.equalTo(@100);
// 右边
make.right.equalTo(self.view.mas_right).with.offset(-20);
// 顶部
make.top.equalTo(self.view.mas_top).with.offset(20);
}];
and
使用情况示例代码
// 尺寸限制:100x100
// 位置:粘着父控件右下角,间距是20
[blueView mas_makeConstraints:^(MASConstraintMaker *make) {
// 宽度高度约束
make.width.and.height.mas_equalTo(100);
// 右边
make.right.equalTo(self.view).offset(-20);
// 顶部
make.top.equalTo(self.view).offset(20);
}];
From: http://liuyanwei.jumppo.com/2015/06/14/ios-library-masonry.html
Masonry是一个轻量级的布局框架 拥有自己的描述语法 采用更优雅的链式语法封装自动布局 简洁明了 并具有高可读性 而且同时支持 iOS 和 Max OS X。 Masonry是一个用代码写iOS或os界面的库,可以代替Auto layout。 Masonry的github地址:https://github.com/SnapKit/Masonry
mas_makeConstraints 是给view添加约束,约束有几种,分别是边距,宽,高,左上右下距离,基准线。添加过约束后可以有修正,修正 有offset(位移)修正和multipliedBy(倍率)修正
语法一般是 make.equalTo or make.greaterThanOrEqualTo or make.lessThanOrEqualTo + 倍数和位移修正
注意点1: 使用 mas_makeConstraints方法的元素必须事先添加到父元素的中,例如[self.view addSubview:view];
注意点2: mas_equalTo 和 equalTo 区别:mas_equalTo 比equalTo多了类型转换操作,一般来说,大多数时候两个方法都是 通用的,但是对于数值元素使用mas_equalTo。对于对象或是多个属性的处理,使用equalTo。特别是多个属性时,必须使用equalTo,例如 make.left.and.right.equalTo(self.view);
注意点3: 注意到方法with和and,这连个方法其实没有做任何操作,方法只是返回对象本身,这这个方法的左右完全是为了方法写的时候的可读性 。make.left.and.right.equalTo(self.view);和make.left.right.equalTo(self.view);是完全一样的,但是明显的加了and方法的语句可读性 更好点。
// exp1: 中心点与self.view相同,宽度为400*400
-(void)exp1{
UIView *view = [UIView new];
[view setBackgroundColor:[UIColor redColor]];
[self.view addSubview:view];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.view);
make.size.mas_equalTo(CGSizeMake(400,400));
}];
}
//exp2: 上下左右边距都为10
-(void)exp2{
UIView *view = [UIView new];
[view setBackgroundColor:[UIColor redColor]];
[self.view addSubview:view];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
// make.left.equalTo(self.view).with.offset(10);
// make.right.equalTo(self.view).with.offset(-10);
// make.top.equalTo(self.view).with.offset(10);
// make.bottom.equalTo(self.view).with.offset(-10);
}];
}
//exp3 让两个高度为150的view垂直居中且等宽且等间隔排列 间隔为10
-(void)exp3{
UIView *view1 = [UIView new];
[view1 setBackgroundColor:[UIColor redColor]];
[self.view addSubview:view1];
UIView *view2 = [UIView new];
[view2 setBackgroundColor:[UIColor redColor]];
[self.view addSubview:view2];
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self.view.mas_centerY);
make.height.mas_equalTo(150);
make.width.mas_equalTo(view2.mas_width);
make.left.mas_equalTo(self.view.mas_left).with.offset(10);
make.right.mas_equalTo(view2.mas_left).offset(-10);
}];
[view2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self.view.mas_centerY);
make.height.mas_equalTo(150);
make.width.mas_equalTo(view1.mas_width);
make.left.mas_equalTo(view1.mas_right).with.offset(10);
make.right.equalTo(self.view.mas_right).offset(-10);
}];
}
![][2]
[2]: http://liuyanwei.jumppo.com/assets/uploads/masonry_1.png
//高级布局练习 iOS自带计算器布局
-(void)exp4{
//申明区域,displayView是显示区域,keyboardView是键盘区域
UIView *displayView = [UIView new];
[displayView setBackgroundColor:[UIColor blackColor]];
[self.view addSubview:displayView];
UIView *keyboardView = [UIView new];
[self.view addSubview:keyboardView];
//先按1:3分割 displView(显示结果区域)和 keyboardView(键盘区域)
[displayView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_top);
make.left.and.right.equalTo(self.view);
make.height.equalTo(keyboardView).multipliedBy(0.3f);
}];
[keyboardView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(displayView.mas_bottom);
make.bottom.equalTo(self.view.mas_bottom);
make.left.and.right.equalTo(self.view);
}];
//设置显示位置的数字为0
UILabel *displayNum = [[UILabel alloc]init];
[displayView addSubview:displayNum];
displayNum.text = @"0";
displayNum.font = [UIFont fontWithName:@"HeiTi SC" size:70];
displayNum.textColor = [UIColor whiteColor];
displayNum.textAlignment = NSTextAlignmentRight;
[displayNum mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.right.equalTo(displayView).with.offset(-10);
make.bottom.equalTo(displayView).with.offset(-10);
}];
//定义键盘键名称,?号代表合并的单元格
NSArray *keys = @[@"AC",@"+/-",@"%",@"÷"
,@"7",@"8",@"9",@"x"
,@"4",@"5",@"6",@"-"
,@"1",@"2",@"3",@"+"
,@"0",@"?",@".",@"="];
int indexOfKeys = 0;
for (NSString *key in keys){
//循环所有键
indexOfKeys++;
int rowNum = indexOfKeys %4 ==0? indexOfKeys/4:indexOfKeys/4 +1;
int colNum = indexOfKeys %4 ==0? 4 :indexOfKeys %4;
NSLog(@"index is:%d and row:%d,col:%d",indexOfKeys,rowNum,colNum);
//键样式
UIButton *keyView = [UIButton buttonWithType:UIButtonTypeCustom];
[keyboardView addSubview:keyView];
[keyView setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[keyView setTitle:key forState:UIControlStateNormal];
[keyView.layer setBorderWidth:1];
[keyView.layer setBorderColor:[[UIColor blackColor]CGColor]];
[keyView.titleLabel setFont:[UIFont fontWithName:@"Arial-BoldItalicMT" size:30]];
//键约束
[keyView mas_makeConstraints:^(MASConstraintMaker *make) {
//处理 0 合并单元格
if([key isEqualToString:@"0"] || [key isEqualToString:@"?"] ){
if([key isEqualToString:@"0"]){
[keyView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(keyboardView.mas_height).with.multipliedBy(.2f);
make.width.equalTo(keyboardView.mas_width).multipliedBy(.5);
make.left.equalTo(keyboardView.mas_left);
make.baseline.equalTo(keyboardView.mas_baseline).with.multipliedBy(.9f);
}];
}if([key isEqualToString:@"?"]){
[keyView removeFromSuperview];
}
}
//正常的单元格
else{
make.width.equalTo(keyboardView.mas_width).with.multipliedBy(.25f);
make.height.equalTo(keyboardView.mas_height).with.multipliedBy(.2f);
//按照行和列添加约束,这里添加行约束
switch (rowNum) {
case 1:
{
make.baseline.equalTo(keyboardView.mas_baseline).with.multipliedBy(.1f);
keyView.backgroundColor = [UIColor colorWithRed:205 green:205 blue:205 alpha:1];
}
break;
case 2:
{
make.baseline.equalTo(keyboardView.mas_baseline).with.multipliedBy(.3f);
}
break;
case 3:
{
make.baseline.equalTo(keyboardView.mas_baseline).with.multipliedBy(.5f);
}
break;
case 4:
{
make.baseline.equalTo(keyboardView.mas_baseline).with.multipliedBy(.7f);
}
break;
case 5:
{
make.baseline.equalTo(keyboardView.mas_baseline).with.multipliedBy(.9f);
}
break;
default:
break;
}
//按照行和列添加约束,这里添加列约束
switch (colNum) {
case 1:
{
make.left.equalTo(keyboardView.mas_left);
}
break;
case 2:
{
make.right.equalTo(keyboardView.mas_centerX);
}
break;
case 3:
{
make.left.equalTo(keyboardView.mas_centerX);
}
break;
case 4:
{
make.right.equalTo(keyboardView.mas_right);
[keyView setBackgroundColor:[UIColor colorWithRed:243 green:127 blue:38 alpha:1]];
}
break;
default:
break;
}
}
}];
}
}
本例子使用的baseline去控制高度位置,这似乎不是太准,如果想要精准控制高度位置,可以使用一行一行添加的方法,每次当前行的top去equelTo上一行的bottom。 给个提示:
for(遍历所有行)
for(遍历所以列)
//当前行约束根据上一行去设置
......
-(void)createUI{
UIView *titleView = [UIView new];
titleView.backgroundColor = [UIColor redColor];
UIView *caredView = [UIView new];
[self.view addSubview:caredView];
UIView *brifeView = [UIView new];
[self.view addSubview:brifeView];
//self.view
self.view.backgroundColor = [UIColor colorWithWhite:0.965 alpha:1.000];
//thrm
UIImageView *plantThrm = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"defalutPlantReferenceIcon"]];
[self.view addSubview:plantThrm];
[plantThrm mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.top.equalTo(self.view).with.offset(10);
}];
//title
[self.view addSubview:titleView];
UIImageView *bgTitleView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"bg-plant-reference-title"]];
[titleView addSubview:bgTitleView];
[titleView mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.view.mas_right);
make.left.equalTo(plantThrm.mas_right).with.offset(20);
make.centerY.equalTo(plantThrm.mas_centerY);
}];
[bgTitleView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(titleView);
}];
UILabel *title = [[UILabel alloc]init];
title.textColor = [UIColor whiteColor];
title.font = [UIFont fontWithName:@"Heiti SC" size:26];
title.text = _reference.name;
[titleView addSubview:title];
[title mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(titleView.mas_left).offset(10);
make.width.equalTo(titleView.mas_width);
make.centerY.equalTo(titleView.mas_centerY);
}];
//植物养护
UILabel *caredTitle = [[UILabel alloc]init];
caredTitle.textColor = [UIColor colorWithRed:0.172 green:0.171 blue:0.219 alpha:1.000];
caredTitle.font = [UIFont fontWithName:@"Heiti SC" size:10];
caredTitle.text = @"植物养护";
[self.view addSubview:caredTitle];
[caredTitle mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(plantThrm.mas_bottom).with.offset(20);
make.left.and.right.equalTo(self.view).with.offset(10);
make.height.mas_equalTo(10);
}];
//将图层的边框设置为圆脚
caredView.layer.cornerRadius = 5;
caredView.layer.masksToBounds = YES;
//给图层添加一个有色边框
caredView.layer.borderWidth = 1;
caredView.layer.borderColor = [[UIColor colorWithWhite:0.521 alpha:1.000] CGColor];
caredView.backgroundColor = [UIColor whiteColor];
[caredView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(caredTitle.mas_bottom).with.offset(5);
make.left.equalTo(self.view.mas_left).with.offset(10);
make.right.equalTo(self.view.mas_right).with.offset(-10);
make.height.equalTo(brifeView);
}];
//植物简介
UILabel *brifeTitle = [[UILabel alloc]init];
brifeTitle.textColor = [UIColor colorWithRed:0.172 green:0.171 blue:0.219 alpha:1.000];
brifeTitle.font = [UIFont fontWithName:@"Heiti SC" size:10];
brifeTitle.text = @"植物简介";
[self.view addSubview:brifeTitle];
[brifeTitle mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(caredView.mas_bottom).with.offset(20);
make.left.and.right.equalTo(self.view).with.offset(10);
make.height.mas_equalTo(10);
}];
//将图层的边框设置为圆脚
brifeView.layer.cornerRadius = 5;
brifeView.layer.masksToBounds = YES;
//给图层添加一个有色边框
brifeView.layer.borderWidth = 1;
brifeView.layer.borderColor = [[UIColor colorWithWhite:0.521 alpha:1.000] CGColor];
brifeView.backgroundColor = [UIColor whiteColor];
[brifeView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(brifeTitle.mas_bottom).with.offset(5);
make.left.equalTo(self.view.mas_left).with.offset(10);
make.right.equalTo(self.view.mas_right).with.offset(-10);
make.bottom.equalTo(self.view.mas_bottom).with.offset(-10);
make.height.equalTo(caredView);
}];
}
完成之后如下图
//把块拆分为四行
-(void)createIndexUIWithView:(UIView *)view{
//拆分四行
UIView *row1 = [UIView new];
UIView *row2 = [UIView new];
UIView *row3 = [UIView new];
UIView *row4 = [UIView new];
[view addSubview:row1];
[view addSubview:row2];
[view addSubview:row3];
[view addSubview:row4];
[row1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.and.left.equalTo(view);
make.height.equalTo(view.mas_height).multipliedBy(0.25);
make.top.equalTo(view.mas_top);
}];
[row2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.and.left.equalTo(view);
make.top.equalTo(row1.mas_bottom);
make.height.equalTo(view.mas_height).multipliedBy(0.25);
}];
[row3 mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(view.mas_right);
make.top.equalTo(row2.mas_bottom);
make.height.equalTo(view.mas_height).multipliedBy(0.25);
make.left.equalTo(view.mas_left);
}];
[row4 mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.and.left.equalTo(view);
make.top.equalTo(row3.mas_bottom);
make.height.equalTo(view.mas_height).multipliedBy(0.25);
}];
[self createIndexRowUI:PlantReferenceWaterIndex withUIView:row1];
[self createIndexRowUI:PlantReferenceSumIndex withUIView:row2];
[self createIndexRowUI:PlantReferenceTemperatureIndex withUIView:row3];
[self createIndexRowUI:PlantReferenceElectrolyteIndex withUIView:row4];
}
//构造每行的UI
-(void)createIndexRowUI:(PlantReferenceIndex) index withUIView:(UIView *)view{
//index标题
UILabel *indexTitle = [UILabel new];
indexTitle.font = [UIFont fontWithName:@"HeiTi SC" size:14];
indexTitle.textColor = [UIColor colorWithWhite:0.326 alpha:1.000];
[view addSubview:indexTitle];
[indexTitle mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(view.mas_left).with.offset(20);
make.centerY.equalTo(view.mas_centerY);
}];
switch (index) {
case PlantReferenceWaterIndex:
{
indexTitle.text = @"水分";
UIImageView * current;
for(int i=1;i<=5;i++){
if(i<_reference.waterIndex){
current = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"icon_water_light"]];
}else{
current = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"icon_water_dark"]];
}
[view addSubview:current];
//间距12%,左边留空30%
[current mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(view.mas_right).with.multipliedBy(0.12*(i-1) +0.3);
make.centerY.equalTo(view.mas_centerY);
}];
}
}
break;
case PlantReferenceSumIndex:
{
indexTitle.text = @"光照";
UIImageView * current;
for(int i=1;i<=5;i++){
if(i<_reference.temperatureIndex){
current = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"icon_summer_light"]];
}else{
current = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"icon_summer_dark"]];
}
[view addSubview:current];
//间距12%,左边留空30%
[current mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(view.mas_right).with.multipliedBy(0.12*(i-1) +0.3);
make.centerY.equalTo(view.mas_centerY);
}];
}
}
break;
case PlantReferenceTemperatureIndex:
{
indexTitle.text = @"温度";
UIImageView * current;
for(int i=1;i<=5;i++){
if(i<_reference.sumIndex){
current = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"icon_temperature_light"]];
}else{
current = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"icon_temperature_dark"]];
}
[view addSubview:current];
//间距12%,左边留空30%
[current mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(view.mas_right).with.multipliedBy(0.12*(i-1) +0.3);
make.centerY.equalTo(view.mas_centerY);
}];
}
}
break;
case PlantReferenceElectrolyteIndex:
{
indexTitle.text = @"肥料";
UIImageView * current;
for(int i=1;i<=5;i++){
if(i<_reference.electrolyteIndex){
current = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"icon_electolyte_light"]];
}else{
current = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"icon_electolyte_dark"]];
}
[view addSubview:current];
//间距12%,左边留空30%
[current mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(view.mas_right).with.multipliedBy(0.12*(i-1) +0.3);
make.centerY.equalTo(view.mas_centerY);
}];
}
}
break;
default:
break;
}
}
//在步骤1createui的基础上,做了一些微调。
-(void)createUI{
self.title = _reference.name;
UIView *titleView = [UIView new];
UIView *caredView = [UIView new];
[self.view addSubview:caredView];
UITextView *brifeView = [UITextView new];
[self.view addSubview:brifeView];
//self.view
self.view.backgroundColor = [UIColor colorWithWhite:0.965 alpha:1.000];
//thrm
UIImageView *plantThrm = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"defalutPlantReferenceIcon"]];
[self.view addSubview:plantThrm];
[plantThrm mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.and.top.equalTo(self.view).with.offset(10);
}];
//title
[self.view addSubview:titleView];
UIImageView *bgTitleView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"bg-plant-reference-title"]];
[titleView addSubview:bgTitleView];
[titleView mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.view.mas_right);
make.left.equalTo(plantThrm.mas_right).with.offset(20);
make.centerY.equalTo(plantThrm.mas_centerY);
}];
[bgTitleView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(titleView);
}];
UILabel *title = [[UILabel alloc]init];
title.textColor = [UIColor whiteColor];
title.font = [UIFont fontWithName:@"Heiti SC" size:26];
title.text = _reference.name;
[titleView addSubview:title];
[title mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(titleView.mas_left).offset(10);
make.width.equalTo(titleView.mas_width);
make.centerY.equalTo(titleView.mas_centerY);
}];
//植物养护
UILabel *caredTitle = [[UILabel alloc]init];
caredTitle.textColor = [UIColor colorWithRed:0.172 green:0.171 blue:0.219 alpha:1.000];
caredTitle.font = [UIFont fontWithName:@"Heiti SC" size:10];
caredTitle.text = @"植物养护";
[self.view addSubview:caredTitle];
[caredTitle mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(plantThrm.mas_bottom).with.offset(20);
make.left.and.right.equalTo(self.view).with.offset(10);
make.height.mas_equalTo(10);
}];
//植物养护 数据
[self createIndexUIWithView:caredView];
//将图层的边框设置为圆脚
caredView.layer.cornerRadius = 5;
caredView.layer.masksToBounds = YES;
//给图层添加一个有色边框
caredView.layer.borderWidth = 1;
caredView.layer.borderColor = [[UIColor colorWithWhite:0.521 alpha:1.000] CGColor];
caredView.backgroundColor = [UIColor whiteColor];
[caredView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(caredTitle.mas_bottom).with.offset(5);
make.left.equalTo(self.view.mas_left).with.offset(10);
make.right.equalTo(self.view.mas_right).with.offset(-10);
make.height.equalTo(brifeView);
}];
//植物简介
UILabel *brifeTitle = [[UILabel alloc]init];
brifeTitle.textColor = [UIColor colorWithRed:0.172 green:0.171 blue:0.219 alpha:1.000];
brifeTitle.font = [UIFont fontWithName:@"Heiti SC" size:10];
brifeTitle.text = @"植物简介";
[self.view addSubview:brifeTitle];
[brifeTitle mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(caredView.mas_bottom).with.offset(20);
make.left.and.right.equalTo(self.view).with.offset(10);
make.height.mas_equalTo(10);
}];
//将图层的边框设置为圆脚
brifeView.layer.cornerRadius = 5;
brifeView.layer.masksToBounds = YES;
//给图层添加一个有色边框
brifeView.layer.borderWidth = 1;
brifeView.layer.borderColor = [[UIColor colorWithWhite:0.447 alpha:1.000] CGColor];
brifeView.backgroundColor = [UIColor whiteColor];
//文字样式
// brifeView.textColor = [UIColor colorWithWhite:0.352 alpha:1.000];
// brifeView.font = [UIFont fontWithName:@"HeiTi SC" size:12];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc]init];
paragraphStyle.lineHeightMultiple = 20.f;
paragraphStyle.maximumLineHeight = 25.f;
paragraphStyle.minimumLineHeight = 15.f;
paragraphStyle.alignment = NSTextAlignmentJustified;
NSDictionary *attributes = @{ NSFontAttributeName:[UIFont systemFontOfSize:12], NSParagraphStyleAttributeName:paragraphStyle, NSForegroundColorAttributeName:[UIColor colorWithWhite:0.447 alpha:1.000]};
//植物简介数据
//brifeView.text = _reference.brief;
brifeView.attributedText = [[NSAttributedString alloc] initWithString: _reference.brief attributes:attributes];
[brifeView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(brifeTitle.mas_bottom).with.offset(5);
make.left.equalTo(self.view.mas_left).with.offset(10);
make.right.equalTo(self.view.mas_right).with.offset(-10);
make.bottom.equalTo(self.view.mas_bottom).with.offset(-10);
make.height.equalTo(caredView);
}];
}
}
采坑: 一个UIScrollView 上add 一些view
view 布局用masonry,
如下, 高度 和底部 有一个算多余约束 无报错, 但是UIScrollView, contentOffset 页面已跳转就会变为0,0
|
|
Objective-C编码规范,内容来自苹果、谷歌的文档翻译,『博爱』的编码经验和对其它资料的总结。
详情可前往:开源项目『BABaseProject』!
转载请注明出处。
##概要
Objective-C是一门面向对象的动态编程语言,主要用于编写iOS和Mac应用程序。关于Objective-C的编码规范,苹果和谷歌都已经有很好的总结:
本文主要整合了对上述文档的翻译、作者自己的编程经验和其他的相关资料,为公司总结出一份通用的编码规范。
##代码格式
###使用空格而不是制表符Tab
不要在工程里使用Tab键,使用空格来进行缩进。在Xcode > Preferences > Text Editing
将Tab和自动缩进都设置为4个空格。(Google的标准是使用两个空格来缩进,但这里还是推荐使用Xcode默认的设置。)
###每一行的最大长度
同样的,在Xcode > Preferences > Text Editing > Page guide at column:
中将最大行长设置为80,过长的一行代码将会导致可读性问题。
###函数的书写
一个典型的Objective-C函数应该是这样的:
|
|
在-
和(void)
之间应该有一个空格,第一个大括号{
的位置在函数所在行的末尾,同样应该有一个空格。(我司的C语言规范要求是第一个大括号单独占一行,但考虑到OC较长的函数名和苹果SDK代码的风格,还是将大括号放在行末。)
如果一个函数有特别多的参数或者名称很长,应该将其按照:
来对齐分行显示:
|
|
在分行时,如果第一段名称过短,后续名称可以以Tab的长度(4个空格)为单位进行缩进:
|
|
###函数调用
函数调用的格式和书写差不多,可以按照函数的长短来选择写在一行或者分成多行:
|
|
以下写法是错误的:
|
|
###@public和@private标记符
@public和@private标记符应该以一个空格来进行缩进:
|
|
###协议(Protocols)
在书写协议的时候注意用<>
括起来的协议和类型名之间是没有空格的,比如IPCConnectHandler()<IPCPreconnectorDelegate>
,这个规则适用所有书写协议的地方,包括函数声明、类声明、实例变量等等:
|
|
###闭包(Blocks)
根据block的长度,有不同的书写规则:
}
应该和调用block那行代码的第一个非空字符对齐。^
和(
之间,^
和{
之间都没有空格,参数列表的右括号)
和{
之间有一个空格。
|
|
###数据结构的语法糖
应该使用可读性更好的语法糖来构造NSArray
,NSDictionary
等数据结构,避免使用冗长的alloc
,init
方法。
如果构造代码写在一行,需要在括号两端留有一个空格,使得被构造的元素于与构造语法区分开来:
|
|
如果构造代码不写在一行内,构造元素需要使用两个空格来进行缩进,右括号]
或者}
写在新的一行,并且与调用语法糖那行代码的第一个非空字符对齐:
|
|
构造字典时,字典的Key和Value与中间的冒号:
都要留有一个空格,多行书写时,也可以将Value对齐:
|
|
##命名规范
###基本原则
####清晰
命名应该尽可能的清晰和简洁,但在Objective-C中,清晰比简洁更重要。由于Xcode强大的自动补全功能,我们不必担心名称过长的问题。
|
|
不要使用单词的简写,拼写出完整的单词:
然而,有部分单词简写在Objective-C编码过程中是非常常用的,以至于成为了一种规范,这些简写可以在代码中直接使用,下面列举了部分:
|
|
命名方法或者函数时要避免歧义
|
|
####一致性
整个工程的命名风格要保持一致性,最好和苹果SDK的代码保持统一。不同类中完成相似功能的方法应该叫一样的名字,比如我们总是用count
来返回集合的个数,不能在A类中使用count
而在B类中使用getNumber
。
###使用前缀
如果代码需要打包成Framework给别的工程使用,或者工程项目非常庞大,需要拆分成不同的模块,使用命名前缀是非常有用的。
前缀由大写的字母缩写组成,比如Cocoa中NS
前缀代表Founation框架中的类,IB
则代表Interface Builder框架。
可以在为类、协议、函数、常量以及typedef宏命名的时候使用前缀,但注意不要为成员变量或者方法使用前缀,因为他们本身就包含在类的命名空间内。
命名前缀的时候不要和苹果SDK框架冲突。
###命名类和协议(Class&Protocol)
类名以大写字母开头,应该包含一个名词来表示它代表的对象类型,同时可以加上必要的前缀,比如NSString
, NSDate
, NSScanner
, NSApplication
等等。
而协议名称应该清晰地表示它所执行的行为,而且要和类名区别开来,所以通常使用ing
词尾来命名一个协议,比如NSCopying
,NSLocking
。
有些协议本身包含了很多不相关的功能,主要用来为某一特定类服务,这时候可以直接用类名来命名这个协议,比如NSObject
协议,它包含了id对象在生存周期内的一系列方法。
###命名头文件(Headers)
源码的头文件名应该清晰地暗示它的功能和包含的内容:
如果头文件内只定义了单个类或者协议,直接用类名或者协议名来命名头文件,比如NSLocale.h
定义了NSLocale
类。
如果头文件内定义了一系列的类、协议、类别,使用其中最主要的类名来命名头文件,比如NSString.h
定义了NSString
和NSMutableString
。
每一个Framework都应该有一个和框架同名的头文件,包含了框架中所有公共类头文件的引用,比如Foundation.h
Framework中有时候会实现在别的框架中类的类别扩展,这样的文件通常使用被扩展的框架名
+Additions
的方式来命名,比如NSBundleAdditions.h
。
###命名方法(Methods)
Objective-C的方法名通常都比较长,这是为了让程序有更好地可读性,按苹果的说法“好的方法名应当可以以一个句子的形式朗读出来”。
方法一般以小写字母打头,每一个后续的单词首字母大写,方法名中不应该有标点符号(包括下划线),有两个例外:
PDF
,TIFF
等。如果方法表示让对象执行一个动作,使用动词打头来命名,注意不要使用do
,does
这种多余的关键字,动词本身的暗示就足够了:
|
|
如果方法是为了获取对象的一个属性值,直接用属性名称来命名这个方法,注意不要添加get
或者其他的动词前缀:
|
|
对于有多个参数的方法,务必在每一个参数前都添加关键词,关键词应当清晰说明参数的作用:
|
|
不要用and
来连接两个参数,通常and
用来表示方法执行了两个相对独立的操作(从设计上来说,这时候应该拆分成两个独立的方法):
|
|
方法的参数命名也有一些需要注意的地方:
pointer
,ptr
这样的字眼去表示指针,参数本身的类型足以说明下面列举了一些常用参数名:
|
|
###存取方法(Accessor Methods)
存取方法是指用来获取和设置类属性值的方法,属性的不同类型,对应着不同的存取方法规范:
|
|
命名存取方法时不要将动词转化为被动形式来使用:
可以使用can
,should
,will
等词来协助表达存取方法的意思,但不要使用do
,和does
:
为什么Objective-C中不适用get
前缀来表示属性获取方法?因为get
在Objective-C中通常只用来表示从函数指针返回值的函数:
###命名委托(Delegate)
当特定的事件发生时,对象会触发它注册的委托方法。委托是Objective-C中常用的传递消息的方式。委托有它固定的命名范式。
一个委托方法的第一个参数是触发它的对象,第一个关键词是触发对象的类名,除非委托方法只有一个名为sender
的参数:
根据委托方法触发的时机和目的,使用should
,will
,did
等关键词
###集合操作类方法(Collection Methods)
有些对象管理着一系列其它对象或者元素的集合,需要使用类似“增删查改”的方法来对集合进行操作,这些方法的命名范式一般为:
|
|
注意,如果返回的集合是无序的,使用NSSet
来代替NSArray
。如果需要将元素插入到特定的位置,使用类似于这样的命名:
|
|
如果管理的集合元素中有指向管理对象的指针,要设置成weak
类型以防止引用循环。
下面是SDK中NSWindow
类的集合操作方法:
|
|
###命名函数(Functions)
在很多场合仍然需要用到函数,比如说如果一个对象是一个单例,那么应该使用函数来代替类方法执行相关操作。
函数的命名和方法有一些不同,主要是:
函数名的第一个单词通常是一个动词,表示方法执行的操作:
|
|
如果函数返回其参数的某个属性,省略动词:
|
|
如果函数通过指针参数来返回值,需要在函数名中使用Get
:
|
|
函数的返回类型是BOOL时的命名:
|
|
###命名属性和实例变量(Properties&Instance Variables)
属性和对象的存取方法相关联,属性的第一个字母小写,后续单词首字母大写,不必添加前缀。属性按功能命名成名词或者动词:
|
|
属性也可以命名成形容词,这时候通常会指定一个带有is
前缀的get方法来提高可读性:
|
|
命名实例变量,在变量名前加上_
前缀(有些有历史的代码会将_
放在后面),其它和命名属性一样:
|
|
一般来说,类需要对使用者隐藏数据存储的细节,所以不要将实例方法定义成公共可访问的接口,可以使用@private
,@protected
前缀。
按苹果的说法,不建议在除了init
和dealloc
方法以外的地方直接访问实例变量,但很多人认为直接访问会让代码更加清晰可读,只在需要计算或者执行操作的时候才使用存取方法访问,我就是这种习惯,所以这里不作要求。
###命名常量(Constants)
如果要定义一组相关的常量,尽量使用枚举类型(enumerations),枚举类型的命名规则和函数的命名规则相同。
建议使用 NS_ENUM
和 NS_OPTIONS
宏来定义枚举类型,参见官方的 Adopting Modern Objective-C 一文:
|
|
定义bit map:
|
|
使用const
定义浮点型或者单个的整数型常量,如果要定义一组相关的整数常量,应该优先使用枚举。常量的命名规范和函数相同:
|
|
不要使用#define
宏来定义常量,如果是整型常量,尽量使用枚举,浮点型常量,使用const
定义。#define
通常用来给编译器决定是否编译某块代码,比如常用的:
|
|
注意到一般由编译器定义的宏会在前后都有一个__
,比如__MACH__
。
###命名通知(Notifications)
通知常用于在模块间传递消息,所以通知要尽可能地表示出发生的事件,通知的命名范式是:
[触发通知的类名] + [Did | Will] + [动作] + Notification
栗子:
|
|
##注释
读没有注释代码的痛苦你我都体会过,好的注释不仅能让人轻松读懂你的程序,还能提升代码的逼格。注意注释是为了让别人看懂,而不是仅仅你自己。
###文件注释
每一个文件都必须写文件注释,文件注释通常包含
一段良好文件注释的栗子:
|
|
文件注释的格式通常不作要求,能清晰易读就可以了,但在整个工程中风格要统一。
###代码注释
好的代码应该是“自解释”(self-documenting)的,但仍然需要详细的注释来说明参数的意义、返回值、功能以及可能的副作用。
方法、函数、类、协议、类别的定义都需要注释,推荐采用Apple的标准注释风格,好处是可以在引用的地方alt+点击
自动弹出注释,非常方便。
有很多可以自动生成注释格式的插件,推荐使用VVDocumenter:
一些良好的注释:
|
|
协议、委托的注释要明确说明其被触发的条件:
|
|
如果在注释中要引用参数名或者方法函数名,使用||
将参数或者方法括起来以避免歧义:
|
|
定义在头文件里的接口方法、属性必须要有注释!
##编码风格
每个人都有自己的编码风格,这里总结了一些比较好的Cocoa编程风格和注意点。
###不要使用new方法
尽管很多时候能用new
代替alloc init
方法,但这可能会导致调试内存时出现不可预料的问题。Cocoa的规范就是使用alloc init
方法,使用new
会让一些读者困惑。
###Public API要尽量简洁
共有接口要设计的简洁,满足核心的功能需求就可以了。不要设计很少会被用到,但是参数极其复杂的API。如果要定义复杂的方法,使用类别或者类扩展。
####import和#include
#import
是Cocoa中常用的引用头文件的方式,它能自动防止重复引用文件,什么时候使用#import
,什么时候使用#include
呢?
#import
#include
,这时必须要保证被引用的文件提供了保护域(#define guard)。栗子:
|
|
为什么不全部使用#import
呢?主要是为了保证代码在不同平台间共享时不出现问题。
###引用框架的根头文件
上面提到过,每一个框架都会有一个和框架同名的头文件,它包含了框架内接口的所有引用,在使用框架的时候,应该直接引用这个根头文件,而不是其它子模块的头文件,即使是你只用到了其中的一小部分,编译器会自动完成优化的。
|
|
###BOOL的使用
BOOL在Objective-C中被定义为signed char
类型,这意味着一个BOOL类型的变量不仅仅可以表示YES
(1)和NO
(0)两个值,所以永远不要将BOOL类型变量直接和YES
比较:
|
|
同样的,也不要将其它类型的值作为BOOL来返回,这种情况下,BOOL变量只会取值的最后一个字节来赋值,这样很可能会取到0(NO)。但是,一些逻辑操作符比如&&
,||
,!
的返回是可以直接赋给BOOL的:
|
|
另外BOOL类型可以和_Bool
,bool
相互转化,但是不能和Boolean
转化。
###使用ARC
除非想要兼容一些古董级的机器和操作系统,我们没有理由放弃使用ARC。在最新版的Xcode(6.2)中,ARC是自动打开的,所以直接使用就好了。
###在init和dealloc中不要用存取方法访问实例变量
当init``dealloc
方法被执行时,类的运行时环境不是处于正常状态的,使用存取方法访问变量可能会导致不可预料的结果,因此应当在这两个方法内直接访问实例变量。
|
|
###按照定义的顺序释放资源
在类或者Controller的生命周期结束时,往往需要做一些扫尾工作,比如释放资源,停止线程等,这些扫尾工作的释放顺序应当与它们的初始化或者定义的顺序保持一致。这样做是为了方便调试时寻找错误,也能防止遗漏。
###保证NSString在赋值时被复制
NSString
非常常用,在它被传递或者赋值时应当保证是以复制(copy)的方式进行的,这样可以防止在不知情的情况下String的值被其它对象修改。
|
|
###使用NSNumber的语法糖
使用带有@
符号的语法糖来生成NSNumber对象能使代码更简洁:
|
|
###nil检查
因为在Objective-C中向nil对象发送命令是不会抛出异常或者导致崩溃的,只是完全的“什么都不干”,所以,只在程序中使用nil来做逻辑上的检查。
另外,不要使用诸如nil == Object
或者Object == nil
的形式来判断。
|
|
###属性的线程安全
定义一个属性时,编译器会自动生成线程安全的存取方法(Atomic),但这样会大大降低性能,特别是对于那些需要频繁存取的属性来说,是极大的浪费。所以如果定义的属性不需要线程保护,记得手动添加属性关键字nonatomic
来取消编译器的优化。
###点分语法的使用
不要用点分语法来调用方法,只用来访问属性。这样是为了防止代码可读性问题。
|
|
###Delegate要使用弱引用
一个类的Delegate对象通常还引用着类本身,这样很容易造成引用循环的问题,所以类的Delegate属性要设置为弱引用。
|
|
From: https://swiftcafe.io/2016/07/18/swift-loop/
Swift 3.0 版本将会去掉沿用已经的 C 风格循环语法, 又是向现代开发语言的一次迈进, 咱们就来看看没了 C 风格循环我们还有什么选择, 看过之后你会不会感觉 C 风格循环在 Swift 中确实有点多余呢?
关于 C 风格循环, 不我们过多介绍了, 就是类似这样的语句:
let numberList = [1, 2, 3, 4, 5]
for var i = 0; i < numberList.count; i++ {
}
如今这样的语法在新版本的 Swift 中即将成为历史了, C 风格的循环语法可能是大家最熟悉的, 大家会不会觉得突然去掉这个语法有些不适应呢? 咱们再来看看 Swift 3 中的替代方案。
第一个替代方案, 我们可以使用 for .. in 这样的语法:
let numberList = [1, 2, 3, 4, 5]
var result = “”
for num in numberList {
result += “(num) “
}
这样就完成了对数组的遍历了, 但是还有另一个情况, 如果我们想知道每次遍历的索引怎么办呢, 还有一种方法:
for num in numberList.enumerate() {
result += “[(num.index)](num.element) “
}
我们可以使用这个集合类型的 enumerate
方法,将这个数组的索引和对应的元素都取了出来,然后我们在循环中就可以对索引项进行引用了, num.index 和 num.element 分别代表对应的索引和元素。
上面这个循环我们还可以再改写一下:
for (index, item) in numberList.enumerate() {
result += “[(index)](item) “
}
不难看出,其实循环中的每一项都是一个元组(Tuple),这个元组的第一项是当前的索引, 第二项是当前的数组元素。 那么我们就可以推理出, enumerate
函数其实就是对 numberList
数组做了一个变换,原来它是一个 Int 类型的数组,经过变换后,成为了 (Int, Int) 元组类型的数组。
是不是这么回事呢? 查看 enumerate 方法的文档后, 看到它的定义是这样的:
func enumerate() -> EnumerateSequence
比我们想象的要复杂些, EnumerateSequence 是个什么鬼, 让我们再来看看它的文档定义:
The SequenceType returned by enumerate(). EnumerateSequence is a sequence of pairs (n, x), where ns are consecutive Ints starting at zero, and xs are the elements of a Base SequenceType
仔细看下, 其实跟我们理解的还是差不多的, 它只不过是对集合类的一个集成, 这个集合每一项是一个元组 (n, x) , n 代表索引, x 代表数组元素。
那么,我们还可以做点更有意思的事情:
for (index, item) in numberList.enumerate().reverse() {
result += “[(index)](item) “
}
调用 enumerate, 之后再调用 reverse 方法, 我们就可以对一个数组进行反向遍历。
for (index, item) in numberList.enumerate().reverse() {
result += “[(index)](item) “
}
我们还可以:
for (index, item) in numberList.enumerate().filter({ (index, item) in index % 2 == 0}) {
result += “[(index)](item) “
}
调用 filter 函数,过滤某些索引, 只遍历符合条件的那些元素。
当然, 我们还可以做的更多更多, 大家有兴趣可以看看 SequenceType
的文档,把你的新思路回复给大家。 http://swiftdoc.org/v2.2/protocol/SequenceType
除了刚才咱们说的这些, Swift 还提供了更方便的循环语法, 叫做 Range 循环。 比如这样:
var rs = “”;
for i in 0…10 {
rs += “(i)”
}
print(rs)
这个语句会输出 0 到 10 之间的所有数字, 0…10 这个表示 Range 区间的范围。 当然,对于我们刚才的数组遍历来说, 一般数组索引都是数组长度减去 1, 用这个区间处理起来就会比较麻烦, 不过好在 Swift 给我们提供了另外一种 Range 方法:
for i in 0..<numberList.count {
rs += “(i)”
}
这次我们换成了 0..<numberList.count, 这种形式会排除闭区间最后那个数组,然后我们就可以在循环中用索引进行访问啦(注意符号 ..< 两边不要有空格)。
好了,今天跟大家分享的内容就这么多。C 风格的循环语句其实更多是我们的一个长期养成的习惯问题。 世界的一切都在进步发展,包括开发语言也是一样。 看了 Swift 提供的循环语法, 你对 C 风格循环还有没有存在的必要时什么看法呢。 早些拥抱趋势和变化总是好的。
如果你觉得这篇文章有帮助,还可以关注微信公众号 swift-cafe,会有更多我的原创内容分享给你~
本站文章均为原创内容,如需转载请注明出处,谢谢。
推荐关注微信公众平台
swift-cafe
/// 此笔记级一些集成UM遇到的坑
分享面板无法弹出 最终找到这个原因一坑
|
|
UM 取消授权
|
|
下面这点算是QQ的坑点 , 统一id 问题
Unionid接口权限申请流程:目前只支持同一个开发者号码下的应用进行打通。如有需要,可以发邮件到connect@qq.com申请,提供应用类型、信息(APPID和APPKEY),附上营业执照图片、网站备案截图(若有网站应用需要提供该项)即可。打通后同一个QQ登录不同APP ID应用后返回的unionid一致。具体打通事宜后续工作人员会通过邮件确认,请在1~5个工作日留意邮件,以邮件回复为准。
http://wiki.connect.qq.com/%E5%BC%80%E5%8F%91%E8%80%85%E5%8F%8D%E9%A6%88
swift
|
|
From: http://ibloodline.com/articles/2016/09/19/singleton.html
单例模式(Singleton
):保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式应该是设计模式中最简答的形式了。这一模式的意图是让类的一个对象成为系统中唯一的实例。
优点:
1、提供了对唯一实例的受控访问。
2、由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
3.因为单例模式的类控制了实例化的过程,所以类可以更加灵活修改实例化过程。
缺点:
1、由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
2、单例类的职责过重,在一定程度上违背了“单一职责原则”。
先看C++
中的实现:
class Singlenton
{
public:
static Singlenton *Instance();
protected:
Singlenton();
private:
static Singlenton *_instance;
};
Singlenton *Singlenton::_instance = NULL;
Singlenton *Singlenton::Instance()
{
if (_instance == NULL) {
_instance = new Singlenton;
}
return _instance;
}
OC
下:
//Singleton.h
@interface Singleton : NSObject
+ (Singleton *)sharedInstance;
@end
//Singleton.m
@implementation Singleton
static Singleton * sharedSingleton = nil;
+ (Singleton *) sharedInstance {
if (sharedSingleton == nil) {
sharedSingleton = [[Singleton alloc] init];
}
return sharedSingleton;
}
@end
上面的实现是有问题的。首先,如果客户端使用不同的方式来初始化单例,则有可能出现多个实例的情况。另外,这样的实现也不是线程安全的。改进:
@implementation Singleton
static id sharedSingleton = nil;
+ (id)allocWithZone:(struct _NSZone *)zone {
if (!sharedSingleton) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedSingleton = [super allocWithZone:zone];
});
}
return sharedSingleton;
}
- (id)init {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedSingleton = [super init];
});
return sharedSingleton;
}
+ (instancetype)sharedInstance {
return [[self alloc] init];
}
+ (id)copyWithZone:(struct _NSZone *)zone {
return sharedSingleton;
}
+ (id)mutableCopyWithZone:(struct _NSZone *)zone {
return sharedSingleton;
}
@end
当然对于懒癌患者来讲,每个单例都写这样的实现实在太不可接受了,我们把它抽取成宏:
// .h文件的实现
#define SingletonH(methodName) + (instancetype)shared##methodName;
// .m文件的实现
#if __has_feature(objc_arc) // 是ARC
#define SingletonM(methodName) \
static id _instace = nil; \
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
if (_instace == nil) { \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super allocWithZone:zone]; \
}); \
} \
return _instace; \
} \
\
- (id)init \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super init]; \
}); \
return _instace; \
} \
\
+ (instancetype)shared##methodName \
{ \
return [[self alloc] init]; \
} \
+ (id)copyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
} \
\
+ (id)mutableCopyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
}
#else // 不是ARC
#define SingletonM(methodName) \
static id _instace = nil; \
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
if (_instace == nil) { \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super allocWithZone:zone]; \
}); \
} \
return _instace; \
} \
\
- (id)init \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super init]; \
}); \
return _instace; \
} \
\
+ (instancetype)shared##methodName \
{ \
return [[self alloc] init]; \
} \
\
- (oneway void)release \
{ \
\
} \
\
- (id)retain \
{ \
return self; \
} \
\
- (NSUInteger)retainCount \
{ \
return 1; \
} \
+ (id)copyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
} \
\
+ (id)mutableCopyWithZone:(struct _NSZone *)zone \
{ \
return _instace; \
}
使用:
//SmartSingleton.h
@interface SmartSingleton : NSObject
SingletonH(SmartSingleton)
@end
//SmartSingleton.m
@implementation SmartSingleton
SingletonM(SmartSingleton)
@end
//客户端调用
Singleton *singleton = [Singleton sharedInstance];
NSLog(@"%@", singleton);
SmartSingleton *smartSingleton = [SmartSingleton sharedSmartSingleton];
NSLog(@"%@", smartSingleton);
Cocoa
中的单例Cocoa
中最常见的单例类是UIApplication
类。它提供了一个控制并协调应用程序的集中点。
每个应用程序有且只有一个UIApplication
实例。它由UIApplicationMain
函数在应用程序启动时创建为单例对象。之后,对同一UIApplication
实例可以通过sharedUIApplication
类方法进行访问。
UIApplication
对象为应用程序处理许多内务管理任务(housekeeping task
),包括传入的用户时间的最初路由,以及为UIControl
分发动作消息给合适的目标对象。它还卫华应用程序中打开的所有UIWindow
对象的列表。应用程序对象总是被分配一个UIApplicationDelegate
对象。应用程序将把重要的运行时事件通知给它,比如iOS
应用程序中的应用程序启动、内存不足警告、应用程序终止和后台进程执行。这让代理(delegate
)有机会作出适当的响应。
NSUserDefault
、NSFileManager
等也是常见的单例实现。
只要应用程序需要用集中式的类来协调其服务,这个类就应生成单一的实例。
文章中的代码都可以从我的GitHub DesignPatterns
找到。
From: http://www.jianshu.com/p/55f781f8c915
在objective-c 1.0中,我们为interface同时声明了属性和底层实例变量,那时,属性是oc语言的一个新的机制,并且要求你必须声明与之对应的实例变量,例如:
@interface MyViewController :UIViewController
{
UIButton *myButton;
}
@property (nonatomic, retain) UIButton *myButton;
@end
在objective-c 2.0中,@property它将自动创建一个以下划线开头的实例变量。因此,在这个版本中,我们不再为interface声明实例变量。变成我们常见的形式
@interface MyViewController :UIViewController
@property (nonatomic, retain) UIButton *myButton;
@end
在MyViewController.m文件中,编译器也会自动的生成一个实例变量_myButton。那么在.m文件中可以直接的使用_myButton实例变量,也可以通过属性self.myButton.都是一样的。
注意这里的self.myButton其实是调用的myButton属性的getter/setter方法。这与C++中点的使用是有区别的,C++中的点可以直接访问成员变量(也就是实例变量)。
例如在oc的.h文件中有如下代码
@interface MyViewController :UIViewController
{
NSString *name;
}
.m文件中,self.name 这样的表达式是错误的。xcode会提示你使用->,改成self->name就可以了。因为oc中点表达式是表示调用方法,而上面的代码中没有name这个方法。所以在oc中点表达式其实就是调用对象的setter和getter方法的一种快捷方式。
你可能还见过这种写法
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UIButton *myButton;
@end
@implementation ViewController
@synthesize myButton;
@synthesize 语句只能被用在 @implementation 代码段中,@synthesize的作用就是让编译器为你自动生成setter与getter方法,@synthesize 还有一个作用,可以指定与属性对应的实例变量,例如@synthesize myButton = xxx;那么self.myButton其实是操作的实例变量xxx,而不是_myButton了。
如果.m文件中写了@synthesize myButton;那么生成的实例变量就是myButton;如果没写@synthesize myButton;那么生成的实例变量就是_myButton。所以跟以前的用法还是有点细微的区别。
类与类别中添加的属性要区分开来,因为类别中只能添加方法,不能添加实例变量。经常会在ios的代码中看到在类别中添加属性,这种情况下,是不会自动生成实例变量的。比如在:UINavigationController.h文件中会对UIViewController类进行扩展
@interface UIViewController (UINavigationControllerItem)
@property(nonatomic,readonly,retain) UINavigationItem *navigationItem;
@property(nonatomic) BOOL hidesBottomBarWhenPushed;
@property(nonatomic,readonly,retain) UINavigationController *navigationController;
@end
这里添加的属性,不会自动生成实例变量,这里添加的属性其实是添加的getter与setter方法。注意一点,匿名类别(匿名扩展)是可以添加实例变量的,非匿名类别是不能添加实例变量的,只能添加方法,或者属性(其实也是方法),常用的扩展是在.m文件中声明私有属性和方法。 Category理论上不能添加变量,但是可以使用rRuntime机制来弥补这种不足。
#import
static const void * externVariableKey =&externVariableKey;
@implementation NSObject (Category)
@dynamic variable;
- (id) variable
{
return objc_getAssociatedObject(self, externVariableKey);
}
- (void)setVariable:(id) variable
{
objc_setAssociatedObject(self, externVariableKey, variable, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@protected是受保护的,只能在本类及其子类中访问,在{}声明的变量默认是@protect
@private是私有的,只能在本类访问
@public公开的,可以被在任何地方访问。
在头文件.h中:
@interface ViewController : UIViewController
{
// 成员变量
@public
NSString* publicString;
@protected
NSString* protectedString;
@private
NSString* privateString;
}
//属性变量
@property (nonatomic,strong) NSArray *propertyString;
@end
一些建议:
1.如果只是单纯的private变量,最好声明在implementation里.
2.如果是类的public属性,就用property写在.h文件里
3.如果自己内部需要setter和getter来实现一些东西,就在.m文件的类目里用property来声明
.h中的interface的大括号{}之间的实例(成员)变量,.m中可以直接使用;
.h中的property(属性)变量,.m中需要使用self.propertyVariable的方式使用propertyVariable变量
但只声明了变量,是不会有属性的,可以通过以下代码证明
在Person.h 头文件中
@interface Person : NSObject {
@private
//name为私有成员变量
NSString name;
}
// age 为成员属性
@property (nonatomic ,copy) NSString age;
在viewController.m 中,通过RunTime机制获得对象的所有成员变量和成员属性。
Person *p = [Person new];
unsigned int count = 0; //count记录变量的数量
// 获取类的所有成员变量
Ivar *members = class_copyIvarList([Person class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = members[i];
// 取得变量名并转成字符串类型
const char *memberName = ivar_getName(ivar);
NSLog(@"变量名 = %s",memberName);
}
// 获取类的所有成员属性
objc_property_t *properties =class_copyPropertyList([Person class], &count);
for (int i = 0; i<count; i++)
{
objc_property_t property = properties[i];
const char* char_f =property_getName(property);
NSString *propertyName = [NSString stringWithUTF8String:char_f];
NSLog(@"属性名 = %@",propertyName);
}
打印结果为
2016-08-12 11:31:50.225 modifyPrivate[777:143231] 变量名 = name
2016-08-12 11:31:50.226 modifyPrivate[777:143231] 变量名 = _age
2016-08-12 11:31:50.226 modifyPrivate[777:143231] 属性名 = age
关于Block的定义,和作为参数的写法
转载 : http://www.dahuangphone.com/dispbbs.asp?boardid=8&id=85&page=3&star=1
|
|
|
|
|
|
From: http://www.jianshu.com/p/5e187c9d389b
AFNetworking 1.0建立在NSURLConnection的基础API之上 ,AFNetworking 2.0开始使用NSURLConnection的基础API ,以及较新基于NSURLSession的API的选项。 AFNetworking 3.0现已完全基于NSURLSession的API,这降低了维护的负担,同时支持苹果增强关于NSURLSession提供的任何额外功能。由于Xcode 7中,NSURLConnection的API已经正式被苹果弃用。虽然该API将继续运行,但将没有新功能将被添加,并且苹果已经通知所有基于网络的功能,以充分使NSURLSession向前发展。
关于NSURLConnection的使用,本文不做详细的介绍,具体参考简书某位大神的介绍http://www.jianshu.com/p/f291ee58c012
1.后台上传和下载。当你的程序退出了也能进行网络操作,这对用户和APP来说都是个好消息,不用运行APP就可以下载和上传,这样更节约手机电量。
2.能够暂停和恢复网络操作。不需要使用NSOperation就可以实现暂停、继续、重启等操作。
3.可配置的容器。
4.可以子类化并且可以设置私有存储方式。可以修改数据的存储方式和存储位置。
5.改进了授权处理机制。
6.代理更强大。
7.通过文件系统上传和下载。
1.使用静态的sharedSession方法,该类使用共享的会话,该会话使用全局的Cache,Cookie和证书。
+ (NSURLSession *)sharedSession;
2.通过sessionWithConfiguration:方法创建对象,也就是创建对应配置的会话,与NSURLSessionConfiguration合作使用。
(NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
3.通过sessionWithConfiguration:delegate:delegateQueue方法创建对象,二三两种方式可以创建一个新会话并定制其会话类型。该方式中指定了session的委托和委托所处的队列。当不再需要连接时,可以调用Session的invalidateAndCancel直接关闭,或者调用finishTasksAndInvalidate等待当前Task结束后关闭。这时Delegate会收到URLSession:didBecomeInvalidWithError:这个事件。Delegate收到这个事件之后会被解引用。
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id <NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue;
加载数据/下载/上传
NSURLSessionTask类
NSURLSessionTask是一个抽象类,它有三个子类
1.NSURLSessionDataTask
2.NSURLSessionUploadTask
3.NSURLSessionDownloadTask
这三个类封装了应用程序的三个基本网络任务:获取数据,比如JSON或XML,以及上传和下载文件。
继承关系如下:
屏幕快照 2016-08-12 13.36.02.png
NSURLSessionConfiguration用于配置会话的属性,可以通过该类配置会话的工作模式:
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
// 超时时间
config.timeoutIntervalForRequest = 10;
// 是否允许使用蜂窝网络(后台传输不适用)
config.allowsCellularAccess = YES;
// 还有很多可以设置的属性
//关于配置信息的实例化方法大概有三种
/*
//默认会话模式(default):工作模式类似于原来的NSURLConnection,使用的是基于磁盘缓存的持久化策略,使用用户keychain中保存的证书进行认证授权。
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
//瞬时会话模式(ephemeral):该模式不使用磁盘保存任何数据。所有和会话相关的caches,证书,cookies等都被保存在RAM中,因此当程序使会话无效,这些缓存的数据就会被自动清空。
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
//后台会话模式(background):该模式在后台完成上传和下载,在创建Configuration对象的时候需要提供一个NSString类型的ID用于标识完成工作的后台会话。
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier
*/
//suspend可以让当前的任务暂停
- (void)suspend;
//resume方法不仅可以启动任务,还可以唤醒suspend状态的任务
- (void)resume;
//cancel方法可以取消当前的任务,你也可以向处于suspend状态的任务发送cancel消息,任务如果被取消便不能再恢复到之前的状态.
- (void)cancel;
如果请求的数据比较简单,也不需要对返回的数据做一些复杂的操作.那么我们可以使用带block
// 快捷方式获得session对象
NSURLSession *session = [NSURLSession sharedSession];
/*GET请求将参数拼接在 url 后面
网络接口 和 参数 以 ? 分隔. 参数和参数之间以 & 符号分隔.注意删除最后一个 & 符号.
如:http://127.0.0.1/login.php?username=zhangsan&password=zhang
*/
NSURL *url = [NSURL URLWithString:@"http://www.daka.com/login?username=daka&pwd=123"];
// GET请求直接根据url实例化网络任务
/*
第一个参数:请求路径:内部会自动将路径包装成请求对象
第二个参数:completionHandler回调(请求完成【成功|失败】的回调)
data:响应体信息(期望的数据)
response:响应头信息,主要是对服务器端的描述
error:错误信息,如果请求失败,则error有值
*/
NSURLSessionTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError error) {
// 默认是子线程.
NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
// 开启任务
[task resume];
POST和GET的区别就在于request,所以使用session的POST请求和GET过程是一样的,区别就在于对request的处理.
NSURL *url = [NSURL URLWithString:@"http://www.daka.com/login"];
//创建可变请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//POST请求将参数添加在请求体中
//设置请求方法
request.HTTPMethod = @"POST";
//设置请求体
request.HTTPBody = [@"username=daka&pwd=123" dataUsingEncoding:NSUTF8StringEncoding];
NSURLSession *session = [NSURLSession sharedSession];
/*
第一个参数:请求对象
第二个参数:completionHandler回调(请求完成【成功|失败】的回调)
data:响应体信息(期望的数据)
response:响应头信息,主要是对服务器端的描述
error:错误信息,如果请求失败,则error有值
*/
// 由于要先对request先行处理,我们通过request初始化task
NSURLSessionTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
// 默认是子线程.
NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
[task resume];
NSURLSession提供了block方式处理返回数据的简便方式,但如果想要在接收数据过程中做进一步的处理,仍然可以调用相关的协议方法.NSURLSession的代理方法和NSURLConnection有些类似,都是分为接收响应、接收数据、请求完成几个阶段.
// 使用代理方法需要设置代理,但是session的delegate属性是只读的,要想设置代理只能通过这种方式创建session
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
// 创建任务(因为要使用代理方法,就不需要block方式的初始化了)
NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.daka.com/login?userName=daka&pwd=123"]]];
// 启动任务
[task resume];
//对应的代理方法如下:
// 1.接收到服务器的响应
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
// 允许处理服务器的响应,才会继续接收服务器返回的数据
completionHandler(NSURLSessionResponseAllow);
}
// 2.接收到服务器的数据(可能调用多次)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
// 处理每次接收的数据
}
// 3.请求成功或者失败(如果失败,error有值)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
// 请求完成,成功或者失败的处理
}
Tips:
关键点在代码注释里面都有提及,重要的地方再强调一下:
如果要使用代理方法,需要设置代理,但从NSURLSession的头文件发现session的delegate属性是只读的.因此设置代理要通过session的初始化方法赋值:sessionWithConfiguration:delegate:delegateQueue:其中:
configuration参数(文章开始提到的)需要传递一个配置,我们暂且使用默认的配置[NSURLSessionConfiguration defaultSessionConfiguration]就好(后面会说下这个配置是干嘛用的);
delegateQueue参数表示协议方法将会在哪个队列(NSOperationQueue)里面执行.
NSURLSession在接收到响应的时候要先对响应做允许处理:completionHandler(NSURLSessionResponseAllow);,才会继续接收服务器返回的数据,进入后面的代理方法.值得一提的是,如果在接收响应的时候需要对返回的参数进行处理(如获取响应头信息等),那么这些处理应该放在前面允许操作的前面.
NSURLSessionDownloadTask同样提供了通过NSURL和NSURLRequest两种方式来初始化并通过block进行回调的方法.下面以NSURL初始化为例:
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:@"http://www.daka.com/resources/image/icon.png"] ;
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
// location是沙盒中tmp文件夹下的一个临时url,文件下载后会存到这个位置,由于tmp中的文件随时可能被删除,所以我们需要自己需要把下载的文件挪到需要的地方
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:path] error:nil];
}];
// 启动任务
[task resume];
/*
Tips:
需要注意的就是需要将下载到tmp文件夹的文件转移到需要的目录.原因在代码中已经贴出.
response.suggestedFilename是从相应中取出文件在服务器上存储路径的最后部分,如数据在服务器的url为http://www.daka.com/resources/image/icon.png, 那么其suggestedFilename就是icon.png.
*/
// 每次写入调用(会调用多次)
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
// 可在这里通过已写入的长度和总长度算出下载进度
CGFloat progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite; NSLog(@"%f",progress);
}
// 下载完成调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
// location还是一个临时路径,需要自己挪到需要的路径(caches下面) NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename]; [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:filePath] error:nil];
}
// 任务完成调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
}
// 使用这种方式取消下载可以得到将来用来恢复的数据,保存起来
[self.task cancelByProducingResumeData:^(NSData *resumeData) {
self.resumeData = resumeData;
}];
// 由于下载失败导致的下载中断会进入此协议方法,也可以得到用来恢复的数据
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
// 保存恢复数据
self.resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData];
}
// 恢复下载时接过保存的恢复数据
self.task = [self.session downloadTaskWithResumeData:self.resumeData];
// 启动任务
[self.task resume];
//程序强制退出就无法断点下载了,具体方法见http://blog.csdn.net/qianlima210210/article/details/49303703
在NSURLSession中,文件上传方式主要有以下两种:
//第一种方式
NSURLSessionUploadTask *task =
[[NSURLSession sharedSession] uploadTaskWithRequest:request
fromFile:fileName
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
}];
//第二种方式
[self.session uploadTaskWithRequest:request
fromData:body
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"-------%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];
/*
处于安全性考虑,通常我们会使用POST方式进行文件上传,所以较多使用第二种方式.
但是,NSURLSession并没有为我们提供比NSURLConnection更方便的文件上传方式.方法中body处的参数需要填写request的请求体(http协议规定格式的大长串).
关于NSURLSessionConfiguration的使用可以参考http://www.jianshu.com/p/fafc67475c73
以及http://www.tuicool.com/articles/VBv2qe
由于苹果已经弃用NSURLConnection,所以在此暂时只介绍AFNetworking3.0及3.0+以上的使用方法
本人习惯使用cocoaPods 进行管理第三方类库,导入过程不做详细介绍,不会的可以上网查看教程
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:URL parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"这里打印请求成功要做的事");
}failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"%@",error); //这里打印错误信息
}];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSMutableDictionary *parameters = @{@"":@"",@"":@""};
[manager POST:URL parameters:parameters progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
- (void)downLoad{
//1.创建管理者对象
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//2.确定请求的URL地址
NSURL *url = [NSURL URLWithString:@""];
//3.创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//4.下载任务
NSURLSessionDownloadTask *task = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
//打印下下载进度
NSLog(@"%lf",1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount);
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
//下载地址
NSLog(@"默认下载地址:%@",targetPath);
//设置下载路径,通过沙盒获取缓存地址,最后返回NSURL对象
NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject];
return [NSURL URLWithString:filePath];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
//下载完成调用的方法 WKNSLog(@"下载完成:");
NSLog(@"%@--%@",response,filePath);
}];
//开始启动任务
[task resume];
}
//第一种方法是通过工程中的文件进行上传
- (void)upLoad1{
//1。创建管理者对象
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//2.上传文件
NSDictionary *dict = @{@"username":@"1234"};
NSString *urlString = @"22222";
[manager POST:urlString parameters:dict constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
//上传文件参数
UIImage *iamge = [UIImage imageNamed:@"123.png"];
NSData *data = UIImagePNGRepresentation(iamge);
//这个就是参数
[formData appendPartWithFileData:data name:@"file" fileName:@"123.png" mimeType:@"image/png"];
} progress:^(NSProgress * _Nonnull uploadProgress) {
//打印下上传进度
WKNSLog(@"%lf",1.0 *uploadProgress.completedUnitCount / uploadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
//请求成功
WKNSLog(@"请求成功:%@",responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
//请求失败
WKNSLog(@"请求失败:%@",error);
}];
}
//第二种是通过URL来获取路径,进入沙盒或者系统相册等等
- (void)upLoda2{
//1.创建管理者对象
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//2.上传文件
NSDictionary *dict = @{@"username":@"1234"};
NSString *urlString = @"22222";
[manager POST:urlString parameters:dict constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:@"文件地址"] name:@"file" fileName:@"1234.png" mimeType:@"application/octet-stream" error:nil];
} progress:^(NSProgress * _Nonnull uploadProgress) {
//打印下上传进度
WKNSLog(@"%lf",1.0 *uploadProgress.completedUnitCount / uploadProgress.totalUnitCount);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
//请求成功
WKNSLog(@"请求成功:%@",responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
//请求失败
WKNSLog(@"请求失败:%@",error);
}];
}
- (void)AFNetworkStatus{
//1.创建网络监测者
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
/*枚举里面四个状态 分别对应 未知 无网络 数据 WiFi
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
AFNetworkReachabilityStatusUnknown = -1, 未知
AFNetworkReachabilityStatusNotReachable = 0, 无网络
AFNetworkReachabilityStatusReachableViaWWAN = 1, 蜂窝数据网络
AFNetworkReachabilityStatusReachableViaWiFi = 2, WiFi
};
*/
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
//这里是监测到网络改变的block 可以写成switch方便
//在里面可以随便写事件
switch (status)
{
case AFNetworkReachabilityStatusUnknown:
NSLog(@"未知网络状态");
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"无网络");
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"蜂窝数据网");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"WiFi网络");
break;
default:
break;
}
}] ;
}
所有的网络请求,均有manager发起
需要注意的是,默认提交请求的数据是二进制的,返回格式是JSON
如果提交数据是JSON的,需要将请求格式设置为AFJSONRequestSerializer
//AFHTTPRequestSerializer 二进制格式(默认请求格式)
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
//AFJSONRequestSerializer JSON
manager.requestSerializer = [AFJSONRequestSerializer serializer];
//AFPropertyListRequestSerializer PList(是一种特殊的XML,解析起来相对容易)
manager.requestSerializer = [AFPropertyListRequestSerializer serializer];
//AFHTTPResponseSerializer 二进制格式
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
//AFJSONResponseSerializer JSON(默认情况下返回json,所以有时后返回的不是json,就要重新设置返回格式)
manager.responseSerializer = [AFJSONResponseSerializer serializer];
//AFXMLParserResponseSerializer XML,只能返回XMLParser,还需要自己通过代理方法解析(下面将介绍用NSXMLParser解析xml)
manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
//AFXMLDocumentResponseSerializer (Mac OS X)
//AFPropertyListResponseSerializer PList
//AFImageResponseSerializer Image
//AFCompoundResponseSerializer 组合
//通过acceptableContentTypes可以添加接收的类型,如果没有设置,出错情况下会提示,具体参考http://www.jianshu.com/p/212a128c9a33,可以在AFURLResponseSerialization.m源代码中添加接收的类型
/*
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
return self;
}
*/
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"application/xml"];
关于该部分的理解,可以参考http://www.jianshu.com/p/3aa19c12000a
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"application/xml"];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
//afn默认发起的是异步请求
[manager GET:@"http://localhost/sources/videos.xml" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"%@",[NSThread currentThread]);//打印结果是主线程,也就是说,异步请求之后,自动返回主线程
// NSLog(@"%@",responseObject);
if ([responseObject isKindOfClass:[NSXMLParser class]]) {
NSXMLParser *parser = responseObject;
parser.delegate = self;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[parser parse];
});
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"%@",error.localizedDescription);
}];
-(void)parserDidStartDocument:(NSXMLParser *)parser{
NSLog(@"打开文档,准备开始解析");
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict{
NSLog(@"startElement=%@ attributeDict=%@",elementName,attributeDict);
if ([elementName isEqualToString:@"video"]) {
self.currentVideo = [[XFSVideo alloc]init];
self.currentVideo.videoId = attributeDict[@"videoId"];
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
NSLog(@"foundCharacters=%@",string);
[self.elementString appendString:string];
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
NSLog(@"endelement=%@",elementName);
NSLog(@"%@",[NSThread currentThread]);
if ([elementName isEqualToString:@"video"]) {
[self.videos addObject:self.currentVideo];
self.currentVideo = nil;
}else if (![elementName isEqualToString:@"videos"]){
[self.currentVideo setValue:self.elementString forKeyPath:elementName];
self.elementString = nil;
}
}
-(void)parserDidEndDocument:(NSXMLParser *)parser{
NSLog(@"%@",self.videos);
NSLog(@"结束文档");
dispatch_async(dispatch_get_main_queue(), ^{
});
}