December 6, 2015 8:43 AM
随笔:
我发现我在第一次开发想那些后面要写的网络什么的确实有点远,而且对于一些封装也有点超前,有句话叫==需求决定供给==,我只有在写和后来读的过程中才能感受到,当我感觉代码太乱的时候,我需要整理,当我感觉每个页面都要写一个同样的请求服务器的消息时,我才需要分出来一个网络层专门做这种事,其实我也想到了前几天张建海老师跟我说的,别想太多了,做就好了,做着做着自然就会有感觉了。
一.addTarget的使用地方
搞清楚view中的一个button的点击事件调用函数,我应该把这个回调函数写在view中呢,还是controller中呢?
[self.teamComunicate addTarget:self action:@selector(comunicate:) forControlEvents:UIControlEventTouchUpInside];
根据addTarget这个参数苹果应该是允许回调事件发生在不同的类中。
二.模型属性的命名规范
把相同属性的模型的前缀设置成一样的,比如team的信息,都是team开头,teamName,teamLogo等,这样方便阅读,也不会很混乱
三.模态视图的使用
PickImageViewController *ickImageViewController = [[PickImageViewController alloc] init];
[self presentModalViewController:ickImageViewController animated:YES];
//返回
[self dismissModalViewControllerAnimated:YES];
当我们在view controller A中模态显示view controller B的时候,A就充当presenting view controller(弹出VC),而B就是presented view controller(被弹出VC)。官方文档建议这两者之间通过delegate实现交互,如果使用过UIImagePickerController从系统相册选取照片或者拍照,我们可以发现imagePickerController和弹出它的VC之间就是通过UIImagePickerControllerDelegate实现交互的。因此我们在实际应用用,最好也遵守这个原则,在被弹出的VC中定义delegate,然后在弹出VC中实现该代理,这样就可以比较方便的实现两者之间的交互。
通过设置presenting VC的modalPresentationStyle属性,我们可以设置弹出View Controller时的风格
通过设置设置presented VC的modalTransitionStyle属性,我们可以设置弹出presented VC时场景切换动画的风格
dismissViewControllerAnimated:completion
谁来调用这消失presented VC的这个方法:正确的做法是“谁污染谁治理”,即presenting VC调用上面的方法来取消presented VC的显示。如果presented VC需要和presenting VC有数据传递的话,建议在presenting VC实现的代理函数中dismiss弹出的view controller。
四.两个Controller通信
1)采用代理模式 子viewController设计代理协议,定义协议接口,父viewController 实现协议接口,实现子viewController 退出时将相关数据更新到父视图。 常用程度
2)采用ios的消息机制 父viewcontroller注册消息,子viewcontroller发送消息,触发父viewcontroller的消息处理。 常用程度
3)采用database做为数据中间的存储媒介,子viewcontroller将状态数据存入DB,父viewcontroller从DB获取数据更新view。 常用程度
4)采用ios的NSDefault 存储传递的参数。 常用程度
5)通过AppDelegate 中定义全局变量(或者单例)实现中间数据的存储。 常用程度
1. 单向交互
第一级level1ViewController负责创建第二级level2ViewController,并且设置level2ViewController相关的属性,然后pushlevel2ViewController。这是最常用的方式。
伪代码
level2ViewController = [[level2ViewController alloc] init];
[level2ViewController setProp_1];
[level2ViewController setProp_2];
[level2ViewController setProp_3];
[level1ViewController. navigationController pushViewController:level2ViewController];
当然还有一种方式是在创建level2ViewController 实例的时候顺便把参数也传了,比较适用于单一参数。如果是多个参数的话,可以用一个Dictionary作为参数的容器。
[[level2ViewController alloc] initWithParam:param]
这种情况下,从level1ViewController的角度来看,它只需要初始化level2ViewController,设置它的一些属性,调用push,然后就什么都不用管了。从level2ViewController的角度来看,当它被push后,在它的生命期内,都不会影响level1ViewController,不需要调用level1ViewController的函数,也没有事件需要通知level1ViewController。
这是最简单的使用情形,也是最理想化的。在项目设计阶段,应该尽量把代码设计成这种结构(仅仅是尽量,并不是必须。而且,也并不是所有的使用场景都都可以设计成这样的结构,毕竟这个太理想化了)。
2. 双向交互
level1ViewController和level2ViewController使用共同的一个数据shareData(一定要理解这里“共同”的含义—-两个controller共享同一个数据,并且都有可能进行读写操作,尤其是写操作。如果level2ViewController只对数据进行读操作,那其实就跟前面1中的情形是一致的)。第一级level1ViewController负责创建第二级level2ViewController,并且设置level2ViewController相关的属性,同时还要把shareData传递给level2ViewController,然后pushlevel2ViewController。伪代码如下:
[[level2ViewController alloc] init];
[level2ViewController setProp_1];
[level2ViewController setProp_2];
[level2ViewController setData: shareData];
[level1ViewController. navigationController pushViewController:level2ViewController];
[level2ViewController release];
和前一种情形的区别是,在level2ViewController的生命期内,它可能会对shareData进行写操作,当发生这种写操作的时候,需要让level1ViewController知道这种变化,并且作出对应的处理。
这个时候,比较容易想到的一种解决办法是使用Delegate,当level2ViewController写操作shareData时,通过Delegate机制通知level1ViewController,给予level1ViewController一个机会作出正确的响应。(常用方法)
这种办法虽然可行,但并不是首选,在面向对象编程中,经常会提到“解耦”,这里我们就要想办法让这两个controller不要联系的这么紧密。那么怎么办呢?
就是使用MVC模式(UIViewController本身就是MVC中的C)。这个时候,shareData就不应该再是一个普通的数据,而应该封装成model,当model中的数据发生了改变的时候,会发出通知告诉C(和/或V)。这样的话,两个controller之间就比较松散了。引入dataModel后,伪代码如下:
[dataModel alloc] initWithData: shareData];
[[level2ViewController alloc] init];
[level2ViewController setProp_1];
[level2ViewController setProp_2];
[level2ViewController setModel: dataModel];
[level1ViewController. navigationController pushViewController:level2ViewController];
[level2ViewController release];
当level2ViewController修改shareData后,不需要负责通知level1ViewController。shareData会负责通知它的controller
3.双向交互,逆向消息传递
这时,基本操作都还是前面描述的1或2中的情形,区别点在于,在level2ViewController的生命期内,发生了某个事件,需要让level1ViewController知道这个事件,并且作出对应的处理。
这个时候,就只能用Delegate了。当level2ViewController发生某个事件时,通过Delegate机制通知level1ViewController,给与level1ViewController一个机会作出正确的响应。
但是!一定要谨慎使用这种方案。如果发现代码中需要用到这种结构,首先应该重新审视项目的架构,看能不能用前面两种方案来处理,可能需要做一定的代码重构。当前面的方案实在是无能为力的时候,才考虑使用此方案。而且,尽量让这种逆向的消息传递更简单,消息事件不要太多。
4.双向交互,大量逆向消息传递
这种情形和前面的3是一样的,但是level2ViewController和level1ViewController之间的消息传递更复杂。
这个时候,同样,要审视代码的结构是否合理。而且很大的可能性是设计不合理,应该考虑把level2ViewController中管理的UIView迁移到level1ViewController中,把controller之间的交互,变成controller内部的view之间的交互,让level1ViewController的内部聚合的更紧密一些,对外部,level1ViewController则表现的更松散。例如,UIAlertView或UIActionSheet,都是UIView的子类。(此处并不绝对,在有些sdk中,类似于UIAlertView或UIActionSheet的组件,它的使用情形,是符合前面3中的描述的,因为这两种组件的行为模式,是有规律性的,可以很方便的复用,举个例子,桌面编程环境中,文件选择器这种组件,通常就是一个顶层window)
最后还要补充说明一些内容。
Cocoa是比较严格的执行了MVC设计模式,++其中的UIViewController,可以看成是某种使用场合下,一些内在联系很紧密、并且协同工作的UIView的一个顶层管理容器++。
在其他的sdk中,不一定有严格的controller,很有可能只有M和V(C的功能在V内实现),甚至只有V(简单的使用场景,就可以不区分M和C,全在V内管理)。这个时候,就有顶层View的概念(有很多种叫法,比如page、window,或者直接使用abstractWidget),这种逻辑概念上的顶层View,在设计的时候,同样需要考虑它们之间的交互,前面描述的这些情形,同样可以套用。
在用户体验的角度来看,controll或者顶层view,通常被成为“场景”,场景之间是否需要交互,前面的描述,也有一定的参考价值。
但是场景的概念并非总和controll或顶层view严格对应,“场景”是更抽象化的内容,更符合人类的直观感觉,而controller顶层view是更工程化的概念,更精确。在用户体验设计中,有些不同的场景,在逻辑上其实可以属于同一个controller.
五.UITableViewCell
在cell的右边通常有一个图标,我们可以设置UITableViewCell的accesoryType属性,这是一个枚举类型具体含义如下:
typedef NS_ENUM(NSInteger, UITableViewCellAccessoryType) {
UITableViewCellAccessoryNone, // 不显示任何图标
UITableViewCellAccessoryDisclosureIndicator, // 跳转指示图标
UITableViewCellAccessoryDetailDisclosureButton, // 内容详情图标和跳转指示图标
UITableViewCellAccessoryCheckmark, // 勾选图标
UITableViewCellAccessoryDetailButton NS_ENUM_AVAILABLE_IOS(7_0) // 内容详情图标
};
如果想要自定义右侧的访问器,其实只要设置UITableViewCell的accessoryView即可,它支持任何UIView控件。
六.UIView动画
1. 概述
一、UIView动画(首尾)
1.简单说明
UIKit直接将动画集成到UIView类中,当内部的一些属性发生改变时,UIView将为这些改变提供动画支持
执行动画所需要的工作由UIView类自动完成,但仍要在希望执行动画时通知视图,为此需要将改变属性的代码放在[UIView beginAnimations:nil context:nil]和[UIView commitAnimations]之间
常见方法解析:
(void)setAnimationDelegate:(id)delegate 设置动画代理对象,当动画开始或者结束时会发消息给代理对象
(void)setAnimationWillStartSelector:(SEL)selector 当动画即将开始时,执行delegate对象的selector,并且把beginAnimations:context:中传入的参数传进selector
(void)setAnimationDidStopSelector:(SEL)selector 当动画结束时,执行delegate对象的selector,并且把beginAnimations:context:中传入的参数传进selector
(void)setAnimationDuration:(NSTimeInterval)duration 动画的持续时间,秒为单位
(void)setAnimationDelay:(NSTimeInterval)delay 动画延迟delay秒后再开始
(void)setAnimationStartDate:(NSDate *)startDate 动画的开始时间,默认为now
(void)setAnimationCurve:(UIViewAnimationCurve)curve 动画的节奏控制
(void)setAnimationRepeatCount:(float)repeatCount 动画的重复次数
(void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses 如果设置为YES,代表动画每次重复执行的效果会跟上一次相反
(void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache 设置视图view的过渡效果, transition指定过渡类型, cache设置YES代表使用视图缓存,性能较好
//打印动画块的位置
NSLog(@"动画执行之前的位置:%@",NSStringFromCGPoint(self.customView.center));
//首尾式动画
[UIView beginAnimations:nil context:nil];
//执行动画
//设置动画执行时间
[UIView setAnimationDuration:2.0];
//设置代理
[UIView setAnimationDelegate:self];
//设置动画执行完毕调用的事件
[UIView setAnimationDidStopSelector:@selector(didStopAnimation)];
self.customView.center=CGPointMake(200, 300);
[UIView commitAnimations];
使用UIView和CALayer都能实现动画效果,但是在真实的开发中,一般还是主要使用UIView封装的动画,而很少使用CALayer的动画。
**
CALayer核心动画与UIView动画的区别:**
UIView封装的动画执行完毕之后不会反弹。即如果是通过CALayer核心动画改变layer的位置状态,表面上看虽然已经改变了,但是实际上它的位置是没有改变的。
//1.创建核心动画
CABasicAnimation *anima=[CABasicAnimation animation];
//平移
anima.keyPath=@"position";
//设置执行的动画
anima.toValue=[NSValue valueWithCGPoint:CGPointMake(200, 300)];
//设置执行动画的时间
anima.duration=2.0;
//设置动画执行完毕之后不删除动画
anima.removedOnCompletion=NO;
//设置保存动画的最新状态
anima.fillMode=kCAFillModeForwards;
// anima.fillMode=kCAFillModeBackwards;
//设置动画的代理
anima.delegate=self;
//2.添加核心动画
[self.customView.layer addAnimation:anima forKey:nil];
二、block动画
1.简单说明
- (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
参数解析:
duration:动画的持续时间
delay:动画延迟delay秒后开始
options:动画的节奏控制
animations:将改变视图属性的代码放在这个block中
completion:动画结束后,会自动调用这个block
转场动画
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
参数解析:
duration:动画的持续时间
view:需要进行转场动画的视图
options:转场动画的类型
animations:将改变视图属性的代码放在这个block中
completion:动画结束后,会自动调用这个block
- (void)transitionFromView:(UIView )fromView toView:(UIView )toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion
方法调用完毕后,相当于执行了下面两句代码:
// 添加toView到父视图
[fromView.superview addSubview:toView];
// 把fromView从父视图中移除
[fromView.superview removeFromSuperview];
参数解析:
duration:动画的持续时间
options:转场动画的类型
animations:将改变视图属性的代码放在这个block中
completion:动画结束后,会自动调用这个block
三、补充
1.UIImageView的帧动画
UIImageView可以让一系列的图片在特定的时间内按顺序显示
相关属性解析:
animationImages:要显示的图片(一个装着UIImage的NSArray)
animationDuration:完整地显示一次animationImages中的所有图片所需的时间
animationRepeatCount:动画的执行次数(默认为0,代表无限循环)
相关方法解析:
(void)startAnimating; 开始动画
(void)stopAnimating; 停止动画
(BOOL)isAnimating; 是否正在运行动画
2.UIActivityIndicatorView
是一个旋转进度轮,可以用来告知用户有一个操作正在进行中,一般用initWithActivityIndicatorStyle初始化
方法解析:
(void)startAnimating; 开始动画
(void)stopAnimating; 停止动画
(BOOL)isAnimating; 是否正在运行动画
UIActivityIndicatorViewStyle有3个值可供选择:
UIActivityIndicatorViewStyleWhiteLarge //大型白色指示器
UIActivityIndicatorViewStyleWhite //标准尺寸白色指示器
UIActivityIndicatorViewStyleGray //灰色指示器,用于白色背景