UIView的常见属性html
UIView的常见属性java
- (void)addSubview:(UIView *)view;
linux
添加一个子控件viewios
- (void)removeFromSuperview;程序员
从父控件中移除web
- (UIView *)viewWithTag:(NSInteger)tag;面试
根据一个tag标识找出对应的控件(通常都是子控件)算法
transform属性sql
利用transform属性能够修改控件的位移(位置)、缩放、旋转数据库
建立一个transform属性
CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty) ;
CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy);
CGAffineTransform CGAffineTransformMakeRotation(CGFloat angle)
(注意:angle是弧度制,并非角度制)
在某个transform的基础上进行叠加
CGAffineTransform CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty);
CGAffineTransform CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy);
CGAffineTransform CGAffineTransformRotate(CGAffineTransform t, CGFloat angle);
清空以前设置的transform属性
view.transform = CGAffineTransformIdentity;
1笔记
1. IBAction的参数
========================================
- (IBAction)left:(UIButton *)button
1> 在OC中,绝大多数的控件监听方法的第一个参数就是控件自己
2> 默认连线时的参数类型是id
3> 若是要在监听方法中,方便控件的使用,能够在连线时或者连线后,修改监听方法的参数类型
2. 修改对象的结构体成员
========================================
在OC中,不容许直接修改“对象”的“结构体属性”的“成员”,可是容许修改“对象”的“结构体属性”
修改结构体属性的成员方法以下:
1> 使用临时变量记录对象的结构体属性
2> 修改临时变量的属性
3> 将临时变量从新设置给对象的结构体属性
3. 在程序开发中须要避免出现魔法数字(Magic Number)
========================================
使用枚举类型,能够避免在程序中出现魔法数字
1> 枚举类型实质上就是一个整数,其做用就是用来替代魔法数字
2> 枚举类型中,指定了第一个整数以后,后面的数字会递增
4. frame & bounds & center
========================================
1> frame能够修改对象的位置和尺寸
2> bounds能够修改对象的尺寸
3> center能够修改对象的位置
5. 首尾式动画
========================================
// beginAnimations表示此后的代码要“参与到”动画中
[UIView beginAnimations:nil context:nil];
// setAnimationDuration用来指定动画持续时间
[UIView setAnimationDuration:2.0];
self.headImageView.bounds = rect;
......
// commitAnimations,将beginAnimation以后的全部动画提交并生成动画
[UIView commitAnimations];
6. transform属性
========================================
在OC中,经过transform属性能够修改对象的平移、缩放比例和旋转角度
经常使用的建立transform结构体方法分两大类
1> 建立“基于控件初始位置”的形变
CGAffineTransformMakeTranslation
CGAffineTransformMakeScale
CGAffineTransformMakeRotation
2> 建立“基于transform参数”的形变
CGAffineTransformTranslate
CGAffineTransformScale
CGAffineTransformRotate
补充:
在OC中,全部跟角度相关的数值,都是弧度值,180° = M_PI
正数表示顺时针旋转
负数表示逆时针旋转
提示:因为transform属性能够基于控件的上一次的状态进行叠加形变,例如,先旋转再平移
所以在实际动画开发中,当涉及位置、尺寸形变效果时,大多修改控件的transform属性,
而不是frame、bounds、center
7. 使用代码建立控件
========================================
在OC开发中,Storyboard中的全部操做均可以经过代码实现,程序员必定要熟练掌握代码布局界面的能力!
使用代码建立控件的步骤以下:
1> 使用控件对应类建立对象
2> 设置对象属性:frame\color\text\image\backgroundImage……
3> [self.view addSubview:btn];将控件添加到视图
设置控件监听方法的示例代码以下:
[btn addTarget:self action:@selector(click:) forControlEvents:UIControlEventTouchUpInside];
提示:
1> addTarget方法定义在UIControl类中,这意味着能够给全部继承自UIControl类的对象添加监听方法
2> 监听方法的第一个参数就是对象自己
3> 监听方法的第二个参数是监听控件的事件
8. viewDidLoad
========================================
viewDidLoad是视图加载完成后调用的方法,一般在此方法中执行视图控制器的初始化工做
在viewDidLoad方法中,必定不要忘记调用父类的方法实现!
[super viewDidLoad];
图片浏览器
1. 界面分析
========================================
1> 须要读取或修改属性的控件须要设置属性
// 序号标签
// 图片
// 图片描述
// 左边按钮
// 右边按钮
2> 须要监听响应事件的对象,须要添加监听方法
// 左边按钮
// 右边按钮
2. 手码懒加载建立控件的步骤
========================================
1> 定义控件属性,注意:属性必须是strong的,示例代码以下:
@property (nonatomic, strong) UIImageView *icon;
2> 在属性的getter方法中实现懒加载,示例代码以下:
- (UIImageView *)icon
{
if (!_icon) {
// 计算位置参数
CGFloat imageW = 200;
CGFloat imageX = (320 - imageW) / 2;
CGFloat imageH = 200;
CGFloat imageY = 80;
// 实例化图像视图
_icon = [[UIImageView alloc] initWithFrame:CGRectMake(imageX, imageY, imageW, imageH)];
// 将图像视图添加到主视图
[self.view addSubview:_icon];
}
return _icon;
}
使用懒加载的好处:
1> 没必要将建立对象的代码所有写在viewDidLoad方法中,代码的可读性更强
2> 每一个控件的getter方法中分别负责各自的实例化处理,代码彼此之间的独立性强,松耦合
3. 使用Plist文件
========================================
使用Plist文件的目的:将数据与代码分离
加载方法:
NSString *path = [[NSBundle mainBundle] pathForResource:@"ImageData" ofType:@"plist"];
_imageList = [NSArray arrayWithContentsOfFile:path];
提示:一般在方法中出现File字眼,一般须要传递文件的全路径做为参数
Tomcat
1. Images.xcassets中的素材
========================================
1> 只支持png格式的图片
2> 图片只支持[UIImage imageNamed]的方式实例化,可是不能从Bundle中加载
3> 在编译时,Images.xcassets中的全部文件会被打包为Assets.car的文件
2. UIImageView的序列帧动画
========================================
// 0. 是否正在动画
[self.tom isAnimating];
// 1. 设置图片的数组
[self.tom setAnimationImages:arrayM];
// 2. 设置动画时长,默认每秒播放30张图片
[self.tom setAnimationDuration:arrayM.count * 0.075];
// 3. 设置动画重复次数,默认为0,无限循环
[self.tom setAnimationRepeatCount:1];
// 4. 开始动画
[self.tom startAnimating];
// 5. 动画播放完成后,清空动画数组
[self.tom performSelector:@selector(setAnimationImages:) withObject:nil afterDelay:self.tom.animationDuration];
3. UIImage imageNamed
========================================
在图片使用完成后,不会直接被释放掉,具体释放时间由系统决定,适用于图片小,经常使用的图像处理
若是要释放快速释放图片,可使用[UIImage imageWithContentsOfFile:path]实例化图像
4. 方法重构的策略
========================================
1> 将具备共性的代码复制到一个新的方法
2> 根据不一样的调用状况,增长方法的参数
提示:在写程序时不要着急重构,有时候把代码先写出来,更容易看清楚如何重构才会更好!
5. Bundle中的图片素材
========================================
往项目中拖拽素材时,一般选择
1> Destination: 勾选
2> Folders:
选择第一项:黄色文件夹
Xcode中分文件夹,Bundle中全部所在都在同一个文件夹下,所以,不能出现文件重名的状况
特色:
*** 能够直接使用[NSBundle mainBundle]做为资源路径,效率高!
*** 可使用[UIImage imageNamed:]加载图像
选择第二项:蓝色文件夹
Xcode中分文件夹,Bundle中一样分文件夹,所以,能够出现文件重名的状况
特色:
*** 须要在[NSBundle mainBundle]的基础上拼接实际的路径,效率较差!
*** 不能使用[UIImage imageNamed:]加载图像
6. 文件管理
========================================
[NSFileManager defaultManager]
经常使用方法
1> 判断文件是否存在
- (BOOL)fileExistsAtPath:(NSString *)path;
2> 将文件从源路径复制到目标路径
- (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;
3> 删除文件
- (BOOL)removeItemAtPath:(N
UIImageView帧动画相关属性和方法
@property(nonatomic,copy) NSArray *animationImages;
须要播放的序列帧图片数组(里面都是UIImage对象,会按顺序显示里面的图片)
@property(nonatomic) NSTimeInterval animationDuration;
帧动画的持续时间
@property(nonatomic) NSInteger animationRepeatCount;
帧动画的执行次数(默认是无限循环)
- (void)startAnimating;
开始执行帧动画
- (void)stopAnimating;
中止执行帧动画
- (BOOL)isAnimating;
是否正在执行帧动画
方式一:有缓存(图片所占用的内存会一直停留在程序中)
+ (UIImage *)imageNamed:(NSString *)name;
name是图片的文件名
方式二:无缓存(图片所占用的内存会在一些特定操做后被清除)
+ (UIImage *)imageWithContentsOfFile:(NSString *)path
- (id)initWithContentsOfFile:(NSString *)path;
path是图片的全路径
知识扩充
1. @property的参数说明
========================================
ARC是苹果为了简化程序员对内存的管理,推出的一套内存管理机制
使用ARC机制,对象的申请和释放工做会在运行时,由编译器自动在代码中添加retain和release
1> strong:强指针引用的对象,在生命周期内不会被系统释放
在OC中,对象默认都是强指针
2> weak:弱指针引用的对象,系统会当即释放
弱指针能够指向其余已经被强指针引用的对象
@property参数使用小结:
1> 控件用weak
2> 属性对象用strong
3> 非对象类型用assign
4> 字符串NSString用copy
提示:在纯手码实现界面布局时,若是经过懒加载处理界面控件,须要使用strong强指针
2. 运行循环
========================================
在iOS的应用程序中,应用程序启动以后,系统即会建立一个运行循环监听用户的交互。
如下代码其本质是在运行循环中注册一个监听事件
[button addTarget:self action:@selector(click:) forControlEvents:UIControlEventTouchUpInside];
当运行循环检测到button的UIControlEventTouchUpInside事件时,会给视图控制器(self)发送一个click消息。
应用管理的笔记
1. 开发前的思路
========================================
1> 从mainBundle中加载Plist
2> 按照plist中的数据数量先肯定各个appView的大小和位置
3> 使用代码建立appView中的子控件,并显示内容
2. 关于九宫格布局的计算方法
========================================
关于如何计算界面的九宫格布局,其实能够有若干种方法,没必要死记课堂的代码,
要可以顺利计算出每个小格子准确的坐标,建议:
1> 先建立若干小的视图
2> 找到本身理解比较容易的计算方法
3> 编写循环建立九宫格布局
要求:可以公用的常量尽可能给抽取出来,以便增长九宫格布局的灵活性,尽可能保证作到:
1> 根据要显示的数据自动调整小格子的位置和数量
2> 一旦调整了要显示的列数,仅须要修改少许的数值便可作到
3. 关于UIButton的一些补充
========================================
3.1 按钮的类型
在iOS的控件中,只有UIButton提供了类方法,能够在实例化按钮时指定按钮的不一样类型。
UIButtonTypeCustom和[[UIButton alloc] init]是等价的
3.2 修改按钮字体
在UIButton中定义有两个readonly的属性:
1> titleLabel
2> imageView
@property中readonly表示不容许修改这两个属性的指针地址,可是能够修改其属性
注意:因为按钮的字体大小是全部状态共用的,所以能够经过
button.titleLabel.font= [UIFont systemFontOfSize:14.0];
修改按钮标签文本的字体大小
可是不能使用如下代码设置按钮标签的文本内容
button.titleLabel.text = @"下载";
由于按钮标签的文本内容是跟按钮的状态向关的
4. 块动画
========================================
4.1 首尾式动画
若是只是修改控件的属性,使用首尾式动画仍是比较方便的,可是若是须要在动画完成后作后续处理,就不是那么方便了
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
// 修改属性的动画代码
// ......
[UIView commitAnimations];
4.2 块动画
块动画相对来讲比较灵活,尤其重要的是可以将动画相关的代码编写在一块儿,便于代码的阅读和理解
[UIView animateWithDuration:2.0 animations:^{
// 修改控件属性动画
label.alpha = 0.0;
} completion:^(BOOL finished) {
// 删除控件
[label removeFromSuperview];
}];
5. 字典转模型
========================================
5.1 字典转模型的好处:
1> 下降代码的耦合度
2> 全部字典转模型部分的代码统一集中在一到处理,下降代码出错的概率
3> 在程序中直接使用模型的属性操做,提升编码效率
模型应该提供一个能够传入字典参数的构造方法
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)xxxWithDict:(NSDictionary *)dict;
5.2 instancetype & id
1> instancetype在类型表示上,跟id同样,能够表示任何对象类型
2> instancetype只能用在返回值类型上,不能像id同样用在参数类型上
3> instancetype比id多一个好处:编译器会检测instancetype的真实类型
5.3 在模型中添加readonly属性
// 定义属性时,会生成getter&setter方法,还会生成一个带下划线的成员变量
// 而若是是readonly属性,则只会生成getter方法,同时没有成员变量
@property (nonatomic, strong, readonly) UIImage *image;
@interface LFAppInfo()
{
UIImage *_imageABC;
}
- (UIImage *)image
{
if (!_imageABC) {
_imageABC = [UIImage imageNamed:self.icon];
}
return _imageABC;
}
在模型中合理地使用只读属性,能够进一步下降代码的耦合度。
5.4 使用数据模型的好处:
*** 调用方不用关心模型内部的任何处理细节!
6. XIB
========================================
Xib文件能够用来描述某一块局部的UI界面
XIB & Storyboard
相同点:
1> 都用来描述软件界面
2> 都用Interface Builder工具来编辑
不一样点
1> Xib是轻量级的,用来描述局部的UI界面
2> Storyboard是重量级的,用来描述整个软件的多个界面,而且能展现多个界面之间的跳转关系
7. View的封装思路
========================================
1> 若是一个view内部的子控件比较多,通常会考虑自定义一个view,把它内部子控件的建立屏蔽起来,不让外界关心
2> 外界能够传入对应的模型数据给view,view拿到模型数据后给内部的子控件设置对应的数据
快捷键
新建
shift + cmd + n 新建项目
cmd + n 新建文件
视图
option + cmd + 回车 打开助理编辑器
cmd + 回车 显示主窗口
cmd + 0 导航窗口
option + cmd + 0 工具窗口
在.m & .h之间切换 control + cmd + 上/下
按照浏览文件的先后顺序切换 control + cmd + 左右
查看头文件 control + cmd + j
切换到对应的函数 control + 6 支持智能输入,注意输入法
运行
cmd + r 运行
cmd + . 中止
cmd + b 编译
cmd + shift + b 静态内存分析编译,能够检查程序结构上是否存在内存泄露
排版
control + i 将选中按钮从新缩进
cmd + ] 向右增长缩进
cmd + [ 向左减小缩进
cmd + / 注释/取消注释,提示:取消注释时,注释双斜线必须在行首
cmd + 向上 到文件开始位置
cmd + 向下 到文件末尾位置
UIButton的常见设置
- (void)setTitle:(NSString *)title forState:(UIControlState)state;
设置按钮的文字
- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state;
设置按钮的文字颜色
- (void)setImage:(UIImage *)image forState:(UIControlState)state;
设置按钮内部的小图片
- (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state;
设置按钮的背景图片
设置按钮的文字字体(须要拿到按钮内部的label来设置)
btn.titleLabel.font = [UIFont systemFontOfSize:13];
- (NSString *)titleForState:(UIControlState)state;
得到按钮的文字
- (UIColor *)titleColorForState:(UIControlState)state;
得到按钮的文字颜色
- (UIImage *)imageForState:(UIControlState)state;
得到按钮内部的小图片
- (UIImage *)backgroundImageForState:(UIControlState)state;
得到按钮的背景图片
猜图笔记
1. 搭建界面
1> 上半部分,固定的,能够用Storyboard实现
2> 下半部分,根据题目变化,能够考虑用代码实现
2. 图片的放大缩小
1> 放大过程
(1) 增长蒙版(遮罩),蒙版默认alpha = 0
(2) 将图片移动到视图的顶层
(3) 计算图片的目标位置,将蒙版的alpha改成0.5,动画显示
2> 缩小过程
(1) 恢复图片位置,动画显示
(2) 隐藏蒙版,将蒙版的默认alpha改成0
提示:若是按钮的alpha = 0,则不能响应用户交互
3. 加载数据,字典转模型(KVC)
1> KVC (key value coding)键值编码
KVC容许间接修改对象的属性值,是cocoa的大招!
提示:使用setValuesForKeys要求类的属性必须在字典中存在。类中的属性能够比字典中的键值多,可是不能少!
2> 下一题按钮的交互
当到达最后一题时,禁用下一题按钮。
4. 游戏的交互部分实现
1> 增长答案视图和备选答案视图,简化按钮的布局
利用九宫格算法动态添加按钮
2> 点击备选按钮,文字到上面
3> 点击答案按钮,文字到下面
4> 答案的检测
(1) 错误,须要提示用户
(2) 正确,提示用户,自动进入下一题
5. 提示功能
6. 首尾工做
1> 图标
2> 启动画面
iTools 将手机中的应用程序导出到电脑上
搜索引擎: 疯狂猜图 ipa
KVC KVO
KVC key value Coding 键值编码
KVO key value Observer 键值观察
介绍UIScrollView
1.介绍UIScrollView
2.大图展现(UIScrollView使用)
1.给scrollView添加内容
2.设置contentSize属性(只能在代码中设置)
3.UIScrollView重要属性
1.contentOffset(经过按钮改变其位移)
2.contentInset
注意:经过stroryboard设置当即生效,经过代码设置需配合contentOffect手动位移
3.其余属性
4.喜马拉雅
1.storyboard界面布局(减小代码)
2.经过最后按钮的frame获取contentSize的height(CGRectGetMaxY(frame))
3.storyboard设置contentInset(使用代码设置还需设置contentOffset)
5.scrollView代理方法
1.要想成为代理遵照协议(UIScrollViewDelegate)
1.声明协议(通常协议名称:控件名称 + Delegate)
2.实现协议定义的接口方法
3.设置代理(UIViewController成为scrollView的代理)
6.scrollView实现缩放功能
1.要想成为代理遵照协议(UIScrollViewDelegate)
1.声明协议(通常协议名称:控件名称 + Delegate)
2.实现协议定义的接口方法()
2.设置代理(UIViewController成为scrollView的代理)
3.设置最大、最小缩放倍数(注:倍数相等时,没法缩放)
7.图片轮播功能
1.将内容添加到scrollView中(5张图片)
2.设置scrollView的contentSize
3.设置翻页属性
4.添加UIPageControl控件
5.设置UIPageControl的页码 (利用contentSize计算页码)
6.增长自动滚动(NSTimer实现)
1.介绍UIScrollView的背景
2.介绍scrollView一些属性(主要有三个)
1>.要想使用scrollView必须作两件事
1.设置scrollView内容
2.设置contentSize
2>.其余重要属性
1.contentOffset(滚动位置)
2.contentInset(注意:在storyborad里面设置效果不一样)
3.喜马拉雅项目
1>.分析页面结构(scrollView的frame肯定)
2>.在storyboard拖控件
3>.重点scrollView(设置内容而且设置contentSize)
4>.调节scrollView的显示位置
4.代理
1>代理思想两个思想
1).监听思想:B监听A发生了什么事情
2).通知思想:A发生了一些事情,要通知B去作
2>scrollView的代理使用
1).如何成为代理(三步)
*声明协议
*设置代理对象self.scrollView.delegate = self;
*实现协议方法
2).代理监听scrollView的拖拽事件
3).用代理实现缩放
*成为UIScrollView的代理()
*设置缩放对象(经过viewForZoomingInScrollView方法)
*设置缩放为范围(maximumZoomScale、minimumZoomScale)
1.图片轮播(能够采用两种维度去分解工做:功能、MVC)
1.UI(分析UI如何实现storyboard、代码建立)
1.scrollView(有两件事)
2.图片(代码增长到scrollView)
3.UIPageControl(需设置总页数、当前页码)
2.业务
1.拖动(整页切换,UIScrollView的宽度为一页)
2.页码设置(当前是第几页)
3.自动滚动
4.优化(timer的机制:触摸式移除,放开时再加进来)
2.智能猜图扩展(代理)
1.UIAlertView的使用以及常见代理使用 (介绍UIWindow的level模式,level最高是UIWindowLevelStatusBar,能够覆盖status bar)
2.UIActionSheetView简单使用以及代理使用(强调了危险操做,标红显示按钮)
3.应用管理扩展(定义协议并使用)
1.定义协议(三步)
*定义protocol(两种optional[代理对象可不实现]、required[代理对象必须实现])
*增长代理属性(weak) @property (weak, nonatomic) id<LFAppInfoViewDelegate> delegate;
*给代理发消息,调用代理的方法(须要判断代理对象是否实现了该方法,不判断调用后(编译时不会)会报错)
注意:定义协议的名称命名[类名+Delegate]、协议方法的命名规范[方法名称须要去掉前缀,而且将本身做为参数]
2.使用代理(三步)
*声明协议
*设置代理对象
*实现协议方法(本例是在代理对象[控制器] 添加一个UILabel)
iOS通知
一个完整的通知通常包含3个属性:
- (NSString *)name; // 通知的名称
- (id)object; // 通知发布者(是谁要发布通知)
- (NSDictionary *)userInfo; // 一些额外的信息(通知发布者传递给通知接收者的信息内容)
初始化一个通知(NSNotification)对象
+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject;
+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
- (instancetype)initWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo;
发布通知
通知中心(NSNotificationCenter)提供了相应的方法来帮助发布通知
- (void)postNotification:(NSNotification *)notification;
发布一个notification通知,可在notification对象中设置通知的名称、通知发布者、额外信息等
- (void)postNotificationName:(NSString *)aName object:(id)anObject;
发布一个名称为aName的通知,anObject为这个通知的发布者
- (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
发布一个名称为aName的通知,anObject为这个通知的发布者,aUserInfo为额外信息
注册通知监听器
通知中心(NSNotificationCenter)提供了方法来注册一个监听通知的监听器(Observer)
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;
observer:监听器,即谁要接收这个通知
aSelector:收到通知后,回调监听器的这个方法,而且把通知对象当作参数传入
aName:通知的名称。若是为nil,那么不管通知的名称是什么,监听器都能收到这个通知
anObject:通知发布者。若是为anObject和aName都为nil,监听器都收到全部的通知
- (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block;
name:通知的名称
obj:通知发布者
block:收到对应的通知时,会回调这个block
queue:决定了block在哪一个操做队列中执行,若是传nil,默认在当前操做队列中同步执行
取消注册通知监听器
通知中心不会保留(retain)监听器对象,在通知中心注册过的对象,必须在该对象释放前取消注册。不然,当相应的通知再次出现时,通知中心仍然会向该监听器发送消息。由于相应的监听器对象已经被释放了,因此可能会致使应用崩溃
通知中心提供了相应的方法来取消注册监听器
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject;
通常在监听器销毁以前取消注册(如在监听器中加入下列代码):
- (void)dealloc {
//[super dealloc]; 非ARC中须要调用此句
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
通知和代理的选择
共同点
利用通知和代理都能完成对象之间的通讯
(好比A对象告诉D对象发生了什么事情, A对象传递数据给D对象)
不一样点
代理 : 一对一关系(1个对象只能告诉另1个对象发生了什么事情)
通知 : 多对多关系(1个对象能告诉N个对象发生了什么事情, 1个对象能得知N个对象发生了什么事情)
键盘通知
咱们常常须要在键盘弹出或者隐藏的时候作一些特定的操做,所以须要监听键盘的状态
键盘状态改变的时候,系统会发出一些特定的通知
UIKeyboardWillShowNotification // 键盘即将显示
UIKeyboardDidShowNotification // 键盘显示完毕
UIKeyboardWillHideNotification // 键盘即将隐藏
UIKeyboardDidHideNotification // 键盘隐藏完毕
UIKeyboardWillChangeFrameNotification // 键盘的位置尺寸即将发生改变
UIKeyboardDidChangeFrameNotification // 键盘的位置尺寸改变完毕
系统发出键盘通知时,会附带一下跟键盘有关的额外信息(字典),字典常见的key以下:
UIKeyboardFrameBeginUserInfoKey // 键盘刚开始的frame
UIKeyboardFrameEndUserInfoKey // 键盘最终的frame(动画执行完毕后)
UIKeyboardAnimationDurationUserInfoKey // 键盘动画的时间
UIKeyboardAnimationCurveUserInfoKey // 键盘动画的执行节奏(快慢)
UIDevice通知
UIDevice类提供了一个单粒对象,它表明着设备,经过它能够得到一些设备相关的信息,好比电池电量值(batteryLevel)、电池状态(batteryState)、设备的类型(model,好比iPod、iPhone等)、设备的系统(systemVersion)
经过[UIDevice currentDevice]能够获取这个单粒对象
UIDevice对象会不间断地发布一些通知,下列是UIDevice对象所发布通知的名称常量:
UIDeviceOrientationDidChangeNotification // 设备旋转
UIDeviceBatteryStateDidChangeNotification // 电池状态改变
UIDeviceBatteryLevelDidChangeNotification // 电池电量改变
UIDeviceProximityStateDidChangeNotification // 近距离传感器(好比设备贴近了使用者的脸部)
建立头部视图
//建立头部视图
+(instancetype)headerViewWithTableView:(UITableView *)tableView{
return [[self alloc]initWithTableView:tableView];
}
-(instancetype)initWithTableView:(UITableView *)tableView{
static NSString * indentifier = @"header";
GYLHeaderView * headerView = [tableView dequeueReusableCellWithIdentifier:indentifier];
if (headerView == nil) {
headerView = [[GYLHeaderView alloc]initWithReuseIdentifier:indentifier];
}
return headerView;
}
//但凡在init中得到的frame都是 0
-(id)initWithReuseIdentifier:(NSString *)reuseIdentifier
{
if(self =[super initWithReuseIdentifier:reuseIdentifier])
{
//1.添加子控件
//1.1添加按钮
UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
//添加按钮的点击事件
[btn addTarget:self action:@selector(btnOnClick:) forControlEvents:UIControlEventTouchDragInside];
//设置按钮的背景图片
[btn setBackgroundImage:[UIImage imageNamed:@"buddy_header_bg"] forState:UIControlStateNormal];
[btn setBackgroundImage:[UIImage imageNamed:@"buddy_header_bg_highlighted"] forState:UIControlStateHighlighted];
//设置按钮的上的尖尖的图片
[btn setImage:[UIImage imageNamed:@"buddy_header_arrow"] forState:UIControlStateNormal];
//设置按钮的内容左对齐
btn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
//2.设置按钮的内边距,这样按钮的内容距离左边就有必定的距离
btn.contentEdgeInsets = UIEdgeInsetsMake(0, 20, 0, 0);
//3.设置按钮色标题和图片之间的距离
btn.titleEdgeInsets = UIEdgeInsetsMake(0, 20, 0, 0);
// btn.imageEdgeInsets 这是设置图片的距离
[btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
//设置btn中的图片不填充整个imageView
btn.imageView.contentMode = UIViewContentModeCenter;
//超出的部分不要进行剪切
// btn.imageView.clipsToBounds = NO;
btn.imageView.layer.masksToBounds = NO;
[self addSubview:btn];
self.btn = btn ;
//1.2添加label
UILabel * label = [[UILabel alloc]init];
// label.backgroundColor = [UIColor grayColor];
//设置文本右对齐
label.textAlignment = NSTextAlignmentRight;
label.textColor = [UIColor grayColor];
[self addSubview:label];
self.label = label;
}
return self;
}
// 该方法在控件的frame被改变的时候就会调用
// 该方法通常用于调整子控件的位置
- (void)layoutSubviews
{
#warning 切记重写layoutSubviews方法必定要调用父类的layoutSubviews
[super layoutSubviews];
// 1.设置按钮的frame
self.btn.frame = self.bounds;
// 2.设置label的frame
CGFloat padding = 20;// 间隙
CGFloat labelY = 0;
CGFloat labelH = self.bounds.size.height;
CGFloat labelW = 150;
CGFloat labelX = self.bounds.size.width - padding - labelW;
self.label.frame = CGRectMake(labelX, labelY, labelW, labelH);
}
- (void)btnOnClick:(UIButton *)btn
{
NSLog(@"按钮被点击了");
// 1.修改组模型的isOpen属性
// 修改模型数据数据
self.qqGroup.open = !self.qqGroup.isOpen;
// 2. 刷新表格(通知代理)
if ([self.delegate respondsToSelector:@selector(headerViewDidClickHeaderView:)]) {
[self.delegate headerViewDidClickHeaderView:self];
}
// 3.修改btn上图片,让图片旋转
// self.btn.imageView.transform = CGAffineTransformMakeRotation(M_PI_2);
// NSLog(@"%p %@", self, self.qqGroup.name);
}
#pragma mark - 当一个控件被添加到其它视图上的时候会调用如下方法
// 已经被添加到父视图上的时候会调用
- (void)didMoveToSuperview
{
// 在这个方法中就快要拿到最新的被添加到tableview上的头部视图修改它的图片
if (self.qqGroup.isOpen) {
self.btn.imageView.transform = CGAffineTransformMakeRotation(M_PI_2);
}
}
// 即将被添加到父视图上的时候会调用
- (void)willMoveToSuperview:(UIView *)newSuperview
{
// NSLog(@"willMoveToSuperview");
}
- (void)setQqGroup:(GYLGroupModel *)qqGroup
{
_qqGroup = qqGroup;
// 1.设置按钮上的文字
[self.btn setTitle:_qqGroup.name forState:UIControlStateNormal];
// 2.设置在线人数
self.label.text = [NSString stringWithFormat:@"%@/%d", _qqGroup.online, (int)_qqGroup.friends.count];
}
UIApplication的特征
UIApplication对象是应用程序的象征
每个应用都有本身的UIApplication对象,并且是单例的
经过[UIApplication sharedApplication]能够得到这个单例对象
一个iOS程序启动后建立的第一个对象就是UIApplication对象
利用UIApplication对象,能进行一些应用级别的操做
…
设置应用程序图标右上角的红色提醒数字
@property(nonatomic) NSInteger applicationIconBadgeNumber;
设置联网指示器的可见性
@property(nonatomic,getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible;
从iOS7开始,系统提供了2种管理状态栏的方式
经过UIViewController管理(每个UIViewController均可以拥有本身不一样的状态栏)
经过UIApplication管理(一个应用程序的状态栏都由它统一管理)
在iOS7中,默认状况下,状态栏都是由UIViewController管理的,UIViewController实现下列方法就能够轻松管理状态栏的可见性和样式
状态栏的样式
- (UIStatusBarStyle)preferredStatusBarStyle;
状态栏的可见性
(BOOL)prefersStatusBarHidden;
…
UIApplication有个功能十分强大的openURL:方法
- (BOOL)openURL:(NSURL*)url;
openURL:方法的部分功能有
打电话
UIApplication *app = [UIApplication sharedApplication];
[app openURL:[NSURL URLWithString:@"tel://10086"]];
发短信
[app openURL:[NSURL URLWithString:@"sms://10086"]];
发邮件
[app openURL:[NSURL URLWithString:@"mailto://12345@qq.com"]];
打开一个网页资源
[app openURL:[NSURL URLWithString:@"http://blog.csdn.net/guoyule2010"]];
打开其余app程序
…
UIPickerView的常见属性
一.UIPickerView
1.UIPickerView的常见属性
// 数据源(用来告诉UIPickerView有多少列多少行)
@property(nonatomic,assign) id<UIPickerViewDataSource> dataSource;
// 代理(用来告诉UIPickerView每1列的每1行显示什么内容,监听UIPickerView的选择)
@property(nonatomic,assign) id<UIPickerViewDelegate> delegate;
// 是否要显示选中的指示器
@property(nonatomic) BOOL showsSelectionIndicator;
// 一共有多少列
@property(nonatomic,readonly) NSInteger numberOfComponents;
2.UIPickerView的常见方法
// 从新刷新全部列
- (void)reloadAllComponents;
// 从新刷新第component列
- (void)reloadComponent:(NSInteger)component;
// 主动选中第component列的第row行
- (void)selectRow:(NSInteger)row inComponent:(NSInteger)component animated:(BOOL)animated;
// 得到第component列的当前选中的行号
- (NSInteger)selectedRowInComponent:(NSInteger)component;
3.数据源方法(UIPickerViewDataSource)
// 一共有多少列
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView;
// 第component列一共有多少行
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component;
4.代理方法(UIPickerViewDelegate)
// 第component列的宽度是多少
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component;
// 第component列的行高是多少
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component;
// 第component列第row行显示什么文字
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component;
// 第component列第row行显示怎样的view(内容)
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view;
// 选中了pickerView的第component列第row行
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component;
二.UIDatePicker
1.常见属性
// datePicker的显示模式
@property (nonatomic) UIDatePickerMode datePickerMode;
// 显示的区域语言
@property (nonatomic, retain) NSLocale *locale;
2.监听UIDatePicker的选择
* 由于UIDatePicker继承自UIControl,因此经过addTarget:...监听
三.程序启动的完整过程
1.main函数
2.UIApplicationMain
* 建立UIApplication对象
* 建立UIApplication的delegate对象
3.delegate对象开始处理(监听)系统事件(没有storyboard)
* 程序启动完毕的时候, 就会调用代理的application:didFinishLaunchingWithOptions:方法
* 在application:didFinishLaunchingWithOptions:中建立UIWindow
* 建立和设置UIWindow的rootViewController
* 显示窗口
3.根据Info.plist得到最主要storyboard的文件名,加载最主要的storyboard(有storyboard)
* 建立UIWindow
* 建立和设置UIWindow的rootViewController
* 显示窗口
UIWindow
1.UIWindow
* 主窗口的概念
* 新建UIWindow
2.UIViewController
* 控制器的建立方式
* 控制器view的建立方式
* view的懒加载
* loadView、viewDidLoad、viewDidUnload、didReceiveMemoryWarning
3.UINavigationController
* 经过“设置”演示基本用途
* 经过非storyboard方式,感觉导航的做用
1> 建立导航控制器
2> 设置UIWindow的根控制器
3> push 1个、2个、3个 子控制器
4> 解释push的原理(栈、导航控制器的管理过程)
5> 栈底、栈顶控制器的概念
6> 如何设置导航栏上面的内容、返回文字的设置
7> pop的方法使用
8> push和addChild、viewControllers和childViewController的关系
* 经过storyboard方式,感觉导航的做用
4.UIViewController的生命周期方法、AppDelegate的生命周期方法
如何建立一个控制器
控制器常见的建立方式有如下几种
经过storyboard建立
直接建立
控制器常见的建立方式有如下几种
经过storyboard建立
直接建立
GYLViewController *nj = [[GYLViewController alloc] init];
指定xib文件来建立
GYLViewController *nj = [[GYLViewController alloc] initWithNibName:@”GYLViewController" bundle:nil];
先加载storyboard文件(Test是storyboard的文件名)
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Test" bundle:nil];
接着初始化storyboard中的控制器
初始化“初始控制器”(箭头所指的控制器)
GYLViewController *nj = [storyboard instantiateInitialViewController];
经过一个标识初始化对应的控制器
GYLViewController *nj = [storyboard instantiateViewControllerWithIdentifier:@”nj"];
控制器view的延迟加载
控制器的view是延迟加载的:用到时再加载
能够用isViewLoaded方法判断一个UIViewController的view是否已经被加载
控制器的view加载完毕就会调用viewDidLoad方法
多控制器
一个iOS的app不多只由一个控制器组成,除非这个app极其简单
当app中有多个控制器的时候,咱们就须要对这些控制器进行管理
有多个view时,能够用一个大的view去管理1个或者多个小view
控制器也是如此,用1个控制器去管理其余多个控制器
好比,用一个控制器A去管理3个控制器B、C、D
控制器A被称为控制器B、C、D的“父控制器”
控制器B、C、D的被称为控制器A的“子控制器”
为了便于管理控制器,iOS提供了2个比较特殊的控制器
UINavigationController
UITabBarController
UINavigationController的简单使用
UINavigationController以栈的形式保存子控制器
@property(nonatomic,copy) NSArray *viewControllers;
@property(nonatomic,readonly) NSArray *childViewControllers;
使用push方法能将某个控制器压入栈
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated;
使用pop方法能够移除控制器
将栈顶的控制器移除
- (UIViewController *)popViewControllerAnimated:(BOOL)animated;
回到指定的子控制器
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated;
回到根控制器(栈底控制器)
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated;
如何修改导航栏的内容
导航栏的内容由栈顶控制器的navigationItem属性决定
UINavigationItem有如下属性影响着导航栏的内容
左上角的返回按钮
@property(nonatomic,retain) UIBarButtonItem *backBarButtonItem;
中间的标题视图
@property(nonatomic,retain) UIView *titleView;
中间的标题文字
@property(nonatomic,copy) NSString *title;
左上角的视图
@property(nonatomic,retain) UIBarButtonItem *leftBarButtonItem;
UIBarButtonItem *rightBarButtonItem 右上角的视图
@property(nonatomic,retain) UIBarButtonItem *rightBarButtonItem;
Segue的属性
每个Segue对象,都有3个属性
惟一标识
@property (nonatomic, readonly) NSString *identifier;
来源控制器
@property (nonatomic, readonly) id sourceViewController;
目标控制器
@property (nonatomic, readonly) id destinationViewController;
Segue的类型
根据Segue的执行(跳转)时刻,Segue能够分为2大类型
自动型:点击某个控件后(好比按钮),自动执行Segue,自动完成界面跳转
手动型:须要经过写代码手动执行Segue,才能完成界面跳转
自动型Segue
按住Control键,直接从控件拖线到目标控制器
点击“登陆”按钮后,就会自动跳转到右边的控制器
若是点击某个控件后,不须要作任何判断,必定要跳转到下一个界面,建议使用“自动型Segue”
手动型Segue
按住Control键,历来源控制器拖线到目标控制器
手动型的Segue须要设置一个标识(如右图)
在恰当的时刻,使用perform方法执行对应的Segue
[self performSegueWithIdentifier:@"login2contacts" sender:nil];
// Segue必须由来源控制器来执行,也就是说,这个perform方法必须由来源控制器来调用
若是点击某个控件后,须要作一些判断,也就是说:知足必定条件后才跳转到下一个界面,建议使用“手动型Segue”
performSegueWithIdentifier:sender:
利用performSegueWithIdentifier:方法能够执行某个Segue,完成界面跳转
接下来研究performSegueWithIdentifier:sender:方法的完整执行过程
[self performSegueWithIdentifier:@“login2contacts” sender:nil];
// 这个self是来源控制器
根据identifier去storyboard中找到对应的线,新建UIStoryboardSegue对象
设置Segue对象的sourceViewController(来源控制器)
新建而且设置Segue对象的destinationViewController(目标控制器)
performSegueWithIdentifier:sender:
调用sourceViewController的下面方法,作一些跳转前的准备工做而且传入建立好的Segue对象
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
// 这个sender是当初performSegueWithIdentifier:sender:中传入的sender
调用Segue对象的- (void)perform;方法开始执行界面跳转操做
取得sourceViewController所在的UINavigationController
调用UINavigationController的push方法将destinationViewController压入栈中,完成跳转
Sender参数的传递
[self performSegueWithIdentifier:@“login2contacts” sender:@“jack”];
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
控制器的数据传递
控制器之间的数据传递主要有2种状况:顺传和逆传
顺传
控制器的跳转方向: A C
数据的传递方向 : A C
数据的传递方式 : 在A的prepareForSegue:sender:方法中根据segue参数取得destinationViewController, 也就是控制器C, 直接给控制器C传递数据
(要在C的viewDidLoad方法中取得数据,来赋值给界面上的UI控件)
逆传
控制器的跳转方向: A C
数据的传递方向 : C A
数据的传递方式 : 让A成为C的代理, 在C中调用A的代理方法,经过代理方法的参数传递数据给A
面试
1. 字符串
若是IDE没有代码自动补全功能,因此你应该记住下面的这些方法。
toCharArray() // 得到字符串对应的char数组
Arrays.sort() // 数组[排序](/tag/%e6%8e%92%e5%ba%8f "View all posts in 排序")
Arrays.toString(char[] a) // 数组转成字符串
charAt(int x) // 得到某个索引处的字符
length() // 字符串长度
length // 数组大小
2. 链表
在Java中,链表的实现很是简单,每一个节点Node都有一个值val和指向下个节点的连接next。
class Node {
int val;
Node next;
Node(int x) {
val = x;
next = null;
}
}
栈:
class Stack{
Node top;
public Node peek(){
if(top != null){
return top;
}
return null;
}
public Node pop(){
if(top == null){
return null;
}else{
Node temp = new Node(top.val);
top = top.next;
return temp;
}
}
public void push(Node n){
if(n != null){
n.next = top;
top = n;
}
}
}
队列:
class Queue{
Node first, last;
public void enqueue(Node n){
if(first == null){
first = n;
last = first;
}else{
last.next = n;
last = n;
}
}
public Node dequeue(){
if(first == null){
return null;
}else{
Node temp = new Node(first.val);
first = first.next;
return temp;
}
}
}
3. 树
这里的树一般是指二叉树,每一个节点都包含一个左孩子节点和右孩子节点,像下面这样:
class TreeNode{
int value;
TreeNode left;
TreeNode right;
}
下面是与树相关的一些概念:
译者注:完美二叉树也隐约称为彻底二叉树。完美二叉树的一个例子是一我的在给定深度的祖先图,由于每一个人都必定有两个生父母。彻底二叉树能够当作是能够有若干额外向左靠的叶子节点的完美二叉树。疑问:完美二叉树和满二叉树的区别?(参考:http://xlinux.nist.gov/dads/HTML/perfectBinaryTree.html)
4. 图
图相关的问题主要集中在深度优先搜索(depth first search)和广度优先搜索(breath first search)。
下面是一个简单的图广度优先搜索的实现。
1) 定义GraphNode
class GraphNode{
int val;
GraphNode next;
GraphNode[] neighbors;
boolean visited;
GraphNode(int x) {
val = x;
}
GraphNode(int x, GraphNode[] n){
val = x;
neighbors = n;
}
public String toString(){
return "value: "+ this.val;
}
}
2) 定义一个队列Queue
class Queue{
GraphNode first, last;
public void enqueue(GraphNode n){
if(first == null){
first = n;
last = first;
}else{
last.next = n;
last = n;
}
}
public GraphNode dequeue(){
if(first == null){
return null;
}else{
GraphNode temp = new GraphNode(first.val, first.neighbors);
first = first.next;
return temp;
}
}
}
3) 用队列Queue实现广度优先搜索
public class GraphTest {
public static void main(String[] args) {
GraphNode n1 = new GraphNode(1);
GraphNode n2 = new GraphNode(2);
GraphNode n3 = new GraphNode(3);
GraphNode n4 = new GraphNode(4);
GraphNode n5 = new GraphNode(5);
n1.neighbors = new GraphNode[]{n2,n3,n5};
n2.neighbors = new GraphNode[]{n1,n4};
n3.neighbors = new GraphNode[]{n1,n4,n5};
n4.neighbors = new GraphNode[]{n2,n3,n5};
n5.neighbors = new GraphNode[]{n1,n3,n4};
breathFirstSearch(n1, 5);
}
public static void breathFirstSearch(GraphNode root, int x){
if(root.val == x)
System.out.println("find in root");
Queue queue = new Queue();
root.visited = true;
queue.enqueue(root);
while(queue.first != null){
GraphNode c = (GraphNode) queue.dequeue();
for(GraphNode n: c.neighbors){
if(!n.visited){
System.out.print(n + " ");
n.visited = true;
if(n.val == x)
System.out.println("Find "+n);
queue.enqueue(n);
}
}
}
}
}
Output:
value: 2 value: 3 value: 5 Find value: 5
value: 4
5. 排序
下面是不一样排序算法的时间复杂度,你能够去wiki看一下这些算法的基本思想。
Algorithm |
Average Time |
Worst Time |
Space |
冒泡排序 |
n^2 |
n^2 |
1 |
选择排序 |
n^2 |
n^2 |
1 |
Counting Sort |
n+k |
n+k |
n+k |
Insertion sort |
n^2 |
n^2 |
|
Quick sort |
n log(n) |
n^2 |
|
Merge sort |
n log(n) |
n log(n) |
depends |
另外,这里有一些实现/演示:: Counting sort、Mergesort、 Quicksort、 InsertionSort。
对程序员来讲,递归应该是一个与生俱来的思想(a built-in thought),能够经过一个简单的例子来讲明。
问题: 有n步台阶,一次只能上1步或2步,共有多少种走法。
步骤1:找到走完前n步台阶和前n-1步台阶之间的关系。
为了走完n步台阶,只有两种方法:从n-1步台阶爬1步走到或从n-2步台阶处爬2步走到。若是f(n)是爬到第n步台阶的方法数,那么f(n) = f(n-1) + f(n-2)。
步骤2: 确保开始条件是正确的。
f(0) = 0;
f(1) = 1;
public static int f(int n){
if(n <= 2) return n;
int x = f(n-1) + f(n-2);
return x;
}
递归方法的时间复杂度是n的指数级,由于有不少冗余的计算,以下:
f(5)
f(4) + f(3)
f(3) + f(2) + f(2) + f(1)
f(2) + f(1) + f(1) + f(0) + f(1) + f(0) + f(1)
f(1) + f(0) + f(1) + f(1) + f(0) + f(1) + f(0) + f(1)
直接的想法是将递归转换为迭代:
public static int f(int n) {
if (n <= 2){
return n;
}
int first = 1, second = 2;
int third = 0;
for (int i = 3; i <= n; i++) {
third = first + second;
first = second;
second = third;
}
return third;
}
对这个例子而言,迭代花费的时间更少,你可能也想看看Recursion vs Iteration。
7. 动态规划
动态规划是解决下面这些性质类问题的技术:
爬台阶问题彻底符合上面的四条性质,所以能够用动态规划法来解决。
public static int[] A = new int[100];
public static int f3(int n) {
if (n <= 2)
A[n]= n;
if(A[n] > 0)
return A[n];
else
A[n] = f3(n-1) + f3(n-2);//store results so only calculate once!
return A[n];
}
**8. 位操做
**
位操做符:
OR (|) |
AND (&) |
XOR (^) |
Left Shift (<<) |
Right Shift (>>) |
Not (~) |
1|0=1 |
1&0=0 |
1^0=1 |
0010<<2=1000 |
1100>>2=0011 |
~1=0 |
得到给定数字n的第i位:(i从0计数并从右边开始)
public static boolean getBit(int num, int i){
int result = num & (1<<i);
if(result == 0){
return false;
}else{
return true;
}
例如,得到数字10的第2位:
i=1, n=10
1<<1= 10
1010&10=10
10 is not 0, so return true;
9. 几率问题
解决几率相关的问题一般须要很好的规划了解问题(formatting the problem),这里恰好有一个这类问题的简单例子:
一个房间里有50我的,那么至少有两我的生日相同的几率是多少?(忽略闰年的事实,也就是一年365天)
计算某些事情的几率不少时候均可以转换成先计算其相对面。在这个例子里,咱们能够计算全部人生日都互不相同的几率,也就是:365/365 * 364/365 * 363/365 * … * (365-49)/365,这样至少两我的生日相同的几率就是1 – 这个值。
public static double caculateProbability(int n){
double x = 1;
for(int i=0; i<n; i++){
x *= (365.0-i)/365.0;
}
double pro = Math.round((1-x) * 100);
return pro/100;
calculateProbability(50) = 0.97
10. 排列组合
组合和排列的区别在于次序是否关键。
若是你有任何问题请在下面评论。
参考/推荐资料:
1. Binary tree
2. Introduction to Dynamic Programming
3. UTSA Dynamic Programming slides
数据存取
iOS应用数据存储的经常使用方式
XML属性列表(plist)归档
Preference(偏好设置)
NSKeyedArchiver归档(NSCoding)
SQLite3
Core Data
应用沙盒
每一个iOS应用都有本身的应用沙盒(应用沙盒就是文件系统目录),与其余文件系统隔离。应用必须待在本身的沙盒里,其余应用不能访问该沙盒
应用沙盒的文件系统目录,以下图所示(假设应用的名称叫Layer)
模拟器应用沙盒的根路径在: (guoyule是用户名, 8.1是模拟器版本)
/Users/guoyule/Library/Application Support/iPhone Simulator/8./1Applications
应用沙盒结构分析
应用程序包:(上图中的Layer)包含了全部的资源文件和可执行文件
Documents:保存应用运行时生成的须要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录
tmp:保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录
Library/Caches:保存应用运行时生成的须要持久化的数据,iTunes同步设备时不会备份该目录。通常存储体积大、不须要备份的非重要数据
Library/Preference:保存应用的全部偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录
应用沙盒目录的常见获取方式
沙盒根目录:NSString *home = NSHomeDirectory();
Documents:(2种方式)
利用沙盒根目录拼接”Documents”字符串
NSString *home = NSHomeDirectory();
NSString *documents = [home stringByAppendingPathComponent:@"Documents"];
// 不建议采用,由于新版本的操做系统可能会修改目录名
利用NSSearchPathForDirectoriesInDomains函数
// NSUserDomainMask 表明从用户文件夹下找
// YES 表明展开路径中的波浪字符“~”
NSArray *array = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, NO);
// 在iOS中,只有一个目录跟传入的参数匹配,因此这个集合里面只有一个元素
NSString *documents = [array objectAtIndex:0];
tmp:NSString *tmp = NSTemporaryDirectory();
Library/Caches:(跟Documents相似的2种方法)
利用沙盒根目录拼接”Caches”字符串
利用NSSearchPathForDirectoriesInDomains函数(将函数的第2个参数改成:NSCachesDirectory便可)
Library/Preference:经过NSUserDefaults类存取该目录下的设置信息
属性列表
属性列表是一种XML格式的文件,拓展名为plist
若是对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,就可使用writeToFile:atomically:方法直接将对象写到属性列表文件中
属性列表-归档NSDictionary
将一个NSDictionary对象归档到一个plist属性列表中
// 将数据封装成字典
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:@"郭玉乐" forKey:@"name"];
[dict setObject:@"18501956963" forKey:@"phone"];
[dict setObject:@"24" forKey:@"age"];
// 将字典持久化到Documents/stu.plist文件中
[dict writeToFile:path atomically:YES];
属性列表-恢复NSDictionary
读取属性列表,恢复NSDictionary对象
// 读取Documents/stu.plist的内容,实例化NSDictionary
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSLog(@"name:%@", [dict objectForKey:@"name"]);
NSLog(@"phone:%@", [dict objectForKey:@"phone"]);
NSLog(@"age:%@", [dict objectForKey:@"age"]);
偏好设置
不少iOS应用都支持偏好设置,好比保存用户名、密码、字体大小等设置,iOS提供了一套标准的解决方案来为应用加入偏好设置功能
每一个应用都有个NSUserDefaults实例,经过它来存取偏好设置
好比,保存用户名、字体大小、是否自动登陆
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@"guoyule" forKey:@"username"];
[defaults setFloat:18.0f forKey:@"text_size"];
[defaults setBool:YES forKey:@"auto_login"];
读取上次保存的设置
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *username = [defaults stringForKey:@"username"];
float textSize = [defaults floatForKey:@"text_size"];
BOOL autoLogin = [defaults boolForKey:@"auto_login"];
注意:UserDefaults设置数据时,不是当即写入,而是根据时间戳定时地把缓存中的数据写入本地磁盘。因此调用了set方法以后数据有可能尚未写入磁盘应用程序就终止了。出现以上问题,能够经过调用synchornize方法强制写入
[defaults synchornize];
NSKeyedArchiver
若是对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,能够直接用NSKeyedArchiver进行归档和恢复
不是全部的对象均可以直接用这种方法进行归档,只有遵照了NSCoding协议的对象才能够
NSCoding协议有2个方法:
encodeWithCoder:
每次归档对象时,都会调用这个方法。通常在这个方法里面指定如何归档对象中的每一个实例变量,可使用encodeObject:forKey:方法归档实例变量
initWithCoder:
每次从文件中恢复(解码)对象时,都会调用这个方法。通常在这个方法里面指定如何解码文件中的数据为对象的实例变量,可使用decodeObject:forKey方法解码实例变量
NSKeyedArchiver-归档NSArray
归档一个NSArray对象到Documents/array.archive
NSArray *array = [NSArray arrayWithObjects:@”a”,@”b”,nil];
[NSKeyedArchiver archiveRootObject:array toFile:path];
恢复(解码)NSArray对象
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSKeyedArchiver-归档Person对象(Person.h)
@interface Person : NSObject<NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) float height;
@end
NSKeyedArchiver-归档Person对象(Person.m)
@implementation Person
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeInt:self.age forKey:@"age"];
[encoder encodeFloat:self.height forKey:@"height"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self.name = [decoder decodeObjectForKey:@"name"];
self.age = [decoder decodeIntForKey:@"age"];
self.height = [decoder decodeFloatForKey:@"height"];
return self;
}
- (void)dealloc {
[super dealloc];
[_name release];
}
@end
NSKeyedArchiver-归档Person对象(编码和解码)
归档(编码)
Person *person = [[[Person alloc] init] autorelease];
person.name = @"GUOYULE";
person.age = 24;
person.height = 1.78f;
[NSKeyedArchiver archiveRootObject:person toFile:path];
恢复(解码)
Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSKeyedArchiver-归档对象的注意
若是父类也遵照了NSCoding协议,请注意:
应该在encodeWithCoder:方法中加上一句
[super encodeWithCode:encode];
确保继承的实例变量也能被编码,即也能被归档
应该在initWithCoder:方法中加上一句
self = [super initWithCoder:decoder];
确保继承的实例变量也能被解码,即也能被恢复
NSData
使用archiveRootObject:toFile:方法能够将一个对象直接写入到一个文件中,但有时候可能想将多个对象写入到同一个文件中,那么就要使用NSData来进行归档对象
NSData能够为一些数据提供临时存储空间,以便随后写入文件,或者存放从磁盘读取的文件内容。可使用[NSMutableData data]建立可变数据空间
NSData-归档2个Person对象到同一文件中
归档(编码)
// 新建一块可变数据区
NSMutableData *data = [NSMutableData data];
// 将数据区链接到一个NSKeyedArchiver对象
NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];
// 开始存档对象,存档的数据都会存储到NSMutableData中
[archiver encodeObject:person1 forKey:@"person1"];
[archiver encodeObject:person2 forKey:@"person2"];
// 存档完毕(必定要调用这个方法)
[archiver finishEncoding];
// 将存档的数据写入文件
[data writeToFile:path atomically:YES];
NSData-从同一文件中恢复2个Person对象
恢复(解码)
// 从文件中读取数据
NSData *data = [NSData dataWithContentsOfFile:path];
// 根据数据,解析成一个NSKeyedUnarchiver对象
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
Person *person1 = [unarchiver decodeObjectForKey:@"person1"];
Person *person2 = [unarchiver decodeObjectForKey:@"person2"];
// 恢复完毕
[unarchiver finishDecoding];
利用归档实现深复制
好比对一个Person对象进行深复制
// 临时存储person1的数据
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person1];
// 解析data,生成一个新的Person对象
Student *person2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
// 分别打印内存地址
NSLog(@"person1:0x%x", person1); // person1:0x7177a60
NSLog(@"person2:0x%x", person2); // person2:0x7177cf0
SQLite3
SQLite3是一款开源的嵌入式关系型数据库,可移植性好、易使用、内存开销小
SQLite3是无类型的,意味着你能够保存任何类型的数据到任意表的任意字段中。好比下列的创表语句是合法的:
create table t_person(name, age);
为了保证可读性,建议仍是把字段类型加上:
create table t_person(name text, age integer);
SQLite3经常使用的5种数据类型:text、integer、float、boolean、blob
在iOS中使用SQLite3,首先要添加库文件libsqlite3.dylib和导入主头文件
建立、打开、关闭数据库
建立或打开数据库
// path为:~/Documents/person.db
sqlite3 *db;
int result = sqlite3_open([path UTF8String], &db);
代码解析:
sqlite3_open()将根据文件路径打开数据库,若是不存在,则会建立一个新的数据库。若是result等于常量SQLITE_OK,则表示成功打开数据库
sqlite3 *db:一个打开的数据库实例
数据库文件的路径必须以C字符串(而非NSString)传入
关闭数据库:sqlite3_close(db);
执行不返回数据的SQL语句
执行创表语句
char *errorMsg; // 用来存储错误信息
char *sql = "create table if not exists t_person(id integer primary key autoincrement, name text, age integer);";
int result = sqlite3_exec(db, sql, NULL, NULL, &errorMsg);
代码解析:
sqlite3_exec()能够执行任何SQL语句,好比创表、更新、插入和删除操做。可是通常不用它执行查询语句,由于它不会返回查询到的数据
sqlite3_exec()还能够执行的语句:
开启事务:begin transaction;
回滚事务:rollback;
提交事务:commit;
带占位符插入数据
char *sql = "insert into t_person(name, age) values(?, ?);";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {
sqlite3_bind_text(stmt, 1, "母鸡", -1, NULL);
sqlite3_bind_int(stmt, 2, 27);
}
if (sqlite3_step(stmt) != SQLITE_DONE) {
NSLog(@"插入数据错误");
}
sqlite3_finalize(stmt);
代码解析:
sqlite3_prepare_v2()返回值等于SQLITE_OK,说明SQL语句已经准备成功,没有语法问题
sqlite3_bind_text():大部分绑定函数都只有3个参数
第1个参数是sqlite3_stmt *类型
第2个参数指占位符的位置,第一个占位符的位置是1,不是0
第3个参数指占位符要绑定的值
第4个参数指在第3个参数中所传递数据的长度,对于C字符串,能够传递-1代替字符串的长度
第5个参数是一个可选的函数回调,通常用于在语句执行后完成内存清理工做
sqlite_step():执行SQL语句,返回SQLITE_DONE表明成功执行完毕
sqlite_finalize():销毁sqlite3_stmt *对象
查询数据
char *sql = "select id,name,age from t_person;";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {
while (sqlite3_step(stmt) == SQLITE_ROW) {
int _id = sqlite3_column_int(stmt, 0);
char *_name = (char *)sqlite3_column_text(stmt, 1);
NSString *name = [NSString stringWithUTF8String:_name];
int _age = sqlite3_column_int(stmt, 2);
NSLog(@"id=%i, name=%@, age=%i", _id, name, _age);
}
}
sqlite3_finalize(stmt);
代码解析
sqlite3_step()返回SQLITE_ROW表明遍历到一条新记录
sqlite3_column_*()用于获取每一个字段对应的值,第2个参数是字段的索引,从0开始
NSManagedObject
经过Core Data从数据库取出的对象,默认状况下都是NSManagedObject对象
NSManagedObject的工做模式有点相似于NSDictionary对象,经过键-值对来存取全部的实体属性
setValue:forKey: 存储属性值(属性名为key)
valueForKey: 获取属性值(属性名为key)
搭建Core Data上下文环境
从应用程序包中加载模型文件
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
传入模型,初始化NSPersistentStoreCoordinator
NSPersistentStoreCoordinator *psc = [[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model] autorelease];
构建SQLite文件路径
NSString *docs = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSURL *url = [NSURL fileURLWithPath:[docs stringByAppendingPathComponent:@"person.data"]];
添加持久化存储库,这里使用SQLite做为存储库
NSError *error = nil;
NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];
if (store == nil) { // 直接抛异常
[NSException raise:@"添加数据库错误" format:@"%@", [error localizedDescription]];
}
初始化上下文,设置persistentStoreCoordinator属性
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
context.persistentStoreCoordinator = psc;
// 用完以后,仍是要[context release];
添加数据
传入上下文,建立一个Person实体对象
NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
设置简单属性
[person setValue:@"guoyule" forKey:@"name"];
[person setValue:[NSNumber numberWithInt:24] forKey:@"age"];
传入上下文,建立一个Card实体对象
NSManagedObject *card = [NSEntityDescription insertNewObjectForEntityForName:@"Card" inManagedObjectContext:context];
[card setValue:@"4414241933432" forKey:@"no"];
设置Person和Card之间的关联关系
[person setValue:card forKey:@"card"];
利用上下文对象,将数据同步到持久化存储库
NSError *error = nil;
BOOL success = [context save:&error];
if (!success) {
[NSException raise:@"访问数据库错误" format:@"%@", [error localizedDescription]];
}
// 若是是想作更新操做:只要在更改了实体对象的属性后调用[context save:&error],就能将更改的数据同步到数据库
查询数据
初始化一个查询请求
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
设置要查询的实体
NSEntityDescription *desc = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];
设置排序(按照age降序)
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];
request.sortDescriptors = [NSArray arrayWithObject:sort];
设置条件过滤(name like '%guoyule-1%')
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like %@", @"*guoyule-1*"];
request.predicate = predicate;
执行请求
NSError *error = nil;
NSArray *objs = [context executeFetchRequest:request error:&error];
if (error) {
[NSException raise:@"查询错误" format:@"%@", [error localizedDescription]];
}
遍历数据
for (NSManagedObject *obj in objs) {
NSLog(@"name=%@", [obj valueForKey:@"name"]
}
删除数据
传入须要删除的实体对象
[context deleteObject:managedObject];
将结果同步到数据库
NSError *error = nil;
[context save:&error];
if (error) {
[NSException raise:@"删除错误" format:@"%@", [error localizedDescription]];
}
Core Data的延迟加载
Core Data不会根据实体中的关联关系当即获取相应的关联对象
好比经过Core Data取出Person实体时,并不会当即查询相关联的Card实体;当应用真的须要使用Card时,才会查询数据库,加载Card实体的信息
建立NSManagedObject的子类
默认状况下,利用Core Data取出的实体都是NSManagedObject类型的,可以利用键-值对来存取数据
可是通常状况下,实体在存取数据的基础上,有时还须要添加一些业务方法来完成一些其余任务,那么就必须建立NSManagedObject的子类
建立NSManagedObject的子类
那么生成一个Person实体对象就应该这样写
Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
person.name = @"GUOYULE";
person.age = [NSNumber numberWithInt:24];
Card *card = [NSEntityDescription insertNewObjectForEntityForName:@”Card" inManagedObjectContext:context];
card.no = @”4414245465656";
person.card = card;
数据存储代码
- (IBAction)savebtn:(id)sender {
// GYLPerson * p = [[GYLPerson alloc]init];
// p.name = @"guoyule";
// p.age = 24;
// p.hight = 178.0f;
GYLStudent * stu = [[GYLStudent alloc]init];
stu.name = @"guoyule";
stu.age = 24;
stu.hight = 178.0f;
stu.email = @"guoyulehit@icloud.com";
//2.获取文件路径
NSString * docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory , NSUserDomainMask , YES) lastObject];
//尾加文件名
NSString * path = [docPath stringByAppendingString:@"guoyule.arc"];
NSLog(@"path = %@",path);
// 3.将本身定义的对象保存到文件中
[NSKeyedArchiver archiveRootObject:stu toFile:path];
}
- (IBAction)readBtn:(id)sender {
NSString * docPth = [NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory,NSUserDomainMask,YES) lastObject];
NSString * path = [docPth stringByAppendingString:@"guoyule.arc"];
// 2.从文件中读取对象
GYLStudent * guo = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSLog(@"%@,%d,%.1f,%@",guo.name,guo.age,guo.hight,guo.email);
}
偏好设置
//
// ViewController.m
// 偏好设置
//
// Created by GuoYule on 15/3/10.
// Copyright (c) 2015年 GuoYule. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()
- (IBAction)saveBtn:(id)sender;
- (IBAction)readBtn:(id)sender;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)saveBtn:(id)sender {
// 偏好设置是专门用来保存应用程序的配置的信息的,通常状况下不要在偏好设置中保存其余的数据
// 若是利用系统的偏好设置来保存数据,默认就是存储在Preferences文件夹下面的
// 偏好设置会将全部的数据保存到同一个文件夹中
// 获取默认的NSUserDefaults
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
// 保存数据(若是设置数据以后没有同步, 会在未来某一时间点自动将数据保存到Preferences文件夹下面)
[defaults setObject:@"guoyule" forKey:@"name"];
[defaults setFloat:178.0 forKey:@"hight"];
[defaults setInteger:24 forKey:@"age"];
// 让NSUserDefaults立刻保存
[defaults synchronize];
}
- (IBAction)readBtn:(id)sender {
NSUserDefaults * de = [NSUserDefaults standardUserDefaults];
NSLog(@"name = %@,hight = %f,age = %ld",[de objectForKey:@"name"],[de floatForKey:@"hight"],(long)[de integerForKey:@"age"]);
}
@end
modal的注意点
[self dismissViewControllerAnimated:YES completion:^{
NSLog(@"OK!!!");
}];
/*
若是控制器之间的关系比较紧密通常用 UINavigationController
若是控制器之间的关系不是很紧密能够用Modal
*/
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(@"prepareForSegue");
// 1.取出目标控制器
UINavigationController *nav = segue.destinationViewController;
NJTwoViewController *two = ( NJTwoViewController *)nav.topViewController; // 获取导航控制器栈顶的控制器
// 2.给目标控制器传递数据
two.name = @"lnj";
}
UITabBarController
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 1.建立window
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
// 2.给window设置根控制器
UITabBarController * tab = [[UITabBarController alloc]init];
// 2.设置UITabBarController为winow的根控制器
self.window.rootViewController = tab;
//建立并添加自子控制器
UIViewController * vc1 = [[UIViewController alloc]init];
vc1.tabBarItem.title = @"消息";
vc1.tabBarItem.image = [UIImage imageNamed:@"tab_recent_nor"];
vc1.tabBarItem.badgeValue = @"111";
vc1.view.backgroundColor = [UIColor blackColor];
UIViewController * vc2 = [[UIViewController alloc]init];
vc2.tabBarItem.title = @"联系人";
vc2.tabBarItem.image = [UIImage imageNamed:@"tab_buddy_nor"];
// vc2.tabBarItem.badgeValue = @"111";
vc2.view.backgroundColor = [UIColor blueColor];
UIViewController * vc3 = [[UIViewController alloc]init];
vc3.tabBarItem.title = @"空间";
vc3.tabBarItem.image = [UIImage imageNamed:@"tab_qworld_nor"];
vc3.tabBarItem.badgeValue = @"11";
vc3.view.backgroundColor = [UIColor greenColor];
UIViewController * vc4 = [[UIViewController alloc]init];
vc4.tabBarItem.title = @"设置";
vc4.tabBarItem.image = [UIImage imageNamed:@"tab_me_nor"];
// vc4.tabBarItem.badgeValue = @"111";
vc4.view.backgroundColor = [UIColor grayColor];
[tab addChildViewController:vc1];
[tab addChildViewController:vc2];
[tab addChildViewController:vc3];
[tab addChildViewController:vc4];
[self.window makeKeyAndVisible];
return YES;
}
tableView的刷新
1.tableView的刷新
1> 数据刷新的整体步骤
* 修改模型数据
* 刷新表格(刷新界面)
2> 刷新表格(刷新界面)的方法
* 全局刷新(每一行都会从新刷新)
- (void)reloadData;
* 局部刷新(使用前提: 刷新先后, 模型数据的个数不变)
- (void)reloadRows:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
* 局部删除(使用前提: 模型数据减小的个数 == indexPaths的长度)
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
2.@property属性的用法
* weak(assign) : 代理\UI控件
* strong(retain) : 其余对象(除代理\UI控件\字符串之外的对象)
* copy : 字符串
* assign : 非对象类型(基本数据类型int\float\BOOL\枚举\结构体)
绘图
- (void)drawRect:(CGRect)rect {
// 这里是Layer Graphics Context
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 绘制图形
// 设置起点
CGContextMoveToPoint(ctx, 10, 10);
// 设置终点 ctx 是上下文
CGContextAddLineToPoint(ctx, 10, 100);
// 绘制图形(渲染到layer上)view
CGContextStrokePath(ctx);
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
// 得到图形的上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 绘制图形的起点
CGContextMoveToPoint(ctx, 50, 0);
// 设置第二个点
CGContextAddLineToPoint(ctx, 0, 100);
// 设置第三个点
CGContextAddLineToPoint(ctx, 100, 100);
// 进行封口操做
CGContextClosePath(ctx);
// 渲染
CGContextStrokePath(ctx);
CGContextAddRect(ctx, CGRectMake(50, 100, 50, 100));
[[UIColor redColor]set];
// CGContextStrokePath(ctx);
CGContextFillPath(ctx);
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
//当自定义的view即将显示的时候 就会调用这个方法
- (void)drawRect:(CGRect)rect {
// 这里是Layer Graphics Context
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 绘制图形
// 设置起点
CGContextMoveToPoint(ctx, 10, 10);
// 设置终点 ctx 是上下文
CGContextAddLineToPoint(ctx, 10, 100);
CGContextAddLineToPoint(ctx, 100, 100);
// 设置绘图状态
// 设置线条颜色
CGContextSetRGBStrokeColor(ctx, 1.0, 0, 0, 1.0);
// 设置线条宽度
CGContextSetLineWidth(ctx, 10.2);
// 设置线条圆头
CGContextSetLineCap(ctx, kCGLineCapRound);
// 设置线条转角的圆形
CGContextSetLineJoin(ctx, kCGLineJoinRound);
// 绘制图形(渲染到layer上)view
CGContextStrokePath(ctx);
}
新建一个起点
void CGContextMoveToPoint(CGContextRef c, CGFloat x, CGFloat y)
添加新的线段到某个点
void CGContextAddLineToPoint(CGContextRef c, CGFloat x, CGFloat y)
添加一个矩形
void CGContextAddRect(CGContextRef c, CGRect rect)
添加一个椭圆
void CGContextAddEllipseInRect(CGContextRef context, CGRect rect)
添加一个圆弧
void CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y,
CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)
绘制平行线
/**
* 绘制平行线
*
* @param rect <#rect description#>
*/
- (void)drawRect:(CGRect)rect {
// 注意这是一个C语言的函数 没有*
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGPoint addLines[] = {CGPointMake(10.0, 200),CGPointMake(50.0, 100),CGPointMake(90.0, 200),CGPointMake(130.0, 100),CGPointMake(170.0, 200),CGPointMake(210.0, 100)};
// 绘制
CGContextStrokeLineSegments(ctx, addLines, sizeof(addLines)/sizeof(addLines[0]));
}
渐变
- (void)drawRect:(CGRect)rect {
// 绘制渐变色
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGColorSpaceRef color = CGColorSpaceCreateDeviceRGB();//建立色彩空间
// 开始设置颜色
UIColor * start = [UIColor blueColor];
CGFloat * startColorComp = (CGFloat *)CGColorGetComponents([start CGColor]);
// 设置结束颜色
UIColor *end = [UIColor yellowColor];
CGFloat * endColorComp = (CGFloat *)CGColorGetComponents([end CGColor]);
// 建立颜色份量数组
CGFloat colorComponents[8] = {
startColorComp[0],startColorComp[1],startColorComp[2],startColorComp[3],endColorComp[0],endColorComp[1],endColorComp[2],endColorComp[3],
};
// 指定渐变开始位置和渐变结束位置
CGFloat colorIndices[2] = {0.0f ,1.0f,};
// 建立渐变
CGGradientRef gradient = CGGradientCreateWithColorComponents(color, (const CGFloat *)&colorComponents, (const CGFloat *)&colorIndices, 2);
CGPoint startPoint ,endPoint;
startPoint = CGPointMake(120, 260);
endPoint = CGPointMake(200.0, 200);
CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint, 0);
}
图片的添加
- (void)drawRect:(CGRect)rect {
UIImage * image = [UIImage imageNamed:@"abc"];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect re = CGRectMake(60, 60, 60, 60);
CGContextClipToRect(ctx, CGRectMake(0, 0, 320, 480));
CGContextDrawTiledImage(ctx, re, image.CGImage);
}
文字处理
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
/**
* CGTextDrawingMode mode 枚举kCGTextFill,
kCGTextStroke,描边
kCGTextFillStroke, 描边加填充
kCGTextInvisible,
kCGTextFillClip, kCGTextFill,填充
kCGTextStrokeClip,
kCGTextFillStrokeClip,
kCGTextClip
*/
// CGContextSetTextDrawingMode(<#CGContextRef c#>, CGTextDrawingMode mode)
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 取得系统的字体
CGFontRef font = CGFontCreateWithFontName((CFStringRef)@"Helvetica");
// 自定义字体
CGContextSetFont(ctx, font);
// 设置字体字号
CGContextSetFontSize(ctx, 100.0);
CGContextSetRGBFillColor(ctx, 0.0, 1.0, 0, 1.0);
CGContextSetRGBStrokeColor(ctx, 1.0, 1.0, 0, 1.0);
CGContextSetTextMatrix(ctx, CGAffineTransformMakeScale(1.0, -1.0));
CGPoint point[] = {
CGPointMake(20, -120),
CGPointMake(120, -200),
CGPointMake(220, -300),
};
CGGlyph glyphs[23] ={90,91,92};
// 填充
CGContextSetTextDrawingMode(ctx, kCGTextFill);
CGContextShowGlyphsAtPositions(ctx, &glyphs[0], &point[0], 1);
// 描边
CGContextSetTextDrawingMode(ctx, kCGTextStroke);
CGContextShowGlyphsAtPositions(ctx, &glyphs[1], &point[1], 1);
//
CGContextSetTextDrawingMode(ctx, kCGTextFillStrokeClip);
CGContextShowGlyphsAtPositions(ctx, &glyphs[2], &point[2], 1);
}
OC 画图
#import "GYLcircle.h"
@implementation GYLcircle
- (void)drawRect:(CGRect)rect
{
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 画圆
CGContextAddArc(ctx, 100, 100, 50, 0, 2 * M_PI, 0);
// 3.渲染 (注意, 画线只能经过空心来画)
CGContextFillPath(ctx);
}
- (void)test3
{
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.画饼状图
// 画线
CGContextMoveToPoint(ctx, 100, 100);
CGContextAddLineToPoint(ctx, 100, 150);
// 画圆弧
CGContextAddArc(ctx, 100, 100, 50, M_PI_2, M_PI, 0);
// CGContextAddArc(ctx, 100, 100, 50, -M_PI, M_PI_2, 1);
// 关闭路径
CGContextClosePath(ctx);
// 3.渲染 (注意, 画线只能经过空心来画)
CGContextFillPath(ctx);
// CGContextStrokePath(ctx);
}
- (void)test2
{
// 画圆弧
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.画圆弧
// x/y 圆心
// radius 半径
// startAngle 开始的弧度
// endAngle 结束的弧度
// clockwise 画圆弧的方向 (0 顺时针, 1 逆时针)
// CGContextAddArc(ctx, 100, 100, 50, -M_PI_2, M_PI_2, 0);
CGContextAddArc(ctx, 100, 100, 50, M_PI_2, M_PI, 0);
CGContextClosePath(ctx);
// 3.渲染
// CGContextStrokePath(ctx);
CGContextFillPath(ctx);
}
- (void)test
{
// 画圆
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.画圆
CGContextAddEllipseInRect(ctx, CGRectMake(50, 100, 50, 50));
[[UIColor greenColor] set];
// 3.渲染
// CGContextStrokePath(ctx);
CGContextFillPath(ctx);
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
// 加载的图片
UIImage * image = [UIImage imageNamed:@"bg"];
// 利用OC方法将图片绘制到layer上面
//1 将图片绘制到指定的位置 point
[image drawAtPoint:CGPointMake(0, 0)];
// 2利用drawInRect的方法将图片绘制到layer上面去 ,是经过拉伸原有的图片进行的绘制
[image drawInRect:CGRectMake(0, 0, 200, 200)];
// 3.利用drawAsPatternInRect的方法将图片绘制到layer上面去,是经过平铺图片实现的
[image drawAsPatternInRect:CGRectMake(0, 0, 320, 480)];
}
-(void)test
{
// 画文字
NSString * str = @"Lenny";
// 获取上下文
// CGContextRef ctx = UIGraphicsGetCurrentContext();
// 绘图
// 不推荐使用C语言的方法绘制文字, 由于quraz2d中的坐标系和UIkit中的坐标系不一致, 绘制出来的文字是颠倒的, 并且经过C语言的方法绘制文字至关麻烦
// CGContextSelectFont(<#CGContextRef c#>, <#const char *name#>, <#CGFloat size#>, <#CGTextEncoding textEncoding#>)
// CGContextShowText(ctx, <#const char *string#>, <#size_t length#>)
// 绘制矩形
// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 绘图
CGContextAddRect(ctx, CGRectMake(50, 50, 100, 100));
// 渲染
CGContextStrokePath(ctx);
NSMutableDictionary * md = [NSMutableDictionary dictionary];
// 设置字体的大小
md[NSFontAttributeName] = [UIFont systemFontOfSize:50];
// 设置字体的颜色
md[NSForegroundColorAttributeName] = [UIColor redColor];
// 设置字体的背景颜色
md[NSBackgroundColorAttributeName] = [UIColor whiteColor];
// 将字体画到指定的点的位置
[str drawAtPoint:CGPointMake(10, 10) withAttributes:md];
// 将文字绘制到指定的范围内, 若是一行装不下会自动换行, 当文字超出范围后就不显示
[str drawInRect:CGRectMake(50, 50, 100, 100) withAttributes:md];
}
矩阵操做
// 画四边形
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 保存上下文
CGContextSaveGState(ctx);
// 注意:设置矩阵操做必须在添加绘图信息以前
CGContextRotateCTM(ctx, M_PI_4);
// CGContextScaleCTM(ctx, 0.5, 0.5);
// CGContextTranslateCTM(ctx, 0, 150);
CGContextAddRect(ctx, CGRectMake(200, 100, 100, 100));
CGContextRestoreGState(ctx);
CGContextAddEllipseInRect(ctx, CGRectMake(20, 20, 100, 100));
CGContextStrokePath(ctx);
图形上下文栈
- (void)drawRect:(CGRect)rect
{
// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 保存一份最纯洁的图形上下文
// 调用一次该方法就会拷贝一个上下文到栈中
CGContextSaveGState(ctx);
//CGContextSaveGState(ctx);
// 第一条线
// 利用图形上下文保存绘图信息
CGContextMoveToPoint(ctx, 150, 20);
CGContextAddLineToPoint(ctx, 20, 100);
// 设置第一条线的状态
CGContextSetLineWidth(ctx, 10);
CGContextSetLineCap(ctx, kCGLineCapRound);
[[UIColor redColor] set];
// 渲染
CGContextStrokePath(ctx);
// 还原开始保存的那份最纯洁的图形上下文
CGContextRestoreGState(ctx);
// 第二条线
CGContextMoveToPoint(ctx, 80, 30);
CGContextAddLineToPoint(ctx, 80, 150);
/*
// 清空状态
CGContextSetLineWidth(ctx, 5);
CGContextSetLineCap(ctx, kCGLineCapButt);
[[UIColor greenColor] set];
*/
// 还原开始保存的那份最纯洁的图形上下文
CGContextRestoreGState(ctx);
/*
// 第3条线
CGContextMoveToPoint(ctx, 200, 30);
CGContextAddLineToPoint(ctx, 80, 150);
*/
// 渲染
CGContextStrokePath(ctx);
Quartz 2D图片的剪切
- (void)drawRect:(CGRect)rect
{
// Drawing code
// 画圆, 以便于之后指定能够显示内容范围
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(ctx, CGRectMake(100, 100, 50, 50));
// 指定上下文中能够显示内容的范围
CGContextClip(ctx);
CGContextStrokePath(ctx);
/*
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2. 绘制三角形
// 设置起点
CGContextMoveToPoint(ctx, 100, 10);
// 设置第二个点
CGContextAddLineToPoint(ctx, 50, 100);
// 设置第三个点
CGContextAddLineToPoint(ctx, 150, 100);
// 设置终点
// CGContextAddLineToPoint(ctx, 100, 10);
// 关闭起点和终点
CGContextClosePath(ctx);
// 指定上下文中能够显示内容的范围
// 注意,指定范围(也就是指点剪切的方法必定要在绘制范围以前调用)
CGContextClip(ctx);
// 3.渲染图形到layer上
CGContextStrokePath(ctx);
*/
UIImage *image = [UIImage imageNamed:@"me"];
// 按照原始大小绘制
[image drawAtPoint:CGPointMake(100, 100)];
CGContextAddRect(ctx, CGRectMake(10, 10, 100, 100));
CGContextFillPath(ctx);
}
动态刷帧
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 进行清屏的操做
CGContextClearRect(ctx, rect);
self.imageY += 5;
if (self.imageY > rect.size.height) {
self.imageY = 0;
}
// Drawing code
UIImage *image = [UIImage imageNamed:@"snow"];
[image drawAtPoint:CGPointMake(10, self.imageY)];
// [self setNeedsDisplay];
}
-(void)awakeFromNib
{
NSLog(@"awakeFromNib");
// 建立CADisplayLink, 默认每秒60次
CADisplayLink *display = [CADisplayLink displayLinkWithTarget:self selector:@selector(updataImage)];
// 将CADisplayLink加入到消息循环中
[display addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)updataImage
{
[self setNeedsDisplay];
}
画四边形的方法
// 画四边形
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 1.第一种方式, 经过链接固定的点绘制四边形
// CGContextMoveToPoint(ctx, 0, 0);
// CGContextAddLineToPoint(ctx, <#CGFloat x#>, <#CGFloat y#>)
// CGContextAddLineToPoint(ctx, <#CGFloat x#>, <#CGFloat y#>)
// CGContextAddLineToPoint(ctx, <#CGFloat x#>, <#CGFloat y#>)
// CGContextAddLineToPoint(ctx, <#CGFloat x#>, <#CGFloat y#>)
// 2.指定起点和宽高绘制四边形
// CGContextAddRect(ctx, CGRectMake(10, 10, 100, 100));
// CGContextStrokePath(ctx);
// 3.两步合为一部
// CGContextStrokeRect(ctx, CGRectMake(10, 10, 100, 100));
// CGContextFillRect(ctx, CGRectMake(10, 10, 100, 100));
// 4.经过OC的方法绘制实心的四边形, 注意没有空心的方法
// UIRectFill(CGRectMake(10, 10, 100, 100));
// 5.经过绘制线条设置宽度
CGContextMoveToPoint(ctx, 10, 10);
CGContextAddLineToPoint(ctx, 100, 100);
CGContextSetLineWidth(ctx, 50);
CGContextStrokePath(ctx);
内存管理
- (void)drawRect:(CGRect)rect
{
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.绘制图形
/*
// 设置起点
CGContextMoveToPoint(ctx, 10, 10);
// 设置终点
CGContextAddLineToPoint(ctx, 100, 100);
// 3.画圆
CGContextAddEllipseInRect(ctx, CGRectMake(50, 50, 50, 50));
*/
// 2.建立路径(一个path就表明一条路径)
// 但凡经过quarzt2d中的带有create/ copy / retain 方法建立出来的值都必须手动的释放
CGMutablePathRef path = CGPathCreateMutable();
// 设置起点
CGPathMoveToPoint(path, NULL, 10, 10);
// 设置终点
CGPathAddLineToPoint(path, NULL, 100, 100);
// 将路径添加到上下文中
CGContextAddPath(ctx, path);
// 3.再建立一条路径用于保存圆
CGMutablePathRef path2 = CGPathCreateMutable();
// 在path中添加画的路径
CGPathAddEllipseInRect(path2, NULL, CGRectMake(50, 50, 50, 50));
CGContextAddPath(ctx, path2);
// 3.渲染'
CGContextStrokePath(ctx);
// 释放前面建立的两条路径
CGPathRelease(path);
CGPathRelease(path2);
// 下面这种方式也能够释放路径
CFRelease(path);
CFRelease(path2);
bitmap
- (void)viewDidLoad {
[super viewDidLoad];
// 0 建立一个bitmap的上文
UIGraphicsGetImageFromCurrentImageContext();
/*
size :指定未来建立出来的bitmap的大小
opaque : YES:不透明 NO:透明
scale: 缩放比例
建立出来的bitmap就对应一个UIImage
*/
UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 200), NO, 0);
// 1.获取文件的下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.绘图
CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 20, 20));
//3.渲染
CGContextStrokePath(ctx);
// 4.生成图片
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
// 5.显示生成图片
self.vi.image = image;
// 6.保存生成的图片 可是要先保存成nsdata数据 而后再写到本地
// 先将图片转换为二进制数据, 而后再将图片写到文件中
NSData * data =UIImagePNGRepresentation(image);
[data writeToFile:@"/Users/guoyule/Desktop/Lenny.png" atomically:YES];
}
生成水印catagray
#import <UIKit/UIKit.h>
@interface UIImage (LK)
/**
* 生成水印
*
* @param bgName 背景图片
* @param logNmae 水印图片
*
* @return 生成好的图片(带水印的图片)
*/
+ (instancetype)imageWithBackgroundImageName:(NSString *)bgName log:(NSString *)logNmae;
@end
#import "UIImage+LK.h"
@implementation UIImage (LK)
+ (instancetype)imageWithBackgroundImageName:(NSString *)bgName log:(NSString *)logNmae
{
// 0. 加载背景图片
UIImage *image = [UIImage imageNamed:bgName];
// 1.建立bitmap上下文
// 执行完这一行在内存中就相遇建立了一个UIImage
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
// 2.绘图图片
// 绘制背景图片
[image drawAtPoint:CGPointMake(0, 0)];
// 绘制水印'
UIImage *logImage = [UIImage imageNamed:logNmae];
CGFloat margin = 10;
CGFloat logY = margin;
CGFloat logX = image.size.width - margin - logImage.size.width;
[logImage drawAtPoint:CGPointMake(logX, logY)];
// NSString *str = @"黑马程序员";
// [str drawAtPoint:CGPointMake(150, 50) withAttributes:nil];
// 3.得到图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
return newImage;
}
@end
UIResponder事件处理 _Lenny Kwok
UIResponder内部提供了如下方法来处理事件
触摸事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
加速计事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
远程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;
UIView的触摸事件处理
UIView是UIResponder的子类,能够覆盖下列4个方法处理不一样的触摸事件
一根或者多根手指开始触摸view,系统会自动调用view的下面方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
一根或者多根手指在view上移动,系统会自动调用view的下面方法(随着手指的移动,会持续调用该方法)
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
一根或者多根手指离开view,系统会自动调用view的下面方法
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
触摸结束前,某个系统事件(例如电话呼入)会打断触摸过程,系统会自动调用view的下面方法
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
提示:touches中存放的都是UITouch对象
UITouch
当用户用一根触摸屏幕时,会建立一个与手指相关联的UITouch对象
一根手指对应一个UITouch对象
UITouch的做用
保存着跟手指相关的信息,好比触摸的位置、时间、阶段
当手指移动时,系统会更新同一个UITouch对象,使之可以一直保存该手指在的触摸位置
当手指离开屏幕时,系统会销毁相应的UITouch对象
提示:iPhone开发中,要避免使用双击事件!
UITouch的属性
触摸产生时所处的窗口
@property(nonatomic,readonly,retain) UIWindow *window;
触摸产生时所处的视图
@property(nonatomic,readonly,retain) UIView *view;
短期内点按屏幕的次数,能够根据tapCount判断单击、双击或更多的点击
@property(nonatomic,readonly) NSUInteger tapCount;
记录了触摸事件产生或变化时的时间,单位是秒
@property(nonatomic,readonly) NSTimeInterval timestamp;
当前触摸事件所处的状态
@property(nonatomic,readonly) UITouchPhase phase;
- (CGPoint)locationInView:(UIView *)view;
返回值表示触摸在view上的位置
这里返回的位置是针对view的坐标系的(以view的左上角为原点(0, 0))
调用时传入的view参数为nil的话,返回的是触摸点在UIWindow的位置
- (CGPoint)previousLocationInView:(UIView *)view;
该方法记录了前一个触摸点的位置
UIEvent
每产生一个事件,就会产生一个UIEvent对象
UIEvent:称为事件对象,记录事件产生的时刻和类型
常见属性
事件类型
@property(nonatomic,readonly) UIEventType type;
@property(nonatomic,readonly) UIEventSubtype subtype;
事件产生的时间
@property(nonatomic,readonly) NSTimeInterval timestamp;
UIEvent还提供了相应的方法能够得到在某个view上面的触摸对象(UITouch)
touches和event参数
一次完整的触摸过程,会经历3个状态:
触摸开始:- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
触摸移动:- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
触摸结束:- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
触摸取消(可能会经历):- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
4个触摸事件处理方法中,都有NSSet *touches和UIEvent *event两个参数
一次完整的触摸过程当中,只会产生一个事件对象,4个触摸方法都是同一个event参数
若是两根手指同时触摸一个view,那么view只会调用一次touchesBegan:withEvent:方法,touches参数中装着2个UITouch对象
若是这两根手指一前一后分开触摸同一个view,那么view会分别调用2次touchesBegan:withEvent:方法,而且每次调用时的touches参数中只包含一个UITouch对象
根据touches中UITouch的个数能够判断出是单点触摸仍是多点触摸
事件的产生和传递
发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中
UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,一般,先发送事件给应用程序的主窗口(keyWindow)
主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件
找到合适的视图控件后,就会调用视图控件的touches方法来做具体的事件处理
touchesBegan…
touchesMoved…
touchedEnded…
重点触摸事件的传递
触摸事件的传递是从父控件传递到子控件
点击了绿色的view:
UIApplication -> UIWindow -> 白色 -> 绿色
点击了蓝色的view:
UIApplication -> UIWindow -> 白色 -> 橙色 -> 蓝色
点击了黄色的view:
UIApplication -> UIWindow -> 白色 -> 橙色 -> 蓝色 -> 黄色
若是父控件不能接收触摸事件,那么子控件就不可能接收到触摸事件(掌握)
如何找到最合适的控件来处理事件?
本身是否能接收触摸事件?
触摸点是否在本身身上?
从后往前遍历子控件,重复前面的两个步骤
若是没有符合条件的子控件,那么就本身最适合处理
1.事件的完整处理过程:
1> 先将事件对象由上往下传递(由父控件传递给子控件), 找到最合适的控件来处理这个事件
2> 调用最合适控件的touches.....方法
3> 若是调用了[super touches...];就会将事件顺着响应者链条往上传递,传递给上一个响应者
4> 接着就会调用上一个响应者的touches.....方法
2.什么是响应者链条?
1> 响应者链条是由多个响应者对象链接起来的链条(什么是响应者对象: 能处理事件的对象)
2> 利用响应者链条, 能让多个控件 处理 同一个触摸事件
3> 怎么利用链条往上传递?谁是上一个响应者
3.谁是上一个响应者(nextResponder)
1> 若是当前这个view是控制器的view, 那么控制器就是上一个响应者
2> 若是当前这个view不是控制器的view, 那么父控件就是上一个响应者
触摸事件
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 1.打开交互
self.iconView.userInteractionEnabled = YES;
// 2.建立手势识别器
UITapGestureRecognizer * tp = [[UITapGestureRecognizer alloc]init];
// 3.添加手势到View
[self.iconView addGestureRecognizer:tp];
// 表示连着按下两次
tp.numberOfTapsRequired = 2;
// 表示手指的根数
tp.numberOfTouchesRequired = 2;
NSInteger x= tp.numberOfTouches;
NSLog(@"%ld",(long)x);
// 4.监听手势识别器
[tp addTarget:self action:@selector(change)];
}
-(void)change
{
NSLog(@"他摸我");
}
UIGestureRecognizer
为了完成手势识别,必须借助于手势识别器----UIGestureRecognizer
利用UIGestureRecognizer,能轻松识别用户在某个view上面作的一些常见手势
UIGestureRecognizer是一个抽象类,定义了全部手势的基本行为,使用它的子类才能处理具体的手势
UITapGestureRecognizer(敲击)
UIPinchGestureRecognizer(捏合,用于缩放)
UIPanGestureRecognizer(拖拽)
UISwipeGestureRecognizer(轻扫)
UIRotationGestureRecognizer(旋转)
UILongPressGestureRecognizer(长按)
UITapGestureRecognizer
每个手势识别器的用法都差很少,好比UITapGestureRecognizer的使用步骤以下
建立手势识别器对象
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
设置手势识别器对象的具体属性
// 连续敲击2次
tap.numberOfTapsRequired = 2;
// 须要2根手指一块儿敲击
tap.numberOfTouchesRequired = 2;
添加手势识别器到对应的view上
[self.iconView addGestureRecognizer:tap];
监听手势的触发
[tap addTarget:self action:@selector(tapIconView:)];
手势识别的状态
typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
// 没有触摸事件发生,全部手势识别的默认状态
UIGestureRecognizerStatePossible,
// 一个手势已经开始但还没有改变或者完成时
UIGestureRecognizerStateBegan,
// 手势状态改变
UIGestureRecognizerStateChanged,
// 手势完成
UIGestureRecognizerStateEnded,
// 手势取消,恢复至Possible状态
UIGestureRecognizerStateCancelled,
// 手势失败,恢复至Possible状态
UIGestureRecognizerStateFailed,
// 识别到手势识别
UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
};
捏合旋转
// 该方法返回的BOOL值决定了view是否可以同时响应多个手势
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
NSLog(@"%@ - %@", gestureRecognizer.class, otherGestureRecognizer.class);
return YES;
}
- (void)pichTest
{
// 捏合手势
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] init];
pinch.delegate = self;
[self.iconView addGestureRecognizer:pinch];
[pinch addTarget:self action:@selector(pinchView:)];
}
- (void)pinchView:(UIPinchGestureRecognizer *)pinch
{
// NSLog(@"捏合事件 %.1f", pinch.scale);
// self.iconView.transform = CGAffineTransformMakeScale(pinch.scale, pinch.scale);
// 1.0 * 0.9
self.iconView.transform = CGAffineTransformScale(self.iconView.transform, pinch.scale, pinch.scale);
pinch.scale = 1.0;
}
- (void)rotationTest
{
// 旋转
UIRotationGestureRecognizer *gesture = [[UIRotationGestureRecognizer alloc] init];
gesture.delegate = self;
[self.iconView addGestureRecognizer:gesture];
[gesture addTarget:self action:@selector(rotationView:)];
}
- (void)rotationView:(UIRotationGestureRecognizer *)gesture
{
// NSLog(@"旋转事件 %.1f", gesture.rotation);
// 每次从最初的位置开始
// self.iconView.transform = CGAffineTransformMakeRotation(gesture.rotation);
// 在传入的transform基础上递增一个弧度
self.iconView.transform = CGAffineTransformRotate(self.iconView.transform, gesture.rotation);
// 将旋转的弧度清零(注意不是将图片旋转的弧度清零, 而是将当前手指旋转的弧度清零)
gesture.rotation = 0;// 若是理解不了 , 记住就OK
}
拖拽
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] init];
[self.customView addGestureRecognizer:pan];
[pan addTarget:self action:@selector(panView:)];
}
- (void)panView:(UIPanGestureRecognizer *)pan
{
// 返回的值是以手指按下的点为原点
// 1 2 3 4 5
CGPoint point = [pan translationInView:pan.view];
NSLog(@"拖拽事件 %@", NSStringFromCGPoint(point));
CGPoint temp = self.customView.center;
temp.x += point.x;
temp.y += point.y;
self.customView.center = temp;
// 理解不了就记住就OK
[pan setTranslation:CGPointZero inView:pan.view];
}
长按和轻扫
- (void)viewDidLoad
{
[super viewDidLoad];
// 向上
UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] init];
// 设置轻扫的方向
swipe.direction = UISwipeGestureRecognizerDirectionUp;
[self.customView addGestureRecognizer:swipe];
[swipe addTarget:self action:@selector(swipeView)];
// 向下
UISwipeGestureRecognizer *swipe2 = [[UISwipeGestureRecognizer alloc] init];
// 设置轻扫的方向
swipe2.direction = UISwipeGestureRecognizerDirectionDown;
[self.customView addGestureRecognizer:swipe2];
[swipe2 addTarget:self action:@selector(swipeView2)];
// 左边
UISwipeGestureRecognizer *swipe3 = [[UISwipeGestureRecognizer alloc] init];
// 设置轻扫的方向
swipe3.direction = UISwipeGestureRecognizerDirectionLeft;
[self.customView addGestureRecognizer:swipe3];
[swipe3 addTarget:self action:@selector(swipeView3)];
// 右边
UISwipeGestureRecognizer *swipe4 = [[UISwipeGestureRecognizer alloc] init];
// 设置轻扫的方向
swipe4.direction = UISwipeGestureRecognizerDirectionRight;
[self.customView addGestureRecognizer:swipe4];
[swipe4 addTarget:self action:@selector(swipeView4)];
}
- (void)swipeView4
{
NSLog(@"轻扫事件右");
}
- (void)swipeView3
{
NSLog(@"轻扫事件左");
}
- (void)swipeView2
{
NSLog(@"轻扫事件下");
}
- (void)swipeView
{
NSLog(@"轻扫事件上");
}
- (void)test
{
// 长按事件
// 1.建立手势识别器
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] init];
// 1.1设置长按手势识别器的属性
// longPress.minimumPressDuration = 5;
// 手指按下后事件响应以前容许手指移动的偏移位
longPress.allowableMovement = 50;
// 2.添加手势识别器到View
[self.customView addGestureRecognizer:longPress];
// 3.监听手势识别器
[longPress addTarget:self action:@selector(longPressView)];
}
-(void)longPressView
{
NSLog(@"长按事件");
}
CAlayer
- (void)viewDidLoad {
[super viewDidLoad];
// 设置layer边框
self.imageView.layer.borderWidth = 10;
// 设置边框的颜色
self.imageView.layer.borderColor = [UIColor redColor].CGColor;//注意borderColor的类型
// 设置layer的圆角(设置主图层的圆角)
self.imageView.layer.cornerRadius = 10;
// 设置超出主图层的部分进行剪切操做
// self.imageView.layer.masksToBounds = YES;
// self.imageView.clipsToBounds = YES;
// 设置的image不是展现在主图层上的,是展现在子图层上的
self.imageView.layer.contents = (id)[UIImage imageNamed:@"LoginScreen"].CGImage;
// 设置阴影的颜色
self.imageView.layer.shadowColor = [UIColor blackColor].CGColor;
// 设置阴影的偏移位
// 若是是正数,表明向右移动
// 上
self.imageView.layer.shadowOffset = CGSizeMake(10, 10);
// 设置阴影的透明度0~1 1 彻底不透明 0 彻底透明
self.imageView.layer.shadowOpacity =1;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// self.imageView.transform = CGAffineTransformMakeTranslation(0, 100);
// self.imageView.layer.transform = CATransform3DMakeTranslation(0, 100, 0);
// NSValue *v = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, -200, 0)];
// [self.imageView.layer setValue:v forKey:@"transform"];
// [self.imageView.layer setValue:@(100) forKey:@"transform.translation.x"];
// self.imageView.transform = CGAffineTransformMakeRotationxx(M_PI_4);
// self.imageView.layer.transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);
[self.imageView.layer setValue:@"100" forKey:@"transform.translation.x"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)test
{
// 设置layer边框
self.imageView.layer.borderWidth = 10;
// 设置边框的颜色
self.imageView.layer.borderColor = [UIColor redColor].CGColor;//注意borderColor的类型
// 设置layer的圆角(设置主图层的圆角)
self.imageView.layer.cornerRadius = 10;
// 设置超出主图层的部分进行剪切操做
// self.imageView.layer.masksToBounds = YES;
// self.imageView.clipsToBounds = YES;
// 设置的image不是展现在主图层上的,是展现在子图层上的
self.imageView.layer.contents = (id)[UIImage imageNamed:@"LoginScreen"].CGImage;
// 设置阴影的颜色
self.imageView.layer.shadowColor = [UIColor blackColor].CGColor;
// 设置阴影的偏移位
// 若是是正数,表明向右移动
// 上
self.imageView.layer.shadowOffset = CGSizeMake(10, 10);
// 设置阴影的透明度0~1 1 彻底不透明 0 彻底透明
self.imageView.layer.shadowOpacity =1;
}
-(void)test2
{
self.picyture.layer.borderWidth = 10;
self.imageView.layer.borderColor = [UIColor redColor].CGColor;
self.imageView.layer.cornerRadius = 10;
// self.iconView.layer.masksToBounds = YES;
// self.iconView.layer.bounds = CGRectMake(0, 0, 200, 200);
// self.iconView.layer.position = CGPointMake(100 , 100);
self.picyture.layer.masksToBounds = YES;
self.picyture.layer.bounds = CGRectMake(0, 0, 200, 200);
self.picyture.layer.position = CGPointMake(400, 200);
}
CALayer 新建
- (void)viewDidLoad
{
[super viewDidLoad];
// 若是一个控制是另一个控件的子控件, 那么这个控件中的layer也是另一个控件的子layer
// NSLog(@"star - %@", self.view.layer.sublayers);
CALayer *layer = [CALayer layer];
layer.backgroundColor = [UIColor redColor].CGColor;
layer.bounds = CGRectMake(0, 0, 100, 100);
// layer.position = CGPointMake(200, 200);
// layer.contents = (id)[UIImage imageNamed:@"me"].CGImage;
[self.view.layer addSublayer:layer];
}
- (void)test
{
NSLog(@"star - %@", self.view.layer.sublayers);
// 1.建立layer
// CALayer *layer = [[CALayer alloc] init];
CALayer *layer = [CALayer layer];
layer.backgroundColor = [UIColor redColor].CGColor;
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = CGPointMake(200, 200);
layer.borderWidth = 10;
layer.cornerRadius = 10;
// 将layer添加在界面上
[self.view.layer addSublayer:layer];
// NSLog(@"%@", layer.superlayer); // 获取layer的父视图
NSLog(@"end - %@", self.view.layer.sublayers);
//
// UIView *view = [[UIView alloc] init];
// view.superview;
// view.subviews;
// [self.view addSubview:view];
}
关于CALayer的疑惑
关于CALayer的疑惑
首先
CALayer是定义在QuartzCore框架中的
CGImageRef、CGColorRef两种数据类型是定义在CoreGraphics框架中的
UIColor、UIImage是定义在UIKit框架中的
其次
QuartzCore框架和CoreGraphics框架是能够跨平台使用的,在iOS和Mac OS X上都能使用
可是UIKit只能在iOS中使用
为了保证可移植性,QuartzCore不能使用UIImage、UIColor,只能使用CGImageRef、CGColorRef
UIView和CALayer的选择
经过CALayer,就能作出跟UIImageView同样的界面效果
既然CALayer和UIView都能实现相同的显示效果,那究竟该选择谁好呢?
其实,对比CALayer,UIView多了一个事件处理的功能。也就是说,CALayer不能处理用户的触摸事件,而UIView能够
因此,若是显示出来的东西须要跟用户进行交互的话,用UIView;若是不须要跟用户进行交互,用UIView或者CALayer均可以
固然,CALayer的性能会高一些,由于它少了事件处理的功能,更加轻量级
position和anchorPoint
CALayer有2个很是重要的属性:position和anchorPoint
@property CGPoint position;
用来设置CALayer在父层中的位置
以父层的左上角为原点(0, 0)
@property CGPoint anchorPoint;
称为“定位点”、“锚点”
决定着CALayer身上的哪一个点会在position属性所指的位置
以本身的左上角为原点(0, 0)
它的x、y取值范围都是0~1,默认值为(0.5, 0.5)
红色图层的anchorPoint是(0,0)
红色图层的anchorPoint是(0.5,0.5)
红色图层的anchorPoint是(1,1)
红色图层的anchorPoint是(0.5,0)
红色图层的anchorPoint是(1,0.5)
隐式动画
每个UIView内部都默认关联着一个CALayer,咱们可用称这个Layer为Root Layer(根层)
全部的非Root Layer,也就是手动建立的CALayer对象,都存在着隐式动画
什么是隐式动画?
当对非Root Layer的部分属性进行修改时,默认会自动产生一些动画效果
而这些属性称为Animatable Properties(可动画属性)
列举几个常见的Animatable Properties:
bounds:用于设置CALayer的宽度和高度。修改这个属性会产生缩放动画
backgroundColor:用于设置CALayer的背景色。修改这个属性会产生背景色的渐变更画
position:用于设置CALayer的位置。修改这个属性会产平生移动画
能够经过动画事务(CATransaction)关闭默认的隐式动画效果
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.myview.layer.position = CGPointMake(10, 10);
[CATransaction commit];
- (void)viewDidLoad {
[super viewDidLoad];
CALayer * layer = [[CALayer alloc]init];
layer.bounds = CGRectMake(0, 0, 200, 200);
layer.anchorPoint = CGPointMake(0.5, 0.5);
layer.backgroundColor = [UIColor redColor].CGColor;
[self.view.layer addSublayer:layer];
self.layer = layer;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// 关闭动画
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.layer.backgroundColor = [UIColor greenColor].CGColor;
self.layer.position = CGPointMake(200, 200);
// self.layer.position // 如何查看CALayer的某个属性是否支持隐式动画, 查看头文件是否有 Animatable
// 如何查看是否CAlayer支持隐式动画,查看头文件是否有Animation
[CATransaction commit];
}
核心动画(简介)
Core Animation,中文翻译为核心动画,它是一组很是强大的动画处理API,使用它能作出很是炫丽的动画效果,并且每每是事半功倍。也就是说,使用少许的代码就能够实现很是强大的功能。
Core Animation能够用在Mac OS X和iOS平台。
Core Animation的动画执行过程都是在后台操做的,不会阻塞主线程。
要注意的是,Core Animation是直接做用在CALayer上的,并不是UIView。
Core Animation的使用步骤
1.使用它须要先添加QuartzCore.framework框架和引入主头文件<QuartzCore/QuartzCore.h>(iOS7不须要)
2.初始化一个CAAnimation对象,并设置一些动画相关属性
3.经过调用CALayer的addAnimation:forKey:方法增长CAAnimation对象到CALayer中,这样就能开始执行动画了
4.经过调用CALayer的removeAnimationForKey:方法能够中止CALayer中的动画
CAAnimation
全部动画对象的父类,负责控制动画的持续时间和速度,是个抽象类,不能直接使用,应该使用它具体的子类
属性解析:(红色表明来自CAMediaTiming协议的属性)
duration:动画的持续时间
repeatCount:动画的重复次数
repeatDuration:动画的重复时间
removedOnCompletion:默认为YES,表明动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。若是想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillMode为kCAFillModeForwards
fillMode:决定当前对象在非active时间段的行为.好比动画开始以前,动画结束以后
beginTime:能够用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2,CACurrentMediaTime()为图层的当前时间
timingFunction:速度控制函数,控制动画运行的节奏
delegate:动画代理
CAPropertyAnimation
是CAAnimation的子类,也是个抽象类,要想建立动画对象,应该使用它的两个子类:CABasicAnimation和CAKeyframeAnimation
属性解析:
keyPath:经过指定CALayer的一个属性名称为keyPath(NSString类型),而且对CALayer的这个属性的值进行修改,达到相应的动画效果。好比,指定@”position”为keyPath,就修改CALayer的position属性的值,以达到平移的动画效果
CABasicAnimation
CAPropertyAnimation的子类
属性解析:
fromValue:keyPath相应属性的初始值
toValue:keyPath相应属性的结束值
随着动画的进行,在长度为duration的持续时间内,keyPath相应属性的值从fromValue渐渐地变为toValue
若是fillMode=kCAFillModeForwards和removedOnComletion=NO,那么在动画执行完毕后,图层会保持显示动画执行后的状态。但在实质上,图层的属性值仍是动画执行前的初始值,并无真正被改变。好比,CALayer的position初始值为(0,0),CABasicAnimation的fromValue为(10,10),toValue为(100,100),虽然动画执行完毕后图层保持在(100,100)这个位置,实质上图层的position仍是为(0,0)
CAKeyframeAnimation
CApropertyAnimation的子类,跟CABasicAnimation的区别是:CABasicAnimation只能从一个数值(fromValue)变到另外一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值
属性解析:
values:就是上述的NSArray对象。里面的元素称为”关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每个关键帧
path:能够设置一个CGPathRef\CGMutablePathRef,让层跟着路径移动。path只对CALayer的anchorPoint和position起做用。若是你设置了path,那么values将被忽略
keyTimes:能够为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每个时间值都对应values中的每一帧.当keyTimes没有设置的时候,各个关键帧的时间是平分的
CABasicAnimation可看作是最多只有2个关键帧的CAKeyframeAnimation
CAAnimationGroup
CAAnimation的子类,能够保存一组动画对象,将CAAnimationGroup对象加入层后,组中全部动画对象能够同时并发运行
属性解析:
animations:用来保存一组动画对象的NSArray
默认状况下,一组动画对象是同时运行的,也能够经过设置动画对象的beginTime属性来更改动画的开始时间
CATransition
CAAnimation的子类,用于作转场动画,可以为层提供移出屏幕和移入屏幕的动画效果。iOS比Mac OS X的转场动画效果少一点
UINavigationController就是经过CATransition实现了将控制器的视图推入屏幕的动画效果
属性解析:
type:动画过渡类型
subtype:动画过渡方向
startProgress:动画起点(在总体动画的百分比)
endProgress:动画终点(在总体动画的百分比)
UIView动画
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表明使用视图缓存,性能较好
Block动画
+ (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
UIImageView的帧动画
UIImageView可让一系列的图片在特定的时间内按顺序显示
相关属性解析:
animationImages:要显示的图片(一个装着UIImage的NSArray)
animationDuration:完整地显示一次animationImages中的全部图片所需的时间
animationRepeatCount:动画的执行次数(默认为0,表明无限循环)
相关方法解析:
- (void)startAnimating; 开始动画
- (void)stopAnimating; 中止动画
- (BOOL)isAnimating; 是否正在运行动画
UIActivityIndicatorView
是一个旋转进度轮,能够用来告知用户有一个操做正在进行中,通常用initWithActivityIndicatorStyle初始化
方法解析:
- (void)startAnimating; 开始动画
- (void)stopAnimating; 中止动画
- (BOOL)isAnimating; 是否正在运行动画
UIActivityIndicatorViewStyle有3个值可供选择:
UIActivityIndicatorViewStyleWhiteLarge //大型白色指示器
UIActivityIndicatorViewStyleWhite //标准尺寸白色指示器
UIActivityIndicatorViewStyleGray //灰色指示器,用于白色背景
抖动代码
//
// ViewController.m
// 01-抖动
//
// Created by Lenny on 3/16/15.
// Copyright (c) 2015 Lenny. All rights reserved.
//
#import "ViewController.h"
#define angle2Radian(angle) ((angle) / 180.0 * M_PI)
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *imageView;
@end
@implementation ViewController
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// 1.建立核心动画
CAKeyframeAnimation * kfr = [[CAKeyframeAnimation alloc]init];
// 2.建立核心动画的类型
kfr.keyPath = @"transform.rotation";
// 度数/ 180.0 *M_PI
kfr.values = @[@(-angle2Radian(4)), @(angle2Radian(4)), @(-angle2Radian(4))];
kfr.removedOnCompletion = NO;
kfr.fillMode = kCAFillModeBackwards;
kfr.duration = 0.1;
// 设置重复次数
kfr.repeatCount = MAXFLOAT;
// 3添加核心动画
[self.imageView.layer addAnimation:kfr forKey:nil];
}
@end
转场帧动画
- (IBAction)preImage:(id)sender {
self.index--;
if (self.index < 1) {
self.index = 7;
}
NSString * imageName = [NSString stringWithFormat:@"%d.jpg",self.index];
UIImage * image = [UIImage imageNamed:imageName];
self.imageView.image = image;
// 建立核心动画
CATransition * ca = [CATransition animation];
// 动画的过分类型
ca.type = @"cube";
// 动画过分的方向
ca.subtype = kCATransitionFromRight;
// 动画的起点终点动画
// ca.startProgress = 0.5;
// ca.endProgress = 0.5;
// 动画时间
ca.duration = 1;
[self.imageView.layer addAnimation:ca forKey:nil];
}
- (IBAction)nextImage:(id)sender {
self.index++;
if (self.index >7) {
self.index = 1;
}
NSString * imageName = [NSString stringWithFormat:@"%d.jpg",self.index];
UIImage * image = [UIImage imageNamed:imageName];
self.imageView.image = image;
// 建立核心动画
CATransition * ca = [CATransition animation];
// 动画的过分类型
ca.type = @"cube";
// 动画过分的方向
ca.subtype = kCATransitionFromLeft;
// 动画的起点终点动画
// ca.startProgress = 0.5;
// ca.endProgress = 0.5;
// 动画时间
ca.duration = 1;
[self.imageView.layer addAnimation:ca forKey:nil];
}
图片的拉伸
UIImage *image = [UIImage imageNamed:@"RedButton"];
// 经过一张原始图片生成一张可拉伸的图片
CGFloat imageW = image.size.width * 0.5;
CGFloat imageH = image.size.height * 0.5;
UIImage *newImage = [image resizableImageWithCapInsets:UIEdgeInsetsMake(imageH, imageW, imageH, imageW) resizingMode:UIImageResizingModeStretch];
[self.loginBtn setBackgroundImage:newImage forState:UIControlStateNormal];
UIImage *selImage = [UIImage imageNamed:@"RedButtonPressed"];
CGFloat selImageW = selImage.size.width * 0.5;
CGFloat selImageH = selImage.size.height * 0.5;
UIImage *selNewImage = [selImage resizableImageWithCapInsets:UIEdgeInsetsMake(selImageH, selImageW, selImageH, selImageW) resizingMode:UIImageResizingModeStretch];
[self.loginBtn setBackgroundImage:selNewImage forState:UIControlStateHighlighted];
项目总结
自定义导航栏
- (void)addTabBarButtonWithNormalImageName:(NSString *)norName andDisableImageName:(NSString *)disName
{
// 3.1建立按钮
LKTabBarBtn *btn = [[LKTabBarBtn alloc] init];
// 3.2设置按钮上显示的图片
// 3.2.1设置默认状态图片
[btn setBackgroundImage:[UIImage imageNamed:norName] forState:UIControlStateNormal];
// 3.2.2设置不可用状态图片
[btn setBackgroundImage:[UIImage imageNamed:disName] forState:UIControlStateDisabled];
// 3.4添加按钮到自定义TabBar
[self addSubview:btn];
// 3.5监听按钮点击事件
[btn addTarget:self action:@selector(btnOnClick:) forControlEvents:UIControlEventTouchDown];
// 3.6设置默认选中按钮
if (1 == self.subviews.count) {
[self btnOnClick:btn];
}
// 3.7设置按钮高亮状态不调整图片
btn.adjustsImageWhenHighlighted = NO;
// 3.8设置tag
}
-(void)layoutSubviews
{
// 注意这是必需要写的否则会出现不可预知的错误
// [super layoutSubviews];
NSLog(@"self.subviews.count = %lu",(unsigned long)self.subviews.count);
for (int i = 0; i < self.subviews.count ; i++) {
// NSLog(@"self.subviews.count = %lu",(unsigned long)self.subviews.count);
UIButton *btn = self.subviews[i];
// 3.3设置frame
// NSLog(@"initWithFrame %@", NSStringFromCGRect(self.frame));
CGFloat btnY = 0;
CGFloat btnW = self.frame.size.width / 5;
CGFloat btnH = self.frame.size.height;
CGFloat btnX = i * btnW;
btn.frame = CGRectMake(btnX, btnY, btnW, btnH);
btn.tag = i;
}
}
-(void)btnOnClick:(LKTabBarBtn * )btn
{
// NSLog(@"按钮被点击了 = %ld",(long)btn.tag);
if ([self.delegate respondsToSelector:@selector(tarBarDidSelectBtnFrom:to:)]) {
[self.delegate tarBarDidSelectBtnFrom:self.selectBtn.tag to:btn.tag];
}
// 0.取消上一次选中的按钮
// self.selectBtn.selected = NO;
self.selectBtn.enabled = YES;
// 1.设置当前的点击的按钮的选中状态
btn.enabled = NO;
// 2.记录当前的按钮
self.selectBtn = btn;
// 3.切换控制器
}
数据的传递经过代理
@protocol LKTabBarDelegate <NSObject>
-(void)tarBarDidSelectBtnFrom:(NSInteger)from to:(NSInteger)to;
@end
@interface LKTabBar : UIView
@property (nonatomic, weak) id<LKTabBarDelegate> delegate;
- (void)addTabBarButtonWithNormalImageName:(NSString *)norName andDisableImageName:(NSString *)disName;
@end
控制器操做
// 1.新建一个tabBar
LKTabBar * myTabBar = [[LKTabBar alloc]init];
// myTabBar.backgroundColor = [UIColor redColor];
myTabBar.frame = self.tabBar.bounds;
// 设置代理事件
myTabBar.delegate = self;
[self.tabBar addSubview:myTabBar];
for (int i = 0; i < self.viewControllers.count; i++) {
// 通知自定义TabBar建立按钮
NSString *norImageName = [NSString stringWithFormat:@"TabBar%d", i + 1];
NSString *disableImageName = [NSString stringWithFormat:@"TabBar%dSel", i + 1];
// 只要调用自定义TabBar的该方法就会建立一个按钮
[myTabBar addTabBarButtonWithNormalImageName:norImageName andDisableImageName:disableImageName];
控制器的跳转
-(void)tarBarDidSelectBtnFrom:(NSInteger)from to:(NSInteger)to
{
self.selectedIndex = to;
NSLog(@"to = %ld",(long)to);
}
巧妙的使用父类
//只是调用一次
+(void)initialize
{
// NSLog(@"initialize");
// 1.设置导航条的主题
// 若是要同时设置不少UINavigationBar的样式, 能够经过设置UINavigationBar的主题的方式来设置以便简化代码
UINavigationBar *navBar = [UINavigationBar appearance];
// 1.1设置全部导航条的背景图片
// 判断当前运行的操做系统的版本
if ([[UIDevice currentDevice].systemVersion doubleValue] > 7.0) {
[navBar setBackgroundImage:[UIImage imageNamed:@"NavBar64"] forBarMetrics:UIBarMetricsDefault];
}else
{
[navBar setBackgroundImage:[UIImage imageNamed:@"NavBar"] forBarMetrics:UIBarMetricsDefault];
}
// 设置导航条上返回按钮和图片的颜色
[navBar setTintColor:[UIColor whiteColor]];
// 1.2设置全部导航条的标题颜色
NSMutableDictionary *md = [NSMutableDictionary dictionary];
md[NSFontAttributeName] = [UIFont systemFontOfSize:16];
md[NSForegroundColorAttributeName] = [UIColor whiteColor];
[navBar setTitleTextAttributes:md];
// 1.3设置UIBarButtonItem的主题
UIBarButtonItem *barItem = [UIBarButtonItem appearance];
// 判断是不是IOS6 若是是IOS6就设置图片
if (!([[UIDevice currentDevice].systemVersion doubleValue] > 7.0)) {
// 设置普通按钮的图片
UIImage *norImage = [UIImage imageNamed:@"NavButton"];
[barItem setBackgroundImage:norImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
UIImage *higImage = [UIImage imageNamed:@"NavButtonPressed"];
[barItem setBackgroundImage:higImage forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
// 设置返回按钮的图片
UIImage *norBackImage = [UIImage imageNamed:@"NavBackButton"];
[barItem setBackButtonBackgroundImage:norBackImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
UIImage *higBackImage = [UIImage imageNamed:@"NavBackButtonPressed"];
[barItem setBackButtonBackgroundImage:higBackImage forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
}else
{
// 是IOS7
NSMutableDictionary *barMd = [NSMutableDictionary dictionary];
barMd[NSFontAttributeName] = [UIFont systemFontOfSize:16];
barMd[NSForegroundColorAttributeName] = [UIColor whiteColor];
[barItem setTitleTextAttributes:barMd forState:UIControlStateNormal];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
// // 拿到目标控制器(即将要入栈的控制器), 设置它的自动隐藏tabbar
// viewController.hidesBottomBarWhenPushed = YES;
// [super pushViewController:viewController animated:animated];
// 拿到目标的控制器(即将要入栈的控制器),设置它的自动隐藏的tabBar
viewController.hidesBottomBarWhenPushed = YES;
[super pushViewController:viewController animated:YES];
}
不使用系统自带的button
//
// LKTitleBtn.m
// 01-彩票
//
// Created by Lenny on 3/17/15.
// Copyright (c) 2015 Lenny. All rights reserved.
//
#import "LKTitleBtn.h"
#import <Availability.h>
@interface LKTitleBtn ()
@property(nonatomic,strong) UIFont * myFont;
@end
@implementation LKTitleBtn
-(id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self setup];
}
return self;
}
-(instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setup];
}
return self;
}
-(void)setup
{
// 记录按钮标题的字体
self.myFont = [UIFont systemFontOfSize:15];
// 设置标题的字体
self.titleLabel.font = self.myFont;
// 设置按钮的图片显示的内容默认为拉伸不是居中
self.imageView.contentMode = UIViewContentModeCenter;
}
//用于返回按钮上标题的位置,传入按钮的rect
-(CGRect)titleRectForContentRect:(CGRect)contentRect
{
CGFloat titleX = 0;
CGFloat titleY = 0;
CGFloat titleW = 0;
CGFloat titleH = contentRect.size.height;
// 获取按钮上的字体 1
[self titleForState:UIControlStateNormal];
// 获取按钮上的字体2
NSString * title = self.currentTitle;//建议使用这个方法 这个方法得到的是任何状态下的title
CGSize maxSize = CGSizeMake(MAXFLOAT, MAXFLOAT);
NSMutableDictionary * md = [NSMutableDictionary dictionary];
// 死循环的缘由是self.titleLabel须要访问titleLabel, 而self.titleLabel又须要调用当前方法获取title的范围, 全部死循环
// md[NSFontAttributeName] = self.titleLabel.font;
// NSLog(@"%@", self.myFont);
md[NSFontAttributeName] = self.myFont;
// 计算文字的范围
// 判断是不是xcode5 , 若是是就编译一下代码, 若是不是就不编译
#ifdef __IPHONE_7_0
if (([[UIDevice currentDevice].systemVersion doubleValue] >= 7.0)) {
CGRect titleRect = [title boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:md context:nil];
titleW = titleRect.size.width;
}else{
CGSize titleSize = [title sizeWithFont:self.myFont];//过期的方法
titleW = titleSize.width;
}
#else
// XCODE4
CGSize titleSize = [title sizeWithFont:self.myFont];
titleW = titleSize.width;
#endif
return CGRectMake(titleX, titleY, titleW, titleH);
}
-(CGRect)imageRectForContentRect:(CGRect)contentRect{
CGFloat imageY = 0;
CGFloat imageH = contentRect.size.height;
CGFloat imageW = 16;//图片的宽度
// 图片的X = 按钮的宽度 - 图片的宽度
CGFloat imageX= contentRect.size.width - imageW;
return CGRectMake(imageX, imageY, imageW, imageH);
}
iOS 打电话的方式
最简单最直接的方式:直接跳到拨号界面
1
NSURL *url = [NSURL URLWithString:@"tel://10010"];
[[UIApplication sharedApplication] openURL:url];
缺点
电话打完后,不会自动回到原应用,直接停留在通话记录界面
2
拨号以前会弹框询问用户是否拨号,拨完后能自动回到原应用
NSURL *url = [NSURL URLWithString:@"telprompt://10010"];
[[UIApplication sharedApplication] openURL:url];
缺点
由于是私有API,因此可能不会被审核经过
3
建立一个UIWebView来加载URL,拨完后能自动回到原应用
if (_webView == nil) {
_webView = [[UIWebView alloc] initWithFrame:CGRectZero];
}
[_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"tel://10010"]]];
须要注意的是:这个webView千万不要添加到界面上来,否则会挡住其余界面
发短信-方法1
直接跳到发短信界面,可是不能指定短信内容,并且不能自动回到原应用
NSURL *url = [NSURL URLWithString:@"sms://10010"];
[[UIApplication sharedApplication] openURL:url];
发短信-方法2
若是想指定短信内容,那就得使用MessageUI框架
包含主头文件
#import <MessageUI/MessageUI.h>
显示发短信的控制器
MFMessageComposeViewController *vc = [[MFMessageComposeViewController alloc] init];
// 设置短信内容
vc.body = @"吃饭了没?";
// 设置收件人列表
vc.recipients = @[@"10010", @"02010010"];
// 设置代理
vc.messageComposeDelegate = self;
// 显示控制器
[self presentViewController:vc animated:YES completion:nil];
代理方法,当短信界面关闭的时候调用,发完后会自动回到原应用
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{
// 关闭短信界面
[controller dismissViewControllerAnimated:YES completion:nil];
if (result == MessageComposeResultCancelled) {
NSLog(@"取消发送");
} else if (result == MessageComposeResultSent) {
NSLog(@"已经发出");
} else {
NSLog(@"发送失败");
}
}
发邮件-方法1
用自带的邮件客户端,发完邮件后不会自动回到原应用
NSURL *url = [NSURL URLWithString:@"mailto://10010@qq.com"];
[[UIApplication sharedApplication] openURL:url];
发邮件-方法2
跟发短信的第2种方法差很少,只不过控制器类名叫作:MFMailComposeViewController
假设发送的邮件内容如右图所示,代码实现看备注
邮件发送后的代理方法回调,发完后会自动回到原应用
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
// 关闭邮件界面
[controller dismissViewControllerAnimated:YES completion:nil];
if (result == MFMailComposeResultCancelled) {
NSLog(@"取消发送");
} else if (result == MFMailComposeResultSent) {
NSLog(@"已经发出");
} else {
NSLog(@"发送失败");
}
}
打开其余常见文件
若是想打开一些常见文件,好比html、txt、PDF、PPT等,均可以使用UIWebView打开
只须要告诉UIWebView文件的URL便可
至于打开一个远程的共享资源,好比http协议的,也能够调用系统自带的Safari浏览器:
NSURL *url = [NSURL URLWithString:@”http://www.baidu.com"];
[[UIApplication sharedApplication] openURL:url];
有时候,须要在本应用中打开其余应用,好比从A应用中跳转到B应用
首先,B应用得有本身的URL地址(在Info.plist中配置)
B应用的URL地址就是:mj://ios.itcast.cn
接着在A应用中使用UIApplication完成跳转
NSURL *url = [NSURL URLWithString:@"mj://ios.itcast.cn"];
[[UIApplication sharedApplication] openURL:url];
应用评分
为了提升应用的用户体验,常常须要邀请用户对应用进行评分
应用评分无非就是跳转到AppStore展现本身的应用,而后由用户本身撰写评论
如何跳转到AppStore,而且展现本身的应用
方法1
NSString *appid = @"444934666";
NSString *str = [NSString stringWithFormat:
@"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=%@", appid];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];
方法2
NSString *str = [NSString stringWithFormat:
@"itms-apps://itunes.apple.com/cn/app/id%@?mt=8", appid];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];
关于图片的切割问题
// 8.切割图片,将切割好的图片设置到按钮上
CGFloat imageH = NJImageHeight * [UIScreen mainScreen].scale;
CGFloat imageW = NJImageWidth * [UIScreen mainScreen].scale;
CGFloat imageY = 0;
CGFloat imageX = index * imageW;//设置第几个的图片的X的值
CGRect rect = CGRectMake(imageX, imageY, imageW, imageH);//切割的范围
// 8.1根据rect切割图片
// CGImage中rect是当作像素来使用
// UIKit 中是点坐标系
// 坐标系的特色:若是在非retain屏上 1个点等于1个像素
// 在retain屏上1个点等于2个像素
// 剪切默认状态的图片
CGImageRef norCGImageRef= CGImageCreateWithImageInRect(norImage.CGImage, rect);
// 将切割好的图片转换为uiimage设置为按钮的背景
[btn setImage:[UIImage imageWithCGImage:norCGImageRef] forState:UIControlStateNormal];
// 剪切选中状态图片
CGImageRef selCGImageRef= CGImageCreateWithImageInRect(selImage.CGImage, rect);
// 将切割好的图片转换为uiimage设置为按钮的背景
[btn setImage:[UIImage imageWithCGImage:selCGImageRef] forState:UIControlStateSelected];
CGFloat imageH = NJImageHeight * [UIScreen mainScreen].scale;//坐标转化为像素
CGFloat imageW = NJImageWidth * [UIScreen mainScreen].scale;
[UIScreen mainScreen].scale 是不是Retina屏 这里的返回值是 1 或者 2
转盘的问题
// 建立12个按钮添加到中间的轮盘上
for (int index = 0; index < 12; index++) {
// 1.建立按钮
NJWheelButton *btn = [[NJWheelButton alloc] init];
// btn.backgroundColor = [UIColor redColor];
// 2.设置按钮选中状态的图片
[btn setBackgroundImage:[UIImage imageNamed:@"LuckyRototeSelected"] forState:UIControlStateSelected];
// 3.设置按钮的bounds
btn.bounds = CGRectMake(0, 0, 68, 143);
// 4.设置按钮的锚点
btn.layer.anchorPoint = CGPointMake(0.5, 1);
// 5.设置按钮的position
btn.layer.position = CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5);
// 6然按钮围绕锚点旋转(关键部分 先进行锚点的设置 再进行后面的操做)
// 6.1计算按钮应该旋转的弧度
CGFloat angle = (30 * index)/180.0 * M_PI;
btn.transform = CGAffineTransformMakeRotation(angle);
// 7.监听按钮的点击事件
[btn addTarget:self action:@selector(update:) forControlEvents:UIControlEventTouchUpInside];
// 获取当前是不是retain屏
// NSLog(@"%.1f", [UIScreen mainScreen].scale);
// 8.切割图片,将切割好的图片设置到按钮上
CGFloat imageH = NJImageHeight * [UIScreen mainScreen].scale;//坐标转化为像素
CGFloat imageW = NJImageWidth * [UIScreen mainScreen].scale;
CGFloat imageY = 0;
CGFloat imageX = index * imageW;//设置第几个的图片的X的值
CGRect rect = CGRectMake(imageX, imageY, imageW, imageH);//切割的范围
// 8.1根据rect切割图片
// CGImage中rect是当作像素来使用(这里是关键点)
// UIKit 中是点坐标系 可是在 CGImage中rect是当作像素来使用的 这里所区分的是Retain屏幕的问题
// 坐标系的特色:若是在非retain屏上 1个点等于1个像素
// 在retain屏上1个点等于2个像素
// 剪切默认状态的图片
CGImageRef norCGImageRef= CGImageCreateWithImageInRect(norImage.CGImage, rect);
// 将切割好的图片转换为uiimage设置为按钮的背景(这里必须进行uiimage的转换不然会出现问题)
[btn setImage:[UIImage imageWithCGImage:norCGImageRef] forState:UIControlStateNormal];
// 剪切选中状态图片
CGImageRef selCGImageRef= CGImageCreateWithImageInRect(selImage.CGImage, rect);
// 将切割好的图片转换为uiimage设置为按钮的背景
[btn setImage:[UIImage imageWithCGImage:selCGImageRef] forState:UIControlStateSelected];
// 添加按钮到中间轮盘图片上
[self.centerWheel addSubview:btn];
}
}
//让轮盘上面的按钮的点击事件
- (void)update:(UIButton *)btn
{
self.selectButton.selected = NO;
btn.selected = YES;
self.selectButton = btn;
}
#import <UIKit/UIKit.h>
#define NJImageWidth 40
#define NJImageHeight 47
@interface NJWheelButton : UIButton
@end
#import "NJWheelButton.h"
//自定义了一个按钮上面显示图片的操做 自定义一个button
@implementation NJWheelButton
- (CGRect)imageRectForContentRect:(CGRect)contentRect
{
CGFloat imageX = (contentRect.size.width - NJImageWidth ) * 0.5;
CGFloat imageY = 18;
return CGRectMake(imageX, imageY, NJImageWidth, NJImageHeight);
}
//这里是为了解决刚点击下去的时候 会出现高亮的状态 这里实现的是自定义的高亮的状态 将系统的高亮状态屏蔽掉
- (void)setHighlighted:(BOOL)highlighted
{
}
@end
block
若是在block中有引用那个必定要讲self设置微弱引用 __unsafe_unretained
// __unsafe_unretained NJShareViewController *unsafeSelf = self;
// __weak NJShareViewController *unsafeSelf = self;
// __weak 当对象释放以后会自动设置为nil, 而__unsafe_unretained不会 会出现空指针的错误
// self.age = 10;
// _age = 10;// 注意改行代码会自动变成 self->_age = 10;
之后直接使用如下的操做
__weak typeof(self) unsafeSelf = self;//装逼专用
进程
什么是进程
进程是指在系统中正在运行的一个应用程序
每一个进程之间是独立的,每一个进程均运行在其专用且受保护的内存空间内
好比同时打开QQ、Xcode,系统就会分别启动2个进程
经过“活动监视器”能够查看Mac系统中所开启的进程
什么是线程
1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程)
线程是进程的基本执行单元,一个进程(程序)的全部任务都在线程中执行
好比使用酷狗播放音乐、使用迅雷下载电影,都须要在线程中执行
线程的串行
1个线程中任务的执行是串行的
若是要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务
也就是说,在同一时间内,1个线程只能执行1个任务
好比在1个线程中下载3个文件(分别是文件A、文件B、文件C)
多线程
什么是多线程
1个进程中能够开启多条线程,每条线程能够并行(同时)执行不一样的任务
进程 车间,线程 车间工人
多线程技术能够提升程序的执行效率
好比同时开启3条线程分别下载3个文件(分别是文件A、文件B、文件C)
多线程的原理
多线程的原理
同一时间,CPU只能处理1条线程,只有1条线程在工做(执行)
多线程并发(同时)执行,实际上是CPU快速地在多条线程之间调度(切换)
若是CPU调度线程的时间足够快,就形成了多线程并发执行的假象
思考:若是线程很是很是多,会发生什么状况?
CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源
每条线程被调度执行的频次会下降(线程的执行效率下降)
多线程的优缺点
多线程的优势
能适当提升程序的执行效率
能适当提升资源利用率(CPU、内存利用率)
多线程的缺点
开启线程须要占用必定的内存空间(默认状况下,主线程占用1M,子线程占用512KB),若是开启大量的线程,会占用大量的内存空间,下降程序的性能
线程越多,CPU在调度线程上的开销就越大
程序设计更加复杂:好比线程之间的通讯、多线程的数据共享
多线程在iOS开发中的应用
什么是主线程
一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”
主线程的主要做用
显示\刷新UI界面
处理UI事件(好比点击事件、滚动事件、拖拽事件等)
主线程的使用注意
别将比较耗时的操做放到主线程中
耗时操做会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验
开发中的注意点
/ 凡是函数名种带有create\copy\new\retain等字眼, 都须要在不须要使用这个数据的时候进行release
// GCD的数据类型在ARC环境下不须要再作release
// CF(Core Foundation)的数据类型在ARC环境下仍是须要再作release
在经常使用
数据请求GET方法
//
// ViewController.m
// NetWork 1
//
// Created by Lenny on 3/21/15.
// Copyright (c) 2015 Lenny. All rights reserved.
//
#import "ViewController.h"
#import "MBProgressHUD+MJ.h"
@interface ViewController ()<NSURLConnectionDataDelegate>
@property (weak, nonatomic) IBOutlet UITextField *nameField;
@property (weak, nonatomic) IBOutlet UITextField *pwdField;
- (IBAction)loginBtnClick;
/**
用来存放服务器返回的全部的数据
*/
@property(nonatomic,strong)NSMutableData * responseData;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
/**
登陆逻辑
*/
- (IBAction)loginBtnClick {
NSString *username = self.nameField.text;
NSString * pwd = self.pwdField.text;
if (username.length == 0) {//没有用户名的输入
[MBProgressHUD showError:@"enter the user name"];
return;
}
if (pwd.length == 0) {
[MBProgressHUD showError:@"enter the pwd"];
return;
}
// 弹框正在登陆中....
[MBProgressHUD showMessage:@"正在拼命的为您加载中...."];
// 2.发送请求给服务器(帐户名称和用户密码)
// GET请求:请求行\请求头
// 2.1设置请求路径
NSString * urlStr = [NSString stringWithFormat:@"http://192.168.1.200:8080/MJServer/login?username=%@&pwd=%@",username,pwd];
// 进行转码的操做 若是其中有中文字符 那么将其转走
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// URL里面不能够含有中文
NSURL * url = [NSURL URLWithString:urlStr];
// 2.2建立请求对象
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];//默认就是GET请求的方式
request.timeoutInterval = 10;//设置请求超时
// 发送请求 注意这里的请求是异步请求
[self sendAsync:request];
NSLog(@"请求已经发出");
}
/**
发送异步请求的第一种方式1:类方法 block
*/
-(void)sendAsync:(NSURLRequest *)request
{
// 这里队列使用的是主线程
NSOperationQueue * queue = [NSOperationQueue mainQueue];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {//当请求结束的时候调用(拿到了服务器的数据,请求失败)
// 隐藏HUD(刷新UI界面,必定要放在主线程中使用,不能放在子线程中使用)
[MBProgressHUD hideHUD];
/**
解析data :
{"error":"用户名不存在"}
{"error":"密码不正确"}
{"success":"登陆成功"}
*/
if (data) {//请求成功
NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
NSString * error = dict[@"error"];
if (error) {//表示登陆失败
[MBProgressHUD showError:error];
}else
{
[MBProgressHUD showSuccess:dict[@"success"]];
}
}else{
[MBProgressHUD showError:@"网络繁忙,请稍后重试...."];
}
}];
}
/**
发送异步请求的方式2 :start方法,代理
*/
-(void)sendAsync2:(NSURLRequest *)request
{
NSURLConnection * conn = [NSURLConnection connectionWithRequest:request delegate:self];
[conn start];//异步执行开始
}
#pragma mark -NSURLConnectionDataDelegate
/**
请求错误(失败)的时候调用(请求超时\断网\没有网络,通常是指客户顿的错误)
*/
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(@"connection:didFailWithError");
[MBProgressHUD hideHUD];
[MBProgressHUD showError:@"网络繁忙,请稍后再试"];
}
/**当接受到服务器的响应的时候(联通了服务器)就会调用
*/
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(@"connection:didReceiveResponse");
// 初始化数据 告诉容器 你能够接收数据了 将数据器准备好
self.responseData = [NSMutableData data];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(@"connection:didReceiveData");
// 将接收到数据放入到容器
[self.responseData appendData:data];
}
/**当服务器的数据接受完毕以后就会调用
*/
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(@"connectionDidFinishLoading");
// 隐藏HUD
[MBProgressHUD hideHUD];
// 解析服务器返回来的数据
NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:self.responseData options:NSJSONReadingMutableLeaves error:nil ];
NSString *error = dict[@"error"];
if (error) {//登陆失败
[MBProgressHUD showError:error];
}else{
NSString * success = dict[@"success"];
[MBProgressHUD showSuccess:success];
}
}
@end
POST
//
// ViewController.m
// POST
//
// Created by Lenny on 3/21/15.
// Copyright (c) 2015 Lenny. All rights reserved.
//
#import "ViewController.h"
#import "MBProgressHUD+MJ.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *nameField;
@property (weak, nonatomic) IBOutlet UITextField *pwdField;
- (IBAction)loginBtnClick;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
/**
登陆逻辑
*/
- (IBAction)loginBtnClick {
NSString *username = self.nameField.text;
NSString * pwd = self.pwdField.text;
if (username.length == 0) {//没有用户名的输入
[MBProgressHUD showError:@"enter the user name"];
return;
}
if (pwd.length == 0) {
[MBProgressHUD showError:@"enter the pwd"];
return;
}
// 弹框正在登陆中....
[MBProgressHUD showMessage:@"正在拼命的为您加载中...."];
// 2.发送请求给服务器(帐户名称和用户密码)
// GET请求:请求行\请求头
// 2.1设置请求路径
NSString * urlStr = [NSString stringWithFormat:@"http://192.168.1.200:8080/LKServer/login"];
// 进行转码的操做 若是其中有中文字符 那么将其转走
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// URL里面不能够含有中文
NSURL * url = [NSURL URLWithString:urlStr];
// 2.2建立请求对象
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];//默认就是GET请求的方式
request.HTTPMethod = @"POST";//设置成为POST请求的方式
// 经过请求头告诉服务器 客户端的类型
[request setValue:@"iOS" forKey:@"User-Agent"];
// 设置请求体
NSString * param = [NSString stringWithFormat:@"username=%@&pwd=%@", username, pwd];
request.HTTPBody = [param dataUsingEncoding:NSUTF8StringEncoding];
request.timeoutInterval = 5;//设置请求超时
// 发送请求 注意这里的请求是异步请求
// 发送数据请求
NSOperationQueue * queue = [NSOperationQueue mainQueue];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {// 当请求结束的时候调用 (拿到了服务器的数据, 请求失败)
// 隐藏HUD (刷新UI界面, 必定要放在主线程, 不能放在子线程)
[MBProgressHUD hideHUD];
if (data) {//请求成功
NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
NSString * error = dict[@"error"];
if (error) {//登陆失败
[MBProgressHUD showError:error];
}else
{
[MBProgressHUD showSuccess:dict[@"success"]];
}
}else{
[MBProgressHUD showError:@"网络繁忙,请稍后再试"];
}
}];
}
@end
文件的解压与压缩
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSString * path = [[NSBundle mainBundle]pathForResource:@"guo.zip" ofType:nil];
NSString * despth = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask , YES)lastObject];
NSString *filepath = [despth stringByAppendingPathComponent:@"guo"];
NSLog(@"解压中");
NSLog(@"%@",filepath);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[SSZipArchive unzipFileAtPath:path toDestination:filepath];
});
}
- (void)createZip
{
// [[NSBundle mainBundle] pathForResource:@"minion_01.png" ofType:nil];
// 1.得到mainBundle中全部的png的图片路径
NSArray *pngs = [[NSBundle mainBundle] pathsForResourcesOfType:@"png" inDirectory:nil];
// 2.zip文件路径
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *zipFilepath = [caches stringByAppendingPathComponent:@"pngs.zip"];
// 3.建立zip文件
[SSZipArchive createZipFileAtPath:zipFilepath withFilesAtPaths:pngs];
}
四个容易混淆的属性
四个容易混淆的属性:
1. textAligment : 文字的水平方向的对齐方式
1> 取值
NSTextAlignmentLeft = 0, // 左对齐
NSTextAlignmentCenter = 1, // 居中对齐
NSTextAlignmentRight = 2, // 右对齐
2> 哪些控件有这个属性 : 通常可以显示文字的控件都有这个属性
* UITextField
* UILabel
* UITextView
2. contentVerticalAlignment : 内容的垂直方向的对齐方式
1> 取值
UIControlContentVerticalAlignmentCenter = 0, // 居中对齐
UIControlContentVerticalAlignmentTop = 1, // 顶部对齐
UIControlContentVerticalAlignmentBottom = 2, // 底部对齐
2> 哪些控件有这个属性 : 继承自UIControl的控件或者UIControl自己
* UIControl
* UIButton
* UITextField
* ...
3. contentHorizontalAlignment : 内容的水平方向的对齐方式
1> 取值
UIControlContentHorizontalAlignmentCenter = 0, // 居中对齐
UIControlContentHorizontalAlignmentLeft = 1, // 左对齐
UIControlContentHorizontalAlignmentRight = 2, // 右对齐
2> 哪些控件有这个属性 : 继承自UIControl的控件或者UIControl自己
* UIControl
* UIButton
* UITextField
* ...
4. contentMode : 内容模式(控制内容的对齐方式), 通常对UIImageView颇有用
1> 取值
/**
规律:
1.Scale : 图片会拉伸
2.Aspect : 图片会保持原来的宽高比
*/
// 前3个状况, 图片都会拉伸
// (默认)拉伸图片至填充整个UIImageView(图片的显示尺寸会跟UIImageView的尺寸同样)
UIViewContentModeScaleToFill,
// 按照图片原来的宽高比进行伸缩, 伸缩至适应整个UIImageView(图片的内容不能超出UIImageView的尺寸范围)
UIViewContentModeScaleAspectFit,
// 按照图片原来的宽高比进行伸缩, 伸缩至 图片的宽度和UIImageView的宽度同样 或者 图片的高度和UIImageView的高度同样
UIViewContentModeScaleAspectFill,
// 后面的全部状况, 都会按照图片的原来尺寸显示, 不会进行拉伸
UIViewContentModeRedraw, // 当控件的尺寸改变了, 就会重绘一次(从新调用setNeedsDisplay, 调用drawRect:)
UIViewContentModeCenter,
UIViewContentModeTop,
UIViewContentModeBottom,
UIViewContentModeLeft,
UIViewContentModeRight,
UIViewContentModeTopLeft,
UIViewContentModeTopRight,
UIViewContentModeBottomLeft,
UIViewContentModeBottomRight,
2> 哪些控件有这个属性 : 全部UI控件都有
5. 若是有多个属性的做用冲突了, 只有1个属性有效(就近原则)
item
1、UINavigationItem
1> 得到方式
self.navigationItem // self是指控制器
2> 做用
能够用来设置当前控制器顶部导航栏的内容
// 设置导航栏中间的内容
self.navigationItem.title
self.navigationItem.titleView
2、UIBarButtonItem
1> 用在什么地方
// 设置导航栏左上角的内容
self.navigationItem.leftBarButtonItem
// 设置导航栏右上角的内容
self.navigationItem.rightBarButtonItem
2> 做用
至关于一个按钮
3、UITabBarItem
1> 得到方式
self.tabBarItem // self是指控制器
2> 做用
能够用来设置当前控制器对应的选项卡标签的内容
// 标签的标题
self.tabBarItem.title
// 标签的图标
self.tabBarItem.image
// 标签的选中图标
self.tabBarItem.selectdImage
4、UINavigationBar
1. 导航控制器顶部的栏(UI控件)
2. UINavigationBar上面显示什么内容, 取决于当前控制器的navigationItem属性
3. UINavigationBar是view, navigationItem是model
4. 由navigationItem给UINavigationBar提供显示的数据
5、UITabBar
1. UITabBarController底部的选项卡条
6、UITabBarButton
1. UITabBar底部的每个标签
2. 每个UITabBarButton里面显示什么内容,取决于当前控制器的tabBarItem属性
3. UITabBarButton是view, tabBarItem是model
4. 由tabBarItem给UITabBarButton提供显示的数据