这期主要回顾一些 UI 的知识点
1. loadView, viewDidLoad 和 viewDidUnload
loadView 主要是用来创建 controller 的 View, 并且创建过程是懒加载的, 也就是只有用到 controller 的 view 的时候, 才会创建该 controller 的 view
viewDidLoad 主要是对界面做一些初始化操作, 方法调用在 loadView 之后
viewDidUnload 主要是在发生内存警告且 view 被释放的时候会调用, 所以一般用在释放跟界面相关的资源, 将相关的实例都赋值为 nil; 与 dealloc 不同的是 该方法只会在 view 被释放的时候调用, dealloc 是在 UIViewController 被释放时调用
2. UINavigationController, UITabBarController 和 UICollectionView
UINavigationController 的使用步骤
2.1. 创建 UINavigationController
2.2 设置导航控制器为 window 的 rootViewController
2.3 添加子 UIViewController 到导航控制器
UINavigationController 常用的属性和方法
2.4 push 相关如: pushViewController:
2.5 pop 相关如: popViewControllerAnimated:
2.6 初始化相关 initWithRootViewController: 如果用自定义 UINavigationBar 初始化 导航控制器用 initWithNavigationBarClass:
2.7
2.6 viewControllers 和 childViewControllers(readOnly)
topViewController
navigationBar
UINavigationController 是通过栈的形式来管理子控制器, 导航条(navigationBar)的内容和栈顶控制器有关(navigationItem)除了返回按钮是由上一个 UIViewController 决定的
UINavigationBar : iOS 7 以前是不透明的, iOS 7 以后是透明的
在透明情况下, 与 contentView 会重合一部分区域
在不透明情况下, contentView 是跟在导航条的下面
横屏默认高度是 32, 竖屏默认高度是 44
在UINavigationController设置UINavigationController的导航条,
UINavigationBar* navigationBarAppearance = [UINavigationBar appearance];
[navigationBarAppearance setTintColor:[UIColor redColor]];
通过appearance对象可以设置导航条的属性
在UINavigationController可以设置UIBarButtonItem的属性
[UIBarButtonItem appearance];
导航条的返回按钮是交给UINavigationBar管理的
UIStoryboardSegue(手动类型, 自动类型)
identifier
sourceViewController
destinationViewController
手动类型的 segue 需要 手动执行 performSegueWithIdentifier: 可以在需要在UIViewController的prepareForSegue: 里面拦截执行segue消息
控制器之间传递数据: 顺传(prepareForSegue:), 逆传(delegate)
UITabBarController 常见属性和方法
1. selectedViewController
2. selectedIndex
3. tabBar(readOnly, 可通过KVC进行修改)
4. delegate
5. viewControllers
UICollectionView可以从缓冲池中取Cell, 取不到系统会帮我们创建, 需要告诉系统我们需要创建什么样的Cell, 注册Cell到系统中
[collectionView
registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@""];
如果cell是从xib中创建的需要注册xib
UICollectionViewLayout (父类)
UICollectionViewFlowLayout(流水布局)
3. UITextField 监听事件的几种方法
addTarget:
delegate
NotificationCenter
4. UITableViewCell 的 initWithStyle: 和 awakeFromNib:
5. Preference 和 NSKeyedArchiver
[[NSUserDefaults standardUserDefaults] setObject:@""forKey:@""]
[[NSUserDefaults standardUserDefaults] synchronize]
偏好设置的局限: 将所有的数据只保存到一个文件, 一般保存应用程序配置信息;不能存储自定义的对象
[NSKeyedArchiver archiveRootObject:@""toFile:@""] 对应 [NSKeyedUnarchiver unarchiveObjectWithFile:@""]
自定义的对象用归档必须实现NSCoding协议
保存文件: - (void)encodeWithCoder:(NSCoder*)aCoder
读取文件: - (instancetype)initWithCoder:(NSCoder*)aDecoder
6. 应用程序沙盒文件系统目录 (iOS 8 以前每个应用程序沙盒隔离, iOS 8 以后开放)
Documents : 需要持久化的重要的数据, iTunes 同步设备会备份该目录
Library/Caches: 需要持久化非重要的很大数据, 同步设备不会备份
Library/Preference: 同步设备会备份, 一般存储系统的偏好设置
temp: 临时数据
获取程序沙盒下的 Documents 目录:
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]
获取应用程序沙盒目录: NSHomeDirectory()
7. UITableView 编辑模式
要重写UITableViewDataSource的commitEditingStyle:方法会有删除界面, 在这个方法里面判断是删除还是添加
只有在 UITableView 编辑模式下才能看见cell的添加按钮, editing设置true开启编辑模式
告诉系统开启的是什么编辑模式: editingStyleForRowAtIndexPath:
先delete模型数据, 再删除tableview指定的cell(性能优于reloadData)
8. Modal
presentViewController:
dismissViewControllerAnimated:(可以调用该控制器以及该控制器自控制器让该控制器消失)
9. Quartz2D
只有在drawRect: 方法才能取得跟view相关的图像上下文
当view第一次显示在屏幕上的时候会调用drawRect:方法
调用view的setNeedsDisplay 或者setNeedsDisplayRect时系统会调用drawRect:
获取Layer的上下文: CGContextRef contextRef =UIGraphicsGetCurrentContext();
设置起点: CGContextMoveToPoint(contextRef,10,0);
设置终点: CGContextMoveToPoint(contextRef,20,20);
设置绘图状态: CGContextSetRGBStrokeColor(contextRef,1.0,0,0,1.0);
设置线条宽度: CGContextSetLineWidth(contextRef,10);
设置线条起点和终点样式: CGContextSetLineCap(contextRef,kCGLineCapRound);
设置线条转角的样式: CGContextSetLineJoin(contextRef,kCGLineJoinRound);
渲染图形到view上: CGContextStrokePath(contextRef); (实心CGContextFillPath(contextRef));
补充:
绘制线条只能通过空心来画 (CGContextStrokePath)
关闭图形 CGContextClosePath(contextRef);
绘制四边形 CGContextAddRect(contextRef,CGRectMake(10,10,100,100));
矩阵操作(先设置再绘图) CGContextRotateCTM(contextRef,M_PI_4);
绘制圆 CGContextAddEllipseInRect(contextRef,CGRectMake(100,100,30,40));
超出圆的部分裁剪 CGContextClip(contextRef); 渲染圆之前调用
绘制圆弧 CGContextAddArc(contextRef,30,30,30,0,M_PI_2,0);
调用OC的方法设置绘图颜色: [[UIColor redColor] set]; 此方法不能用早CALayer的drawInContext:中
贝塞尔曲线
当前点: CGContextMoveToPoint(contextRef,10,10);
控制点和终点: CGContextAddQuadCurveToPoint(contextRef,30,30,20,20);
图形上下文栈
保存上下文 CGContextSaveGState(contextRef);
还原上下文 CGContextRestoreGState(contextRef);
凡是通过Quartz2D中的带有create/copy/retain 方法创建出来的值都必须手动的释放
CGPathRelease(path) / CFRelease(path)
截屏
创建一个bitmap的上下文
UIGraphicsBeginImageContext(self.viewForFirstBaselineLayout.frame.size);
将屏幕绘制到上下文
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
从上下文中取出图片
UIImage*drawImage =UIGraphicsGetImageFromCurrentImageContext();
NSData*data =UIImagePNGRepresentation(drawImage);
保存到相册
UIImageWriteToSavedPhotosAlbum(drawImage,self,@selector(image:didFinishSavingWithError:contextInfo:),nil);
10. 手势识别器(UIGestureRecognizer, 抽象类)
通过touches方法监听view事件, 有下面几个局限
1. 需要自定义view(UIStoryboard 可以拖手势识别控件)
2. 由于view内部的touches方法中监听的触摸事件, 以此默认情况下, 无法让其他的外界对象监听view的触摸事件
3. 不容易区分用户的手势行为
UITapGestureRecognizer 点击
UIPinchGestureRecognizer 捏合
UIPanGestureRecognizer 拖拽
UISwipeGestureRecognizer 轻扫 (默认从左往右)
UIRotationGestureRecognizer 旋转
UILongPressGestureRecognizer 长按
1. 创建手势识别器
UITapGestureRecognizer*tapGestureRecognizer = [[UITapGestureRecognizer alloc] init];
tapGestureRecognizer.delegate = self
2. 添加手势识别器到view
[self.view addGestureRecognizer:tapGestureRecognizer]
3. 监听手势识别器
[tapGestureRecognizer addTarget:self action:@selector(tapTest)];
注: UIImageView 默认是不支持和用户交互的
11. CALayer 和 UIView
在创建UIView 的时候, UIView内部会自动创建一个CALayer, 用过UIView的layer属性可以访问这个图层
UIView之所以显示在屏幕上, 完全是因为它内部的一个图层
UIView不具备显示的功能, UIView 的CALayer才具有显示的功能
通过操作CALayer对象, 可以很方便访问UIView的一些外观属性,比如
阴影, 圆角大小, 边框宽度和颜色
如果一个控件是另外一个控件的子空间, 那么这个控件的layer也是另外一个控件layer的子layer
CALayer是定义在QuartzCore框架中的 (跨平台)
CGImageRef 和 CGColorRef是定义在CoreGraphics中的 (跨平台)
相比CALyer(性能高于UIView), UIView 多了事件处理的功能
CALayer常见属性
self.layer.borderColor= [UIColor redColor].CGColor;
self.layer.cornerRadius=10; (设置主图层的圆角)
self.layer.masksToBounds=YES; (类似UIView的self.clipsToBounds=YES)
self.layer.shadowColor= [UIColor redColor].CGColor;
self.layer.shadowOffset=CGSizeMake(10,30);
self.layer.shadowOpacity=0.5;(默认是完全透明的0)
self.layer.contents= (id)([UIImage imageNamed:@""].CGImage); (设置的image展示在子图层)
transform, bounds, position
[self.layer setValue:@(10)forKey:@"transform.translation.x"];
自定义CALyer
CALayer*layer = [CALayer layer];
[self.layer addSublayer:layer];
Position 和 AnchorPoint
Position 用来设置 CALayer 在父层中的位置, 以父层的左上角为原点(0, 0)
AnchorPoint 决定CALayer的哪个点在position属性所指的位置, 以自己的左上角为原点, x,y取值范围都是0-1, 默认是(0.5, 0.5)
所有非Root Layer(手动创建CALyer), 都有隐身动画(动画属性)
关闭隐身动画
[CATransaction begin];
[CATransaction setDisableActions:YES];
[CATransaction commit];
CALayer 画图的两种方式(都需要手动调用CALayer 的 setNeedsDisplay)
1. CALayer 的 drawInContext:
2. CALayer的delegate
- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)ctx
10. Core Animation
Core Animation 是自己接作用在 CALayer 上的, 并非UIView, 执行动画的过程都是在后台操作的, 不会阻塞主线程
1. 初始化一个CAAnimation(抽象类)的子类对象, 并设置一些相关属性
2. 调用CALayer 的 addAnimation:forKey: 方法增加CAAnimation对象到CALayer中
3. 调用CALayer 的 removeAnimationForKey:可以停止CALayer中的动画
CAAnimation 的四个子类
CABasicAnimation (fromValue, toValue)
CAKeyFrameAnimation (values, path, keyTimes)
CATransition (type, subtype, startProgress, endProgress)
CAAnimationGroup (animations)
局限: 表面动画执行, 实际上控件的frame没有改变, 如果需要frame也改变, 用UIView的动画
参照: http://www.jianshu.com/p/8c1c1697c0ce
11. initWithFrame: 和 layoutSubviews:
initWithFrame一般用在自定义控件中,对自定义的控件添加子控件, 保证封装性
在自定义的控件中一般都需要delegate属性与外交交互
设置控件的frame必须要在layoutSubviews: 这个方法中操作, 如果在initWithFrame:设置frame, 在init方法中调initWithFrame, frame是没有值的
12. 获取系统的iOS版本
[[UIDevice currentDevice].systemVersion doubleValue]
13. UIImage图片拉伸和切割
1. resizableImageWithCapInsets:
2. stretchableImageWithLeftCapWidth:
iOS 7 以后可以用图形化界面设置图片拉伸, 拉伸参数编译器会为你设置
图片切割
CGImageCreateWithImageInRect(image.CGImage,CGRectMake(0,0,20,20));
注意: CGImage中rect是当像素来使用, UIKit是点坐标系, 在非retain屏中一个点等于一个像素, retain屏等于两个像素
可以通过[UIScreen mainScreen].scale 来获取是否为retain屏
14. + (void)initialize 和 + (void)load
该类第一次使用才会调用initialize, 在父子关系中可能会调用多次
load
15. 多线程
iOS中的主线程(UI线程)
显示(刷新)UI界面
处理UI事件
耗时的操作不要放在主线程
NSThread (程序员管理内存)
GCD (替代NSThread, 充分利用设备多线程, 自动管理内存)
NSOperation (基于GCD, 更加面向对象, 自动管理内存)
自定义NSOperation把想做的操作放在- (void)main中去执行, 还需要经常调用isCancelled方法对取消做出响应
注意: 自己创建自动释放池(因为异步操作无法访问主线程的自动释放池)
NSThread*thread = [[NSThread alloc]initWithTarget:self selector:@selector(run)object:nil];
[threadstart];
创建显示线程自动启动:
[NSThread detachNewThreadSelector:@selector(run)toTarget:self withObject:nil];
创建隐式并自动启动:
[self performSelectorInBackground:@selector(run)withObject:nil]
加锁 (锁一份代码只需要一分锁, 多把锁无效)可达到线程同步
@synchronized(self) {
}
线程之间通信:
转换线程的时候传数据 (常用情况是子线程下载完东西把数据传到主线程)
GCD两个核心概念
1. 任务 (同步还是异步)
2. 队列 (只影响任务的执行方式, 并发还是串行)
并发队列: 可以让多个任务并发(同时)执行,自动开启多个线程同时执行任务, 并发功能只有在异步(dispatch_async)函数下才有效
获得全局的并发队列: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
串行队列: 一个任务执行完了再执行下一个
dispatch_queue_create("quequeName",NULL) 非ARC需要释放dispatch_release(queue);
也可以用CFRelease(queue), CoreFundation 和 Fundation框架的数据类型是可以相互转化的(桥接)
dispatch_get_main_queue() (线程间通信有用)添加到主线程的任务都会放到主线程中去执行
同步任务放到主线程可能会引起死锁
使用GCD步骤
1. 定制任务(同步, 异步具备开新线程的能力)
2. 将任务添加队列
GCD自动从队列中取出任务, 放到相应的线程中执行
同步: 在当前线程执行, 不具备开新线程的能力
异步: 在新的线程中执行任务, 具备开新线程的能力
并发: 多个任务同时执行
串行: 一个任务执行完后再执行另一个任务
常见的延迟操作:
[self performSelector:@selector(delayTest:)withObject:nil afterDelay:3.0];
这个方法在当前程操作, 不会另开线程
dispatch_after 代码块, 只要不是主队列, 就会开线程执行
一次性代码: dispatch_once
16. 需要设置按钮的image 和 backgroundImage, 建议先把按钮的类型改为custom, 才能保证设置成功
17. 在OC中成员变量不能以new开头
18. 只有在init开头的构造方法方法中, 才能允许对self进行赋值, 默认的是驼峰命名
iOS 中发送HTTP请求的方案
1. 苹果原生
NSURLConnection
NSURLSession (iOS 7 )
常用类
NSURL: 请求地址
NSURLRequest
NSURLConnection 使用步骤
1. 创建一个NSURL对象, 设置请求路径
2. 传入NSURL创建一个NSURLRequest对象, 设置请求头请求体
3. 使用NSURLConnection发送NSURLRequest
19. JSON 和 XML
苹果原生: NSJSONSerialization(系列化器)常见方法
JSON - > OC对象
+ (id)JSONObjectWithData:
OC 对象 ->JSON
+(NSData *)dataWith
XML 解析:
1. 苹果原生
NSXMLParser: SAX方式解析, 使用简单 (比较适合大的文件)
GDataXML: DOM方式解析 (一次性将整个XML文档加载进内存, 比较适合小的文件解析, 需要添加libxml2.dylib库)
动态库需要在Build settings中
1. 搜索Header Search Paths添加/user/include/linxml2
2. 还需需要搜索Other Links, 结果Other Linker Flags中加-lxml2
3. 在ARC中导入第三方库(非ARC), 在Build Phases中Compile Sources的问价添加-fno-objc-arc
GDataXML使用:
1. GDataXMLDocument
2. GDataXMLElement
解析XML数据
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData: data options: 0 error: nil];
获得文档里面的根元素: rootElement *root = doc.rootElement ;
获得跟元素里面所有的test元素:
NSArray *array = [root elementsForName:@"test"];
array 里面每个元素都是GDataXMLElement
GDataXMLElement *element = array[0] ;
[element attributeForName:@"node"].stringValue;
libxml2: 同时支持DOM 和 SAX
NSXMLParser*parser = [[NSXMLParser alloc] init];
parser.delegate=self;
[parser parse]; 同步执行
刷新表格
NSHTTPURLResponse 是 NSURLResponse 的子类, 用NSURLConnection(默认是get请求)发送异步请求放回的NSURLResponse, 只有转换成NSHTTPURLResponse, 才能拿到响应头,响应行等信息
发送post请求:NSMutableURLRequest
URL中不能包含中文, 得给中文进行转码
stringByAddingPercentEncodingWithAllowedCharacters:
stringByAddingPercentEscapesUsingEncoding deprecated(苹果废弃的方法)
发送JSON给服务器仅仅设置请求体是不够的, 得在请求头里面设置MIMEType
[request setValue:@"application/json" forHTTPHeaderField:@"Content-type"];
默认情况Content-type是普通参数
网络状态监控:
需要添加SystemConfiguration.framework框架
导入Reachability.h / Reachability.m
Reachability *reachability = [Reachability reachabilityForLocalWiFi];
主动获得wiFi状态:
NetworkStatus status = reachability.currentReachabilityStatus;
需要调用reachability(全局)对象的开始监听方法才能监控网络变化通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkServiceTypeChange:) name:kReachbilityChangeNotification object:nil];
小文件下载:
NSData
NSURLConnection send.....
大文件下载:
创建完对象后会发起一个异步的请求
NSURLConnection connection....delegate(NSURLConnectionDataDelegate)
无暂停功能,只能取消
要做断点下载需要设置请求头:
NSString*range = [NSString stringWithFormat:@"bytes=%lld-", currentLength];
[request setValue:range forHTTPHeaderField:@"Range"];
多线程断点下载:
利用NSFileManager 和 NSFileHandle 优化大文件下载内存:
NSFileManager创建一个0kb的文件(Caches)
NSFileHandle 把句柄移到创建文件的最后面
NSFileHandle 写数据到文件 (用完需要关闭文件并且把它清空)
NSURLSession: iOS 7 推出, 目的是取代NSURLConnection
得到Session对象:
NSURLSession*session = [NSURLSession sharedSession];
任务: 任何请求都是一个任务
NSURLSessionDataTask: 普通的GET / POST 请求
NSURLSessionDownloadTask: 文件下载 (系统默认是下载到temp文件夹下, 需要用NSFileManager把下载文件剪切到Documents文件夹下)
如果要想知道下载任务进度,session需要用NSURLSession得sessionWithConfiguration:方法
创建,在此方法中设置delegate(注意session创建的任务方法里面不能有block, 否则delegate里面的方法不会调用)
NSURLSessionUploadTask: 上传任务, 一般HTTP服务器不支持
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request ...
[dataTask resume];
dataTask cancelByProducingResumeData: 做断点下载需要保存传入的data方便下次继续下载downloadTaskWithResumeData:
KVO、通知NSNotification和代理的比较
KVO/通知NSNotification/代理这三种方法主要是用来监听事件发生的
两者都是观察者模式,不同的是,KVO是被观察者直接发送消息给观察者,是对象间的交互,而通知则是观察者和被观察者通过通知中心对象之间进行交互,即消息由被观察者发送到通知中心对象,再由中心对象发给观察者,两者之间并不进行直接的交互
代理只能一对一,一个对象只有一个代理,而KVO和通知可以一对多,一个通知可以发给多个观察者
网友评论