在控制台里使用po [UIViewController _printHierarchy]命令即可打印出controller的层级,一目了然
ReactiveCocoa的学习
软件开发是一个讲究效率的事情,不可能每件事都需要我们自己亲历亲为,所以必要的时候就需要借助很多外库,即使是一门语言也会将常用的功能提炼出来,比如c++的STL。在研读代码的过程当中发现,很多的ReactiveCocoa代码,所以对ReactiveCocoa进行了一个简单的学习。
ReactiveCocoa顾名思义这是一个响应式的编程,响应式编程的一个经典案例就是 a=b+c 其中a是b和c的和。当b和c发生变化的时候,a也就跟着变化,这KVO的一种实现。
ReactiveCocoa还不仅仅只包含响应式的编程思想,ReactiveCocoa还包括了函数式编程思想,其实现在不仅仅是ReactiveCocoa具有函数式编程的优点,很多语言也引入了函数式编程的一些语法豆,比如c++的lambda表达式。函数式编程主要具有以下优点:
1.函数是等同于变量的类型
2.不影响全局变量
3.只用”表达式”,不用”语句”
首先需要了解ReactiveCocoa的基本组成元件.其中Reactive的基本元件式Stream而stream又可以具体分为Signals和Sequences。而其中sequences是可push和可pull的,而其中Signals仅仅只可以push。加上作用在Stream上的一些方法就可以构成这个构架。
学习一门工具,看重的当然是工具的一些特殊用途,例如有了锄头就可以代替手挖东西,带来高速和方便性。在第一眼看到ReactiveCocoa,其实自己并没有注意到ReactiveCocoa其特别的用处,虽然函数式和响应式编程都很有用,但是在框架中怎么体现?在什么情况下我们会使用ReactiveCocoa而不是采用传统的OC来实现。在ReactiveCocoa的帮助文档当中找到了这些,并基于这几个用处在XX上椰看到了不同程度的应用。
ReactiveCocoa 应用场景
1.处理异步或者是事件驱动的数据源问题
这个可以看成是一个KVO,类似于一个观察者模型,当被观察对象发现变化时候需要通知观察者。在ReactiveCocoa当中的的一个command通常指的是RACCommand类;command依据一些行为创建或者订阅一个信号,这样做很方便一些交互式的app。通常command适用于UI-驱动类型的,比如当点击某个按钮时候必须完成一些事情。
中有以下代码
_funcView.qujianBtn.rac_command = [[RACCommand alloc]initWithSignalBlock:^RACSignal *(id input) {//这是一个command的创建
NSLog(@"goto qujian");
@strongify(self);
[CNLoginBarrier checkLoginAndDoBlock:^{
UIViewController *viewController = [STDomainHandler viewController:qujianVCNameKey withParams:nil];
[self.navigationController pushViewController:viewController animated:YES];//这个则是判断这个是取件按钮,将页面转到取件页面
}];
return [RACSignal empty];
}];
其中通过ReactiveCocoa的rac_command的属性我们对每一次按下取件按钮都进行了埋点和转页面操作
2.将相互依赖的操作串联起来
3.并行化独立的操作
这是因为很多rac的执行都是异步的,特别是网络请求部分。这样可以并行化处理,减少卡顿
4.简单化一些集合的操作
利用combineLatest and 等操作实现整体操作命令
本地和服务端的数据传输
在iOS的设计当中经常涉及到服务端和客服端之间的数据交换问题,所以对于服务端和客服端之间的数据传输问题的相关研究就显得十分的重要。一般不同的实体(服务端,客服端)可能采用的不是同一种语言,这时候他们之间的数据传输,就需要一个中介进行数据传输,在IOS客服端和服务端中这个中介就是JSON格式的文件。
中介既然连接着客服端和服务端,那么必然客服端和服务端都能识别。在采用java框架的服务端系统中JSON本就是标配。在IOS 中NSJSONSerialization类就提供了对JSON的全面支持,通过该类可以轻松的将JSON格式转换为dictinary或者是array,同样也可以将字典和数组转化为JSON格式。(JSON本就是简单的key-Value的对应关系)
IOS的界面编辑
MVVM模型
在iOS设计当中,公司采用的是MVVM的设计方式来取代MVC的设计,MVVM的设计模式最早是在微软的.NET中被使用的。很有意思的,关于苹果的很多设计思想来自于微软,其中ReactiveCocoa的思想也是来自于微软的。
传统的MVC采用的是如下图所示的结构:
可以看的到是C负责了很多的工作需要确认来自view的输入,需要从Model中拿数据到View中现实,还要完成一些和View的交互工作。
而在MVVM的过程当中,我们通过将view Controller和View看成一个实体具体的示意图可以涂下图所示
其中这样就把很多工作放在了view Model当中,而View Controller主要就是负责实现和view的交互工作,而view Model主要负责的是数据绑定工作(把来自于model的数据绑定到相关的属性当中)。在XX的设计实现当中可以看到有下示的结尾的数据
- ViewController(这个代表的是MVVM当中的View Controllelr,这个结尾的文件一般被放在了view分组下,因为我们将view和viewController当成了一个实体)
- ViewModel(这个代表的是MVVM中的View Model,这个主要呗放在了View Model的分组下)
- Model(这个代表 P的是MVVM中的model存在有些文件不是没有以model结尾的,但都放在了Model的分组下))
- .xib文件,cell结尾的(代表的是MVVM中的View,所以很多.xib文件和view中不是以view controller结尾的文件大都是属于这一类的。)
view的设计
关于界面的设计一般有三种方法
>
* 采用storyboard的设计
* 采用xib的设计
* 采用第三方库(masonry、snapKit)的设计
采用xib的设计
采用storyboard和xib的设计都是通过采用在导航栏中拖取一定的控件到相应的面板上进行界面的设计。如下图所示
这种设计采用的是通过导航栏左下角拉取一些相应的控件到xib绘制板上通过xib版的位置来制作。这里需要注意的有:
>
* 每个控件的层次,这个在左边的层次界面可以进行拖动修改
* 关于控件的大小可以在导航栏中尺寸进行修改
* xib文件的在加载一般采用的是[UINib nibWithNibName:@”STSchoolTaskCell” bundle:nil] 的方式进行加载的
采用masonry的设计
采用masonry的设计比较容易学习的网站。语法相对比较简单。但是具体在放什么位置有点迷惑?在单纯的view代码中有见到。在view controller中也有见到。
采用xib和masonry设计的对比实验
简单的示范我们希望设计一个滑块控件通过一个label来显示滑块控件的值。具体示意图如下图所示
在采用xib设计的方式时候我们首先创建ViewController类在创建类的时候勾选创建xib文件具体如下图所示
接下来的设计就是xib中进行拖拽控件进行相应的控制,最后在view controller中添加关于显示滑块控件的显示逻辑。因为xib和view Controller中的联系一般是通过IBOutlet和IBAction进行关联,可以在控件上按下control按键进行连接。采用xib的设计可能会让代码比较少。这个例子当中的viewController的代码
#import "sliderViewController.h"
@interface sliderViewController ()
- (IBAction)sliderAction:(id)sender;
@property (strong, nonatomic) IBOutlet UILabel *price;
@end
@implementation sliderViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)sliderAction:(id)sender {
UISlider*ss=(UISlider*)sender;
NSString*nt=[[NSString alloc]initWithFormat:@"%f",ss.value];
self.price.text=nt;
}
@end
但是采用这个方法就存在有一些本来应该是在view中设计的东西跑到了ViewController中进行。特别是在XX设计当中很多字体和颜色都是封装的,所以不可能在xib中进行设计。
其中xib的设计虽然简单明了,但是关于约束部分却变得十分纷乱离杂,特别是在修改约束时候。
采用masonry的设计却十分的简单明了先创建一个基于UIVIew的view视图,在view视图当中完成view相关的工作具体代码见
//
// sliderView.h
//
#import <UIKit/UIKit.h>
#import "Masonry.h"
@interface sliderView : UIView
@property(nonatomic,strong)UILabel*price;
@property(nonatomic,strong)UISlider*slider;
-(IBAction)valueChange:(id)sender;
-(sliderView*)setupView;
@end
//
// sliderView.m
#import "sliderView.h"
#import "Masonry.h"
@implementation sliderView
@synthesize price,slider;
-(IBAction)valueChange:(id)sender
{
price.text=[NSString stringWithFormat:@"%f",((UISlider*)sender).value];
}
-(sliderView*)setupView{
price=[[UILabel alloc]init];
[self addSubview:price];
// price.text=@"dsasdf";
price.backgroundColor=[UIColor colorWithRed:0.1 green:0.8 blue:0.3 alpha:0.5];
self.backgroundColor=[UIColor colorWithRed:0.9 green:0.8 blue:0.3 alpha:0.5];
[self mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeMake(400, 300));
}];
slider=[[UISlider alloc]init];
slider.backgroundColor=[UIColor colorWithRed:0.1 green:0.8 blue:0.3 alpha:0.5];
[slider addTarget:self action:@selector(valueChange:) forControlEvents:UIControlEventValueChanged];
[self addSubview:slider];
[slider mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeMake(200, 50));
make.centerX.mas_equalTo(self);
make.centerY.mas_equalTo(self);
}];
[price mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeMake(200, 50));
make.centerX.mas_equalTo(self);
make.centerY.mas_equalTo(self).offset(-90);
}];
return self;
}
@end
而在viewController中只添加的是和view的一些交互代码
- (void)viewDidLoad {
[super viewDidLoad];
self.svc=[[sliderView alloc]initWithFrame:CGRectMake(0, 0, 400, 300)];
[self.svc setupView];
[self.view addSubview:self.svc];
self.view.frame=self.svc.frame;
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
在XX的设计当中采用masonry更贴合MVVM的设计思想
观察者模式
观察者模式也叫发布(publish )-订阅(Subscribe)模式,是对象之间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。在IOS当中使用观察者模式的情况主要有:
1. 当一个对象的改变时候,其它的对象也跟着这个对象发生变化
2. 一个对象发生变化时候需要通知其它对象,并且不需要知道目的的对象是什么。
在IOS设计当中经常使用ReactiveCocoa来实现该模式通过利用利用RACObserve来实现观察者模式的发布者的注册,当观察者发生变化的时候,可以利用subscribeNext来实现队消息的传递,这个过程当中观察者不需要知道传递给的订阅者是谁。具体例子
[RACObserve(self, extraGoodcell.goodFee)
subscribeNext:^(id x) {
[self.feeCell resetFeeCellWithMinfee:2 andMaxfee:20 andgoodFee:[x
integerValue]
andDiscountFee:0];
}];
在例子中通过将self 目标注册为信息的发布者,通过监测extraGoodcell.goodFee的值变化来通知订阅者。当监测的内容发生变化的时候订阅者就可以依据监测的值执行相应的工作。这个常用于两个View中一个UIview依赖另外一个view的值而设置,一般通过在两个view共同的viewcontroller中来利用上述例子进行消息的绑定。
其它
- 注意nsinterge类显示其数值时候一般是用的“%d”,而不是“%@”
- 尽量的利用一些控件的边框实现一些视觉效果,而不是利用图片来实现
UIcollectionView的学习(一)
简单介绍
本次编程工作需要使用一个UICollectionView来实现顺便带物品的展示功能。之前对这个集合视图不是很了解。所以在编码过程不免很多磕磕碰碰。不同与tableView网上关于这部分的介绍要少很多。UIcollectionView和UItableView一样是继承了UIScollView,同样的集合视图也需要委托协议(delegate)和数据源协议(datasource)来帮忙完成一些事情。其中委托协议主要完成的工作是:负责控件的事件和状态变化的反应,而数据源协议主要是负责沟通控件和应用数据之间的桥梁,例如section数目和row数目。。自定义 Collection View 布局很好的介绍了一些基础知识但是很多小细节是没有明确说明。
其中必须实现的UICollectionViewDataSource协议中的这些函数
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
#warning Incomplete implementation, return the number of sections
return 2;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
#warning Incomplete implementation, return the number of items
return 4;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
// Configure the cell
return cell;
}
这一点和Uitableview的实现很类似。但是UIcollectionView除了UicollectiveViewDatasource和UicollectionViewDelegate两个协议之外还必须实现的一个布局协议UICollectionViewFlowLayout协议。
自定义cell的注意点
在自定义实现UIcollectionViewCell时候,首先继承UIcollectionViewCell类实现基础的cell的设置。然后在定义UIcollectionView,首先需要将该类注册
[self.extraGoodCollectionView registerClass:<#(nullable Class)#>
forCellWithReuseIdentifier:<#(nonnull NSString *)#>];//一个类(纯代码)注册
//
[self.extraGoodCollectionView registerNib:<#(nullable UINib *)#>
forCellWithReuseIdentifier:<#(nonnull NSString *)#>];//一个类(xib文件,带xib文件的)注册
在UICollectiveView中必须实现的一个重要的方法就是
-(instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
[self setupView]; //初始化代码
}
return self;
}
这个方法是在cell初始化的时候自动实现的,也是唯一的一个入口。所以针对cell除了frame我们不能传多余的参数
- 注意_的调用和self.的差别:
如果引用的话不会有区别,是同一个指针,如果赋值是有区别的 self.xx=oo 首先把xxretaincount -1然后retain oo _XX复制直接指向oo 不存在retain这一步步。也不会 - 引用一个类的两种办法
@class:如果你只是定义成员变量、属性
import:如果是继承某个类
事件的捕捉
在IOS中经常需要捕捉一些事件并且通过捕捉到的事件状态及时的更新相应的界面或者触发其他的事件。其中在非游戏编程中最经常遇到的就是捕捉tableView或者是CollectionView中的点击事件。单纯的一个tableview和CollectionView中的一个cell比较容易办,就是采用其代理方法didSelectItemAtIndexPath来进行处理。但是在设计到到cell继续捕捉粒度更小的事件的时候,如果还在cell中进行处理就会使得tableView的代理方法变得庞大杂乱。
在设计很多界面的时候,如果界面可以很明显的分为水平的几个大模块时候就可以采用tableView的设计。通过利用tableView的heightForRowAtIndexPath设置不同模块的高度,而在cellForRowAtIndexPath也是关键的地方可以利用枚举的方法返回不同位置的cell。而cell就是一个单纯的view视图。这样做的好处是是的界面模块之间的删除添加变得简单。
例如在tableview的cell中定义了一个collectionView,当需要去捕捉collectionView中小cell的点击事件的时候。因为collectionView只是tableView种的一个cell,所以其只是视图
其文件一般只是
–collectionView file
–collectionViewModel file
而其中没有响应的
–collectionView controller file
所一般是在–collectionView file中去定义一个代理,利用ReactiveCocoa的消息传递机制去传递回点击的事件。这样通过在TableView controller中的响应可以进行处理相关的点击事件。