基础概念
相关框架
UIKit 创建管和管理应用程序的用户界面
QUartzCore 提供动画特效以及通过硬件进行渲染的能力
CoreGraphics 提供2D绘制的基于C的API
CoreLocation 使用GPS和WIFI获取位置信息
MapKit 为应用程序提供内嵌地图的接口
AVFoundation 音频处理
Xcode中创建项目时字段说明
Product Name 产品名称
Organization Name 公司名称
Organization Identifier 公司唯一标示,通常为域名的反写,如 com.uiideas
Bundle Identifier 产品唯一标识符,自动生成(公司唯一标识符+产品名称)
Language 使用的语言,包含OC和Swift
Devices 设备,Universal指通用
Use Core Data 封装了SQLite数据库
Include Unit Tests 包含单元测试工具
Include UI Tests 包含UI测试工具
模拟器操作
回到首页
Command + Shift + H
切换物理和模拟键盘
Command + Shift + K
缩放模拟器
Command + 1,2,3,4,5
说放手势
按住option键可开启多指缩放,同时按住shift键可移动手指位置
MVC
UIView
界面由UIView组成,每一个控件都是一个UIView
UIViewController
负责创建,显示,销毁UIView,负责监听UIView的事件
内部有个UIView属性,是它所负责的UIView对象
程序启动流程
1.加载一个storyboard(在Main interface中指定)
2.加载箭头指向的Controller
3.加载Controller中的View展示给用户
IB(Interface Builder)
IBAction
从返回值角度上看,相当于void
只有返回值声明为IBAction的方法,才能和storyboard中的控件进行连线
IBOutlet
只有声明为IBOutlet的属性,才能和storyboard中的控件进行连线
控件
概述
每一个控件启示都是一个容器
可以将其它控件放到该控件的内部
比较常见的还是将UIView作为容器
可以将A控件放入B控件
A控件是B控件的子控件
B控件是A控件的父控件
每一个控制器都有一个UIView
控制器本身是不可见的
能够看到的是控制器的View
每一个控制器都有一个UIView属性
控制器中管理的所有子控件都是该控件的子控件
常见控件
UIImageView 图片显示控件
UISlider 滑块
UIProgressView 进度条
UIButton 按钮
UILable 文本标签
UITextField 文本输入框
UITextView 多行可滚动的输入框
UISwitch 开关
UIDatePicker 日期控件
UIPickerView 选择器控件
UIScollView 滑动控件
UIPageControl 分页控件
UITableView 表格
UICollectionView 九宫格
UIWebView 网页显示控件
UItoolbar 工具条
UISegmentControl 选项卡
UIActivityIndicator 转圈圈
UIAlertView Alert弹框(ios8弃用)
UIActionSheet 底部弹框
UIView
说明
所有UI控件都继承自UIView。
UI控件的共有属性都定义在UIView中,如frame,center
常见属性
superview 父控件
subviews 子控件数组,顺序
tag 每个控件的标识,可通过tag查找子控件,不同的空间tag可相同
transform 控件形变属性(可设置旋转角度,比例缩放,平移等)
frame 控件所在矩形框在父控件中的位置和尺寸(父控件左上角为原点)
bounds 控件所在矩形框的位置和尺寸(以自己左上角为原点,通常xy为0)
center 控件中心点的位置(父控件左上角为原点)
常见方法
addSubview 添加子控件
removeFromSuperview 把自己从父控件中移除
viewWithTag 根据tag找出对应的控件(一般是子控件)
UIlabel
常见属性
text 显示的文字
font 字体
textColor 文字颜色
textAlignment 对齐方式
numbersOfLines 文字行数
lineBreakMode 换行模式
UIImageView
加载图片
两种方式
imageName
指向它的指针被销毁,该资源不会从内存中干掉
imageWithContentOfFile
指向它的指针被销毁,该资源会从内存中干掉
Assets.xcasset中的图片
打包后成为Assets.car
无法拿到路径
只能通过imageName方式加载
无法通过imageWithContentOfFile方式获取图片
图片默认有缓存,通常放置经常使用的资源
放置项目目录下
可以通过NSBundle获取路径
可以通过imageName方式获取图片
也可以通过imageWithContentOfFile方式获取图片
图片无缓存,通常放置不经常用的图片和占用空间较大的资源
图片拉伸
resizableImageWithCapInsets
说明
对象方法,需要指定上下左右保护区域的大小
返回一个四周区域收保护的图片
stretchableImageWithLeftCapWidth
说明
对象方法,需要指定上面和左边受保护区域的大小
右边和下边制动根据左边和上边减1
返回一个指定区域受到保护的图片
UIButton
说明
按钮,既能显示文字,也可以显示图片
按钮状态
normal 默认状态
highlighted 高亮状态,当手按上去还没有抬起时的状态
disable 失效状态
修改按钮内图片和文字位置
方法一
//新建继承UIButton的类,重写如下两个方法
#import "customButton.h"
@implementation customButton
-(CGRect)titleRectForContentRect:(CGRect)contentRect{
return CGRectMake(0, 0, 40, 40);
}
-(CGRect)imageRectForContentRect:(CGRect)contentRect{
return CGRectMake(0, 50, 40, 40);
}
@end
方式二
//重写layoutSubviews方法
#import "customButton.h"
@implementation customButton
-(void)layoutSubviews{
[super layoutSubviews];
self.titleLabel.frame = CGRectMake(0, 0, 100, 40);
self.imageView.frame = CGRectMake(100, 0, 40, 40);
}
@end
设置内边距
button.imageEdgeInsets 设置图片内边距
button.contentEdgeInsets 设置内容内边距
button.titleEdgeInsets 设置文字内边距
Xib & storyboard
对比
共同点
都用来描述软件界面
都用Interface Builder工具来编辑
本质都是转换成代码去创建控件
不同点
Xib是轻量级的,用来描述局部的UI界面
Storyboard是重量级的,用来描述整个软件的多个界面,并可展示多个界面之间的跳 转关系
Xib的加载
说明
xib的包文件扩展名nib,可用Nib的方式加载
xib的加载后返回一个数组,因为xib内可能有多个文件
当xib和class关联后,可把加载xib的逻辑放在view中
View从xib中加载注意
xib无法通过 alloc init 和 initWithFrame 的方式创建
不会调用init和initWithFrame方法
如果子控件是从xib中创建的,是处于未唤醒状态
在view中用代码添加子控件,需在initWithCoder和awakeFromNib中创建
方法一
NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"xib文件名" owner:nil options:nil]
方法二
UINib *nib = [UINib nibWithNibName:@"xib文件名" bundle:nil]; NSArray *views = [nib instantiateWithOwner:nil options:nil];
相关方法
awakeFromNib 当xib被加载时调用,在XXXView.m文件中有效
渐变动画
方式一
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
//动画相关代码
[UIView commitAnimations];
方式二
[UIView animateWithDuration:2.0 animations:^{
//code
}];
方式三
[UIView animateWithDuration:(NSTimeInterval) animations:^{
//code
} completion:^(BOOL finished) {
//code
}];
方式四
[UIView animateWithDuration:(NSTimeInterval) delay:(NSTimeInterval) options:(UIViewAnimationOptions) animations:^{
//code
} completion:^(BOOL finished) {
//code
}]
UIScrollView
说明
一个能够滚动的视图控件,可以用来展示大量内容,可通过滚动查看所有内容
超出UIScrollView的内容会被隐藏
UIScrollView的subviews中出了内容还包含两个滚动条的UIImageView
常见属性
contentSize 滚动区域的范围
contentOffset 内容显示的偏移量
contentInset 内容的内边距,设置后默认偏移量不包含内边距
scrollEnabled 是否可滚动,默认YES
userInteractionEnabled 是否与用户交互相应用户操作,默认YES
bounces 滚动到边界的弹簧效果,默认YES
alwaysBounceVertical 无论内容区域大小,强制垂直方向弹簧效果,默认NO
alwaysBounceHorizontal 无论内容区域大小,强制水平方向弹簧效果,默认NO
pagingEnabled 开启分页功能,以UIScrollView的宽度为分页标准
showsHorizontalScrollIndicator 显示水平滚动条,默认YES
showsVerticalScrollIndicator 显示垂直滚动条,默认YES
代理(delegate)
说明
当UIScrollView发生滚动时,会自动通知它的delegate对象,给代理发送消息
如果想要监听UIScrollView的滚动操作,需先给UIScrollView设置个代理对象
通过代理获取UIScrollView的滚动过程
代理对象需遵守UIScrollViewDelegate协议
当发生特定操作时,会调用delegate对象的特定方法
步骤
1.通过UIScrollView的delegate属性指定代理对象
2.代理对象遵守UIScrollViewDelegate协议(可在类扩展中遵守)
3.在代理对象中设置特定行为事件对应的代理方法
注意
任何OC都可作为UIScrollView的代理,一般是控制器
当指定的代理对象是一个新独立文件的类时,需在.h中遵守协议
UIScrollView对代理对象的引用为弱引用,指定的代理对象需被强引用
常见代理方法
scrollViewDidScroll 滚动时调用
scrollViewDidZoom 缩放时调用
scrollViewWillBeginDragging 即将开始拖拽时调用
scrollViewWillEndDragging 即将结束拖拽时调用
scrollViewDidEndDragging 停止拖拽时调用
scrollViewWillBeginDecelerating 开始减速时候调用
scrollViewDidEndDecelerating 减速完毕停止滚动时调用
常见控件事件监听
当控件继承自UIControl,可通过addTarget监听事件
点击控制器的view时系统会自动调用控制器的touchesBegan方法
内容缩放
设置viewForZoomingInScrollView代理方法,返回需要缩放的UIView
设置UIScrollView的maximumZoomScale或minimumZoomScale属性设置说放比例
UIPageControl
说明
提供分页功能
属性
pageIndicatorTintColor 圆点的颜色
currentPageIndicatorTintColor 当前页的圆点颜色
numberOfPages 一共的页码
currentPage 当前页码
hidesForSinglePage 当页面只有一页时隐藏
设置图标
通过KVC设置current和other两个属性
UITableView
说明
表格控件,继承至UIScrollView。
UITableView的每一行都是一个UITableViewCell。
代理需遵守UITableViewDelegate协议
UITableView样式
UITableViewStylePlain 每个cell紧密贴在一起,滑动时标题固定顶部
UITableViewStyleGrouped 每个分组间有间距
dataSource
UITableView的数据源,指定后通过协议方法定义显示的内容
作为dataSource的对象需遵守UITableViewDataSource协议
常见属性
rowHeight 行高,默认44
sectionFooterHeight 每一组的头部高度
sectionFooterHeight 每一组的尾部高度
sectionIndexColor 索引条文字颜色
sectionIndexBackgroundColor 索引条背景颜色
estimatedRowHeight 设置估算高度
editing 编辑模式,cell左滑后editing值为YES
indexPathsForSelectedRows 当前用户选中的cell的索引
allowsMultipleSelectionDuringEditing 编辑模式下可以多选
方法
reloadData 刷新列表,重新调用数据源方法
reloadRowsAtIndexPaths 刷新列表中局部数据(必须保证数组个数不变)
insertRowsAtIndexPaths 插入一条cell
deleteRowsAtIndexPaths 删除一条cell
setEditing 修改编辑模式,另有带动画的设置方法
dataSource协议方法
numberOfSectionsInTableView 共有多少个数据组,不实现默认1组
numberOfRowsInSection 设置每个组有多少行数据
cellForRowAtIndexPath 设置每一行的数据
titleForHeaderInSection 每组的头部标题
titleForFooterInSection 每组的尾部标题
separatorColor 分割线颜色
separatorStyle 分割线样式
tableHeaderView 整张表的头部控件
tableFooterView 整张表的尾部控件
sectionIndexTitlesForTableView 显示索引条
代理方法
didSelectRowAtIndexPath 选中某一行调用的方法
didDeselectRowAtIndexPath 取消选中某一行调用的方法
heightForHeaderInSection 可设置不同的组不同头部高度
heightForFooterInSection 可设置不同的组不同尾部高度
heightForRowAtIndexPath 可设置每个cell不同的行高
viewForHeaderInSection 返回每组的尾部控件
viewForFooterInSection 返回每组的头部控件
commitEditingStyle 增加左滑删除功能
editActionsForRowAtIndexPath 增加多按钮的左滑功能
titleForDeleteConfirmationButtonForRowAtIndexPath 修改左滑动删除文字
性能优化
取消表格中没有数据行的分割线,两种方式:
1. 修改表格样式为UITableViewStyleGrouped
2. tableView.tableFooterView = [[UIView alloc]init];
cell懒加载
苹果的方式
每当一个cell即将进入视野范围内,才调用方法创建cell
当一个cell离开视野后,再次进入视野时,会重新创建cell
这样会频繁的创建和销毁cell,分配内存很耗性能
优化思路
当存在dequeueReusableCellWithIdentifier时,
离开视野的cell,系统会自动放入缓存池中。
当缓存池内的cell在此进入视野内,直接从缓存池读取而不重新创建
范例
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *ID = @"idname";
//去缓存池中取是否有指定标识的cell
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:ID];
if(cell == nil){
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
//TODO 写入不同的cell都相同的设置
}
//TODO 写入不同的cell不同的设置
cell.textLabel.text = @"测试";
return cell;
缓存池注册class的另外两个方法
[self.tableView registerClass:[CustomCell class] forCellReuseIdentifier:ID];
[self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([CustomCell class]) bundle:nil] forCellReuseIdentifier:ID];
可变高度Cell(Self Sizing Cells)
// 告诉tableView所有cell的真实高度是自动计算的(根据设置的约束)
self.tableView.rowHeight = UITableViewAutomaticDimension;
// 设置估算高度,和上面代码结合使用会开启IOS8之后的Self Sizing Cells
self.tableView.estimatedRowHeight = 44;
heightForRowAtIndexPath调用次数
系统展示tableView时需计算contentSize的和滚动条的大小。
此时会多次调用heightForRowAtIndexPath代理方法。
设置估算高度estimatedRowHeight来优化性能。
减少heightForRowAtIndexPath的调用
UITableViewCell
说明
UITableView的每一行都是一个UITableViewCell。
Cellyou两种类型,Dynamic和Static。
Dynamic为显示数量和内容依照数据源来决定的。
Static的数量和内容是在storyboard中固定的。
样式
UITableViewCellStyleDefault 默认样式,没有描述
UITableViewCellStyleValue1 描述在下面
UITableViewCellStyleValue2 描述在右边
UITableViewCellStyleSubtitle 描述较大,没有图片
常见属性
imageView contentView的子控件,左侧的图片
textLabel contentView的子控件,文字
detailTextLabel contentView的子控件,描述
contentView UITableViewCell的子控件
accessoryType cell右边图标样式
accessoryView 可自定义右边显示的控件
selectionStyle 选中时的样式
selectedBackgroundView 设置选中时的背景View
backgroundColor 背景颜色
backgroundView 可给背景设置控件
UITextView
说明
文本输入框控件
代理需遵守UITextFieldDelegate协议
代理方法
textViewShouldBeginEditing 是否允许开始编辑
textViewShouldEndEditing 是否允许结束编辑
textViewDidBeginEditing 开始编辑时调用
textViewDidEndEditing 结束编辑时调用
shouldChangeTextInRange 是否允许改变文本框内容
UIPickView
说明
滑动选择控件
数据源需遵守UIPickerViewDataSource协议
代理需遵守UIPickerViewDelegate协议
常见方法
selectRow 选中指定的列和行
reloadComponent 刷新指定的列
selectedRowInComponent 返回指定的列选中的行
协议方法
numberOfComponentsInPickerView 返回列数
numberOfRowsInComponent 返回没一列的行数
代理方法
widthForComponent 返回每一列的宽度
rowHeightForComponent 每一列的高度
titleForRow 设置每一行的内容
attributedTitleForRow 设置每一条数据的大小颜色阴影描边
viewForRow 设置每条数据显示的view
didSelectRow 当前选中的列和行
UIWebView
说明
UIWebView是iOS内置的浏览器控件
系统自带的Safari浏览器就是通过UIWebView实现的
UIWebView不但可以加载网页,还能加载多数常见文件
使用代理方法需遵守UIWebViewDelegate协议
对象方法
loadRequest 加载资源
reload 重新加载(刷新)
stopLoading 停止加载
goBack 后退
goForward 前进
scalesPageToFit 是否对网页进行自动缩放
dataDetectorTypes 数据自动识别,包括电话号码,URL,地址等
canGoBack 是否可以后退
canGoForward 是否可以前进
代理方法
shouldStartLoadWithRequest 即将开始加载request时调用
webViewDidStartLoad 开始加载网页时调用
webViewDidFinishLoad 网页加载完成时调用
didFailLoadWithError 网页加载失败时调用
范例
加载网页
//初始化webView
NSURL *url = [NSURL URLWithString:@"https://m.taobao.com/#index"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[self.webView loadRequest:request];
//设置边距
self.webView.scrollView.contentInset = UIEdgeInsetsMake(20, 20, 20, 20);
加载本地文件
NSURL *url = [NSURL fileURLWithPath:@"/user/xxx/xxx"];
[self.webView loadRequest:[NSURLRequest requestWithURL:url]];
控制器
多控制器
说明
一个iOS的app很少只有一个控制器组成,除非这个App非常简单
当App有多个控制器时,我们需要对这些控制器进行管理
可以用1个控制器去管理多个控制器
为了便于管理控制器,iOS提供了两个比较特殊的控制器:
UINavigationController 和 UITabBarController
UINavigationController
说明
导航控制器
利用UINavigationController可轻松管理多个控制器
轻松完成多个控制器的切换,如系统自带的 "设置"
创建控制器时包含了:导航条,栈定控制器的view
使用步骤
初始化UINavigationController
设置UIWindow的rootViewController为UINavigationController
根据具体情况,通过push方法添加对应个数的子控制器
属性
viewControllers 子控制器的数组
childViewControllers 子控制器的数组(只读)
方法
pushViewController 添加子控制器至栈中
popViewControllerAnimated 将栈顶的控制器移除
popToViewController 回到指定的控制器
popToRootViewControllerAnimated 回到根控制器
appearance 获取导航条标识
栈顶控制器(当前显示的控制器)
navigationItem属性
backBarButtonItem 左上角的返回按钮
titleView 中间的标题视图
title 中间的标题文字
eftBarButtonItem 左上角的视图
rightBarButtonItem 右上角的视图
范例
设置标题
self.navigationItem.title = @"设置标题";
设置标题试图
self.navigationItem.titleView = [UIButton buttonWithType:UIButtonTypeInfoDark];
设置左侧按钮
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"左按钮" style:0 target:self action:@selector(leftButton)];
UITabBarController
说明
标签控制器。
控制器中在底部有一个UITabBar导航条。
有多少个子控制器,UITabBar中就有多少个UITabBarButton。
UITabBarController可以轻松管理多个控制器。
添加的多个控制器对平分屏幕底下的UITabBar导航条。
当前选中的控制器的tabBarItem属性可设置UITabBarButton显示内容
使用步骤
初始化UITabBarController
设置UIWindow的rootViewController为UITabBarController
根据具体情况,通过addChildViewController方法添加对应个数的子控制器
常见属性
viewControllers 子控制器的数组
childViewControllers 子控制器的数组(只读)
selectedIndex 通过索引指定选中显示的子控制器
常见方法
addChildViewController 添加子控制器
当前控制器设置UITabBarButton
vc.tabBarItem.title 设置标题
vc.tabBarItem.badgeValue 设置提示数字
vc.tabBarItem.image 设置图片
Controller生命周期
viewDidLoad 当控制器加载完毕时调用
viewWillAppear 当控制器View即将显示时调用
viewDidAppear 当控制器View显示完毕时调用
viewWillLayoutSubviews 当控制器View将要布局子控件时调用
viewDidLayoutSubviews 当控制器View布局子控件完毕时调用
viewWillDisappear 当控制器View即将消失时调用
viewDidDisappear 当控制器View消失完毕时调用
UITableViewController
说明
继承自UIViewController。
内部引用了一个UITableView。
内部view属性和tableView属性指向同一个UITableView。
遵守了UITableViewDelegate和UITableViewDataSource协议
内部UITableView的dataSource和delegate指向当前UITableViewController
属性
automaticallyAdjustsScrollViewInsets 是否自动设置偏移量
UIAlertController
说明
弹出框
弹框样式
UIAlertControllerStyleActionSheet 屏幕底部上拉弹出样式
UIAlertControllerStyleAlert 弹框样式
按钮样式
UIAlertActionStyleDefault 默认样式(蓝色)
UIAlertActionStyleCancel 取消样式(蓝色,单独显示)
UIAlertActionStyleDestructive 危险操作(红色)
使用步骤
1. 创建控制器(指定样式和标题)
2. 创建按钮
3. 添加按钮
4. 显示弹框
范例
//创建控制器
UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"确定要退出嘛?" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
//创建按钮
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"点击了取消");
}];
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"点击了确认");
}];
//添加按钮
[alertVC addAction:confirmAction];
[alertVC addAction:cancelAction];
//显示弹框
[self presentViewController:alertVC animated:YES completion:nil];
事件
响应者对象
在iOS中不是任何对象都能处理事件。
只有继承了UIReaponder的对象才能接受并处理事件,我们称之为响应者对象。
UIApplication,UIViewController,UIView都继承自UIReaponder。
触摸事件 (Multitouch Event)
UITouch
说明
当用户一根手指触摸屏幕时,会创建一个与手指相关联的UITouch对象。
一个手指对应一个UITouch对象。
UITouch保存着喝手指相关的信息,比如触摸的位置,时间,阶段。
当手指移动时,系统会更新同一个UITouch对象。
属性
window 触摸产生时的窗口
view 触摸产生时所在的视图
tapCount 短时间内触摸的次数
timestamp 触摸事件的时间,单位是秒
phase 触摸事件的状态
方法
locationInView 触摸的位置
previousLocationInView 上一个触摸点的位置
UIEvent
说明
UIEvent称为事件对象,记录产生事件的时间和类型
每一个事件都会产生一个UIEvent对象
属性
type 事件类型
subtype
timestamp 事件产生的时间
事件传递
说明
事件对列为堆的结构
触摸事件的传递是从父控件传递至子控件的。
事件处理过程
当发生触摸事件,系统会将该事件加入到一个由UIApplication管理的事件队列中
UIApplication会从事件队列中取出最前面的事件,并将事件分发下去处理。
通常先发送事件给应用程序的主窗口(Window)
主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件。
找到合适的视图控件后,就会调用视图控件的touches方法来做具体的事件处理。
如果touches方法未实现,事件会顺着响应者链条向上传递
响应者链条
是由多个响应者对象链接起来的链条
作用是能清楚的看见每个响应者之间的关系,并且让一个事件多个处理对象
如果当前view是控制器的view,那么他的上一个响应者是控制器
如果当前view不是控制器的view,那么他的上一个响应者是父控件
Window的上一个响应者是Application
无法接受事件
说明
如果父控件不能接受事件,则子控件不能接受事件。
UIImageView默认userInteractionEnabled为NO,无法接受事件。
如果子控件无法接受事件。则父控件相应事件
当一个控件隐藏时,它的子控件也随之隐藏。
当一个控件透明时,它的子控件也随之透明。
三种情况
userInteractionEnabled = NO; 不接收用户交互
hidden = YES; 隐藏
alpha = 0.0 ~ 0.01 透明
hitTest
系统调用hitTest方法用来寻找最适合的view。
当一个事件传递给当前view时调用。
可重写此方法来直接指定相应事件的view。
pointInside
系统调用用来判断当前点是否在当前View上面
是在系统调用hitTest方法中调用的
point点必须要和它方法调用者在同一个坐标系里面
事件方法(系统自动调用)
touchesBegan 开始触摸
touchesMoved 触摸时移动
touchesEnded 结束触摸
touchesCancelled 触摸时被其他系统事件打断(如电话,关机)
UIGeStureRecognizer
说明
要完成手势识别,必须借助手势识别器-UIGeStureRecognizer
利用UIGeStureRecognizer,能轻松识别用户在某个View上面的常见手势
UIGeStureRecognizer是一个抽象类,定义了所有手势的基本行为。
使用UIGeStureRecognizer的子类才可以处理具体的手势
可设置代理,需遵守UIGestureRecognizerDelegate协议
子类
UITapGestureRecognizer tap手势
UIPinchGestureRecognizer 捏合,用于缩放
UIPanGestureRecognizer 拖拽
UISwipeGestureRecognizer 轻扫
UIRotationGestureRecognizer 旋转
UILongPressGestureRecognizer 长按(移动时会持续调用方法)
代理方法
shouldReceiveTouch 是否允许接受手指
shouldRecognizeSimultaneouslyWithGestureRecognizer 是否允许同时支持多个手势
范例
- (void)viewDidLoad {
[super viewDidLoad];
//创建手势识别器
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)];
//添加手势
[self.image addGestureRecognizer:tap];
}
//实现手势方法
-(void)tap:(UITapGestureRecognizer *)tap{
NSLog(@"%s",__func__);
}
加速计事件 (Accelerometer Event)
远程控制事件 (Remote Control Event)
绘图
Quartz2D
说明
Quartz2D是一个二维绘图引擎,同时支持iOS和Mac系统
可用于绘制图形,剪切图片,制作报表等
功能
绘制图形: 线条\三角形\矩形\圆\弧
绘制文字
绘制\生成图片
读取\生成PDF
截图\裁剪图片
自定义UI控件
图形上下文
说明
图形上下文(Graphies Context): 是一个CGContextRef类型的数据
相同的一套绘图序列,指定不同的上下文,可将相同的图像绘制到不同的目标
作用
可以保存绘图信息和绘图状态
决定绘制的输出目标(绘制到什么地方去)
类型
Bitmap Graphies Context
PDF Graphies Context
Window Graphies Context
Layer Graphies Context
Printer Graphies Context
图片上下文
图片上下文需要手动开启
开启的上下文的大小就是生成图片的大小
手动开启的上下文需要手动关闭
生成图片的代码不需要写在drawRect中,可写在任何地方
自定义view步骤
新建一个类,继承自UIView
实现 drawRect 方法,这个方法内已自动创建个和当前view相关联的上下文
获取上下文
绘制相应的图形内容
利用图形上下文将绘制的所有内容渲染显示到view上面
UIGraphics方法
UIGraphicsGetCurrentContext 获取上下文
UIGraphicsBeginImageContext 开启图片上下文
UIGraphicsEndImageContext 关闭图片上下文
UIGraphicsGetImageFromCurrentImageContext 从当前上下文生成图片
CGContext方法
CGContextAddPath 把绘制的路径添加到上下文
CGContextSetLineWidth 设置线宽
CGContextSetLineJoin 设置线的链接方式
CGContextSetLineCap 设置线的顶端样式
CGContextFillPath 填充
CGContextStrokePath 描边
CGContextSaveGState 将当前状态保存自上下文状态栈
CGContextRestoreGState 从上下文状态栈中取出状态
CGContextTranslateCTM 对上下文路径平移
CGContextRotateCTM 对上下文路径旋转
CGContextScaleCTM 对上下文路径缩放
CGContextClearRect 擦除上下文中指定区域
其他方法
setNeedsDisplay 重新绘制,当前view的方法
renderInContext layer的方法,把内容渲染至上下文
UIBezierPath
对象方法
fill 填充(自动获取上下文)
stroke 描边(自动获取上下文)
closePath 关闭路径(用线连接终点和起点)
sffClip 设置裁剪区域,超出路径范围外的被裁减
removeAllPoints 删除路径上所有的点
类方法
addQuadCurveToPoint 绘制直线和曲线
bezierPathWithRect 绘制矩形
bezierPathWithRoundedRect 绘制圆角矩形
bezierPathWithArcCenter 绘制弧
属性
lineWidth 线宽
UIKit绘图
String对象方法
drawAtPoint 在指定的起点绘制文字
drawInRect 在指定的rect内绘制文字
UIImage对象方法
drawAtPoint 在指定坐标点绘制图片(原始大小)
drawInRect 把图片绘制到指定的区域内(缩放)
drawAsPatternInRect 以平铺的方式在指定的区域绘图
其他方法
UIRectClip 对指定区域裁剪
UIRectFill 对指定区域填充
图片生成文件
UIImageJPEGRepresentation 把图片生成JPGE文件,可指定压缩
UIImagePNGRepresentation 把图片生成PNG文件
范例
绘制直线
- (void)drawRect:(CGRect)rect {
//获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//创建路径
UIBezierPath *path = [UIBezierPath bezierPath];
//设置起点
[path moveToPoint:CGPointMake(100, 50)];
//添加一根线到终点
[path addLineToPoint:CGPointMake(250, 250)];
//以上次终点为起点绘制第二条线
[path addLineToPoint:CGPointMake(50, 150)];
//设置颜色
[[UIColor redColor] set];
//设置线宽
CGContextSetLineWidth(ctx, 10.0);
//设置线的链接方式为圆角
CGContextSetLineJoin(ctx, kCGLineJoinRound);
//设置线的顶端样式为圆角
CGContextSetLineCap(ctx, kCGLineCapRound);
//把绘制的路径保存到上下文
CGContextAddPath(ctx, path.CGPath);
//把上下文内容显示到View上
CGContextStrokePath(ctx);
}
绘制曲线
- (void)drawRect:(CGRect)rect {
//获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//创建路径
UIBezierPath *path = [UIBezierPath bezierPath];
//设置起点
[path moveToPoint:CGPointMake(100, 50)];
//添加一根曲线到终点
[path addQuadCurveToPoint:CGPointMake(250, 250) controlPoint:CGPointMake(50, 250)];
//设置线宽
CGContextSetLineWidth(ctx, 5.0);
//把绘制的路径保存到上下文
CGContextAddPath(ctx, path.CGPath);
//把上下文内容显示到View上
CGContextStrokePath(ctx);
}
绘制矩形
- (void)drawRect:(CGRect)rect {
//获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//设置颜色
[[UIColor blueColor] set];
//绘制矩形
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(50, 50, 200, 200)];
//把绘制的路径保存到上下文
CGContextAddPath(ctx, path.CGPath);
//填充
CGContextFillPath(ctx);
}
绘制圆角矩形
- (void)drawRect:(CGRect)rect {
//获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//设置颜色
[[UIColor blueColor] set];
//绘制矩形
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 200, 200) cornerRadius:50];
//把绘制的路径保存到上下文
CGContextAddPath(ctx, path.CGPath);
//填充
CGContextFillPath(ctx);
}
绘制椭圆
- (void)drawRect:(CGRect)rect {
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 100, 200)];
[path fill];
}
通过图片上下文给图片加水印
//加载图片
UIImage *image = [UIImage imageNamed:@"黄人"];
//开启一个透明的图片上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0);
//绘制图片
[image drawAtPoint:CGPointZero];
//绘制文字
NSString *str = @"水印";
[str drawAtPoint:CGPointMake(10, 20) withAttributes:nil];
//从上下文中生成图片
UIImage *newImg = UIGraphicsGetImageFromCurrentImageContext();
//关闭上下文
UIGraphicsEndImageContext();
//给imageView赋值
self.imageView = newImg;
CALayer
说明
UIView之所以能显示在屏幕上,完全是因为它内部的一个图层
在创建UIView对象时,UIView内部都会自动创建一个图层(CALayer对象)
通过UIView的layer属性可以访问这个层
UIView需要显示到屏幕上时,会调用drawRect方法进行绘图,并将内容绘制在图层上
绘图完成后,系统会将图层拷贝到屏幕上,于是就完成了UIView的显示
UIView本身并不具备显示功能,是其内部的layer具有显示功能
通过CALayer对象,可调整UIView的阴影,圆角,边框等功能
UIView的图片会放置在它的layer的contents属性中
CALayer和UIView的应用区别
CALayer定义QuartzCore框架中
CGImageRef和CGColorRef定义在CoreGraphies框架中
UIColor和UIImage定制在UIKit框架中
QuartzCore和CGColorRef支持跨平台,iOS 和 Mac OS X 上都可以使用
UIKit只能使用在iOS中
为了保证可移植性QuartzCore不能使用UIColor,只能使用CGColorRef等
CALayer和UIView的选择
通过CALayer可实现和UIView一样的界面效果
UIView相比CALayer有事件处理功能。
展示的内容如果需和用户交互使用UIView,否则两者皆可。
相比UIView,CALayer的性能更好更加轻量。
为了程序的扩展性,更推荐使用UIView。
隐式动画
系统对非根层属性修改时,默认产生的动画效果称为隐式动画。
UIView内部默认关联的CALayer成为根层(Root Layer)。
手动创建的CALayer对象称为非根层。非根层都存在隐式动画。
会产生动画的属性称为 Animatable properties(可动画属性)。
常见如: bounds, backgroundColor, position 等。
可通过动画事务(CATransaction)关闭默认的隐式动画效果
属性
shadowColor 阴影颜色
shadowOpacity 阴影不透明度
shadowOffset 阴影偏移
shadowRadius 设置阴影模糊半径
borderWidth 边框宽度,默认边框向内
borderColor 边框颜色
cornerRadius 设置圆角半径
masksToBounds 把超过根层之外的东西裁剪掉
transform 形变属性,类型为CATransform3D
contents 存档图片,类型为CGImage
position 设置CALayer在父层的位置
anchorPoint 定位点,决定层中那个点为position的位置
范例
自定义layer
CALayer *layer = [CALayer layer];
layer.backgroundColor = [UIColor blueColor].CGColor;
layer.frame = CGRectMake(100, 200, 200, 200);
[self.view.layer addSublayer:layer];
取消非根层隐式动画
[CATransaction setDisableActions:YES];
self.layer.position = CGPointMake(300, 300);
设置隐式动画时长
[CATransaction setAnimationDuration:2.0];
self.layer.position = CGPointMake(300, 300);
CAGradientLayer
说明
渐变层,继承来CALayer
对象属性
colors 渐变颜色的数组
locations 渐变位置的数组,和colors对应
startPoint 渐变起始点,可用来确定渐变方向
endPoint 渐变结束点,可用来确定渐变方向
opacity 不透明度
范例
CAGradientLayer *gradientL = [CAGradientLayer layer];
gradientL.frame = self.view.bounds;
gradientL.colors = @[(id)[UIColor grayColor].CGColor,(id)[UIColor redColor].CGColor];
gradientL.startPoint = CGPointMake(0, 0.5);
gradientL.endPoint = CGPointMake(1, 0.5);
gradientL.locations = @[@0.3,@0.8];
[self.view.layer addSublayer:gradientL];
CAReplicatorLayer
说明
复制层,可对其内部子层做复制
对象属性
instanceCount 包括自己复制的分数
instanceTransform 对复制的子层做形变
instanceDelay 复制出来的子层的动画延时执行时长
instanceRedOffset 复制出来的子层的红色偏移
instanceGreenOffset 复制出来的子层的绿色偏移
instanceBlueOffset 复制出来的子层的蓝色偏移
instanceAlphaOffset 复制出来的子层的Alpha偏移
CAShapeLayer
说明
形状图层,可根据指定的路径生成形状图层
对象属性
path 生成形状路径
fillColor 形状填充颜色
动画
Core Animation
说明
Core Animation 是一组非常强大的处理动力动画的API。
支持跨平台,可用在 Mac OS X 和 iOS 平台。
动画执行过程是在后台操作,不会阻塞主线程。
直接作用在CALayer上,并非UIView上。
核心动画和UIView动画区别
核心动画只作用在CALayer上
核心动画看到的都是假象,不会修改UIView的真实位置
当不需要与用户进行交互时使用核心动画
当根据路径做动画时使用核心动画
转场动画使用核心动画(转场类型较多)
相关类说明
CAAnimation 基类
CAAnimationGroup 动画组,可以做一组动画
CABasicAnimation 基础动画,单个值变更的动画
CAKeyframeAnimation 多个值变更的动画
CATransition 转场动画
使用步骤
1. 需要有CALayer
2. 初始化上一个CAAnimation对象,并设置一些动画相关属性
3. 通过调用CALayer的addAnimation方法,增加CAAnimation对象到CALayer
对象属性
keyPath 需要设置动画的属性
toValue 属性变化的值
values CAKeyframe的属性变化的值的数组
path CAKeyframe的动画路径
type CATransition的动画效果类型
startProgress 设置CATransition动画的开始位置
endProgress 设置CATransition动画的结束位置
repeatCount 动画重复的次数
duration 动画的时长
autoreverses 动画自动反转
removedOnCompletion 动画完成后不自动删除动画
fillMode 动画完成后的状态
代理方法
animationDidStart 动画开始时执行
animationDidStop 动画完成时执行
范例
CABasicAnimation
//创建动画对象
CABasicAnimation *anim = [CABasicAnimation animation];
//设置属性和值
anim.keyPath = @"position.x";
anim.toValue = @300;
//动画完成后 不自动删除动画
anim.removedOnCompletion = NO;
//指定动画完成后的状态
anim.fillMode = kCAFillModeForwards;
//添加动画
[self.subView.layer addAnimation:anim forKey:@"x"];
CAKeyframeAnimation
#define angle2Rad(angle) ((angle) / 180.0 * M_PI)
CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
anim.keyPath = @"transform.rotation";
anim.values = @[@(angle2Rad(-5)),@(angle2Rad(5)),@(angle2Rad(-5))];
anim.repeatCount = MAXFLOAT;
anim.duration = 0.2;
[self.imageView.layer addAnimation:anim forKey:nil];
Modal
说明
除了push之外,还有另外一种控制器的切换方式,就是Modal
任何控制器都能通过Modal的形式展示出来
Modal的默认效果:新控制器从屏幕底部上拉,直到覆盖之前的控制器
方法
presentViewController 以Modal的形式展示控制器
dismissViewControllerAnimated 关闭当初Modal出来的控制器
transform
属性
transform
方法
CGAffineTransformMakeTranslation 相对于原始位置平移
CGAffineTransformTranslate 相对于上次位置平移
CGAffineTransformMakeRotation 相对于原始位置旋转
CGAffineTransformRotate 相对于上次位置旋转
CGAffineTransformMakeScale 相对于原始位置缩放
CGAffineTransformScale 相对于上次位置缩放
范例
相对于上次位置平移
[UIView animateWithDuration:0.3 animations:^{
self.image.transform = CGAffineTransformTranslate(self.image.transform, 0, -50);
}];
相对于上次位置旋转45度
[UIView animateWithDuration:0.3 animations:^{
self.image.transform = CGAffineTransformRotate(self.image.transform, M_PI_4);
}];
相对于上次位置放大1.2倍
[UIView animateWithDuration:0.3 animations:^{
self.image.transform = CGAffineTransformScale(self.image.transform, 1.2, 1.2);
}];
屏幕适配
Autoresizing
说明
可再storyboard或代码中设置控件相对于父控件的上下左右距离和长度宽度。
可根据不同的屏幕大小固定控件位置。
可设置控件的宽度和高度根据父控件的尺寸伸缩。
属性
autoresizingMask 设置Autoresizing的方式
范例
UIView *redView = [[UIView alloc]init];
redView.backgroundColor = [UIColor redColor];
CGFloat x = self.view.frame.size.width - 100;
CGFloat y = self.view.frame.size.height - 100;
redView.frame = CGRectMake(x, y, 100, 100);
redView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleTopMargin;
[self.view addSubview:redView];
Auto Layout
说明
一种自动自动布局技术,专门用来布局UI界面
Autoresizing 仅能解决父控件和子控件之间的相对关系。
Auto Layout 可以解决任何控件之间的相对关系。
苹果建议使用 Auto Layout 不使用frame。
核心概念
约束(Constraints): 通过给控件添加约束,来决定控件的位置和尺寸
参照: 在添加约束时,依照哪个控件来添加
VFL
可视化格式语言(Visual Format Language)
为了简化 Auto Layout 编码推出的轻量语言
Masonry
Auto Layout 的第三方框架
避免苹果 Auto Layout API的繁琐
[官方地址][https://github.com/SnapKit/Masonry]
使用代码添加约束原则
两个同级控件添加约束,需要添加至两个控件的父级
不同层级控件添加约束,需添加到两个控件最近的共同父级
两个父子控件添加约束,需添加至父控件
范例
UIView *redView = [[UIView alloc]init];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
//禁止自动转换Autoresizing为Auto Layout
redView.translatesAutoresizingMaskIntoConstraints = NO;
//设置宽度约束
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:0.0 constant:100];
//添加约束
[redView addConstraint:width];
//设置高度约束
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:0.0 constant:100];
//添加约束
[redView addConstraint:height];
//设置右边距约束
NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1 constant:-20];
//添加约束给父级
[self.view addConstraint:right];
//设置右边距约束
NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:-20];
//添加约束给父级
[self.view addConstraint:bottom];
延迟执行
NSTimer
说明
可指定是否重复和每次调用的事件
是一种非阻塞的执行方式,
会受到Runloop运行模式的影响
可以通过NSTimer类的- (void)invalidate;取消执行。
对象方法
scheduledTimerWithTimeInterval 指定延迟时间后执行指定方法
invalidate 停止定时器,
CADisplayLink
说明
每次屏幕刷新时调用指定的方法
屏幕每秒刷新60次
必须加入主运行循环才会执行
属性
paused 是否暂停
对象方法
addToRunLoop 加入到指定的runLoop
范例
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(display)];
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
performSelector
说明
任务在主线程中执行
是一种非阻塞的执行方式,
暂时未找到取消执行的方法。
格式
[self performSelector:@selector(delayMethod) withObject:nil afterDelay:1.0f];
GCD
说明
通过Grand Central Dispatch 延迟执行某些操作
可指定队列和延迟时间
格式
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
其它
通知
说明
NSNotificationCenter(通知中心),负责不同对象间的消息通信
每一个应用程序都有一个NSNotificationCenter实例。
任何对象都可以向通知中心发布NSNotification,描述自己做什么。
其它对象可以申请在特定通知发布时收到通知。
监听通知必须在发布通知之前
通知属性
-(NSString *)name; 通知的名称
-(id)object; 通知的发布者
-(NSDictionary)userInfo; 通知发布者传递给通知接受者的信息
发布
NSNotification *notf = [NSNotification notificationWithName:@"set" object:self userInfo:@{@"msg":@"success"}];
[[NSNotificationCenter defaultCenter]postNotification:notf];
监听
[[NSNotificationCenter defaultCenter]addObserver:obj selector:@selector(msg:) name:@"set" object:notf];
移除
[[NSNotificationCenter defaultCenter]removeObserver:self name:@"set" object:notf];
UIDevice
说明
UIDevice提供了一个单例对象,它代表设备,通过它可获取一些设备信息。
比如电池电量(batteryLevel),电池状态(batteryState),设备类型(model)
UIDevice会发布设备状态改变的通知
范例
获取系统版本
NSString * ver = [[UIDevice currentDevice]systemVersion];
KVC
说明
(Key Value Coding)键值编码
最大的功能是取值和赋值
可进行自动类型转换
可以用KVC方式改变内部私有成员变量
forKey和forKeyPath
区别
forKeyPath包含了所有forkey的区别
forKeyPath可进行内部点语法递归访问内部属性
共同点
key必须存在,否则会报错
使用时,内部会自动查找key和_key属性
setValuesForKeysWithDictionary问题
字典中的key必须在模型中属性找到
如果模型中带有模型,这种方式会把模型内的模型设置为其它数据格式
格式
[obj setValue:(nullable id) forKey:(nonnull NSString *)];
[obj setValue:(nullable id) forKeyPath:(nonnull NSString *)];
范例
设置值
[person.dog setValue:@"DaHuang" forKey:@"name"];
[person setValue:@"DaHuang" forKeyPath:@"dog.name"];
取值
[person valueForKey:@"name"];
[person valueForKeyPath:@"dog.name"];
改变私有成员变量
@interface Person : NSObject
{
int _age;
}
-(void)printAge;
@end
@implementation Person
-(void)printAge{
NSLog(@"age:%d",_age);
}
@end
Person *person = [[Person alloc]init];
[person setValue:@"20" forKeyPath:@"age"];
[person printAge];
字典转模型
[self setValuesForKeysWithDictionary:dict];
模型转字典
NSDictionary *dist = [person dictionaryWithValuesForKeys:@[@"name",@"age"]];
取出模型中所有某个属性值
NSArray *allPerson = @[person_1,person_2,person_3];
NSArray *allPersonName = [allPerson valueForKeyPath:@"name"]
KVO
说明
(Key Value Observing)键值监听
监听对象的某个属性值的改变
对象销毁时需移除监听器
只要类用到KVO,苹果会自动为这个类生成一个子类,可通过isa查看。
范例
设置监听器
[person addObserver:self forKeyPath:@"name" options:nil context:nil];
移除监听器
[person removeObserver:self forKeyPath:@"name"];
设置监听函数
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
}
runtime
说明
运行时机制
能动态获取当前类的成员属性,不能获取其子类或父类的属性
项目相关
程序启动原理
1.执行main函数
2.执行UIApplicationMain,创建UIApplication对象,并设置UIApplication的代理。
3.开启事件循环(主运行循环,用来保证应用程序不退出)
4.去加载Info.plist文件,依据Info.plist文件执行如下操作
判断是否有Main参数,如果有则加载Main.storyBoard
创建一个窗口
把Main.storyBoard中箭头所指向的控制器设为根控制器
显示窗口(把窗口的根控制器的view添加到窗口)
5.应用程序启动完毕,并通知代理。
UIWindow
说明
UIWindow是一个特殊的UIView,通常一个App中至少有一个UIWindow。
iOS程序启动完毕后,创建的第一个视图控件就是UIWindow。
UIWindow创建后,则创建控制器的view,并把控制器的view添加到UIWindow。
一个iOS程序之所以能显示到屏幕上,完全是因为有UIWindow。
没有UIWindow则看不到任何UI界面。
范例
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//创建窗口
self.window = [[UIWindow alloc]init];
//指定窗口的根控制器啊
UIViewController *vc = [[UIViewController alloc]init];
vc.view.backgroundColor = [UIColor redColor];
self.window.rootViewController =vc;
//显示窗口
[self.window makeKeyAndVisible];
return YES;
}
LaunchScreen
说明
Xcode7种多了一个LaunchScreen.storyboard用来设置启动页面。
如果没有在项目设置中指定LaunchScreen.storyboard,默认屏幕大小为4S大小。
LaunchScreen的底层实现是把LaunchScreen.storyboard当中的内容生成一张图片。
Info.plist
说明
用来保存设置应用程序的配置信息。是一个字典类型
属性
Bundle identifier 应用程序唯一标识,发布和推送时会用到
Bundle name 应用程序名称
Bundle versions string, short 用于itunes上显示的版本号,即对外的版本
Bundle version 应用程序打包版本号,用于项目管理,不对外
程序中获取获取参数
[NSBundle mainBundle].infoDictionary[@"property"]
Prefix.pch
说明
可存放一些公有的宏
可导入公用头文件
可自定义NSLog方法用来在生产环境关闭日志
原理
编译过程中,会把PCH文件当中的所有内容导入到工程中所有文件内
如果PCH中内容较多,会导致编译过程较长
和C混合编译时会报错,需使用__OBJC__判断当前文件是否是OC文件
创建
Command + N -> Other -> PCH File
文件名通常和项目同名
配置
项目配置 -> Build Settings -> Precompile Prefix Header : Yes
项目配置 -> Build Settings -> Prefix Header : XXX/XXX.pch
范例
#ifdef __OBJC__
#define ScreenWidth [UIScreen mainScreen].bounds.size.width
#import "XXXXX.h"
#ifdef DEBUG
#define XMGLOG(...) NSLog(__VA_ARGS__)
#else
#define XMGLOG(...)
#endif
#endif
AppIcon
说明
设置应用程序的图标
把图标按照特定文件名和尺寸放置到Assets.xcassets中AppIcon内
状态栏
说明
从iOS7开始,系统提供了2种管理状态栏的方式,
通过UIViewController管理(每一个UIViewController都可以拥有自己不同的状态栏)。
通过UIApplication管理(一个应用程序的状态栏都由它统一管理)。
在iOS7中,默认情况下,状态栏都是由UIViewController管理的。
可通过Info.plist中的View controller-based status bar appearance字段设置是否由UIApplication管理。
UIViewController实现状态栏样式和可见性
状态栏的样式
- (UIStatusBarStyle)preferredStatusBarStyle;
状态栏的可见性
- (BOOL)prefersStatusBarHidden;
UIApplication
说明
UIApplication对象是应用程序的象征。
一个应用程序中只有一个UIApplication对象,并且是单例的。
通过 [UIApplication shareApplication] 可获取这个单例对象
一个iOS程序启动后创建的第一个对象就是UIApplication对象
利用UIApplication对象可进行一些应用级别的操作
属性
keyWindow 获取通过nakeKeyAndVisiable设置的主窗口
delegate
说明
当系统发生某些事情(如电话,锁屏)导致App收到打扰时,会产生一系列事件。
这时UIApplication会通知它的代理对象,让代理处理这些系统事件
代理协议为UIApplicationDelegate
工程创建时已默认创建了AppDelegate文件并遵守了UIApplicationDelegate协议
系统事件
应用程序的生命周期事件(如程序启动或关闭)
系统事件(如来电)
内存警告
协议方法
didFinishLaunchingWithOptions 应用程序启动完毕时调用
applicationWillResignActive 应用程序失去焦点时调用
applicationDidEnterBackground 应用程序进入后台时调用
applicationWillEnterForeground 应用程序进入前台时调用
applicationDidBecomeActive 应用程序获取焦点时调用
applicationWillTerminate 应用程序退出时调用
applicationDidReceiveMemoryWarning 应用程序收到内存警告时调用
范例
设置应用程序图标右上角的红色提醒数字
//获取UIApplication对象
UIApplication *app = [UIApplication sharedApplication];
//注册通知
UIUserNotificationSettings *notice = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge categories:nil];
[app registerUserNotificationSettings:notice];
//设置提醒值
app.applicationIconBadgeNumber = 20;
设置联网状态
UIApplication *app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = YES;
设置状态栏(需先设置Info.plist)
UIApplication *app = [UIApplication sharedApplication];
app.statusBarHidden = YES;
app.statusBarStyle = UIStatusBarStyleLightContent;
打开网页
[app openURL:[NSURL URLWithString:@"tel//10086"]];
[app openURL:[NSURL URLWithString:@"sms//10086"]];
[app openURL:[NSURL URLWithString:@"mailto//7760442@g.com"]];
[app openURL:[NSURL URLWithString:@"http://www.baidu.com"]];
创建单例对象
@interface Person : NSObject
+(instancetype)sharePerson;
@end
@implementation Person
static Person *_instance;
//程序加载时,就创建对象
+(void)load{
_instance = [[Person alloc]init];
}
//每次创建对象都返回同一个实例
+(instancetype)sharePerson{
return _instance;
}
//如果调用alloc抛出错误
+(instancetype)alloc{
if(_instance){
NSException *exc = [NSException exceptionWithName:@"异常名称" reason:@"错误原因" userInfo:nil];
[exc raise];
}
return [super alloc];
}
@end
RunTime
说明
Runtime简称运行时,OC就是运行时机制,是运行时的一些机制,其中最主要的是消息机制。
对于C语言,函数的调用在编译时会决定调用哪个函数。
对于OC语言,属于动态调用过程,在编译时不能决定真正调用哪个函数。
只有在真正运行时的时候才会根据函数的名称找到对应的函数来调用。
在编译阶段,OC可以调用任何函数,即使这个函数未实现,只要声明过就不会报错。
在编译阶段,C语言调用未实现的语言会报错。
通过RunTime可以调用私有方法
设置RunTime方法提示
Build Settings -> Enable Strict Checking of objc_Send Calls -> NO
方法调用流程
1. 根据对象的isa指针,找到方法所在类。
2. 注册方法编号,因编号查找比字符串查找快速。
3. 根据方法编号,去方法的类中方法列表中查找函数实现地址。
4. 根据函数地址去方法区调用对应的函数,方法的实现是保存在内存中的方法区、而不在类里面
发送消息
说明
任何方法调用的本质为发送小心,发送消息是通过RunTime实现的。
Xcode的clang编译器最终会把OC中的代码转换成底层RunTime的实现。
可在终端通过 clang -rewrite-objc xx.m 查看最终生成代码。
使用时需导入<objc/message.h>头文件
范例
创建对象
#import <objc/message.h>
id objc = objc_msgSend([NSObject class], @selector(alloc));
objc = objc_msgSend(objc, @selector(init));
从自定义类创建对象
Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("allco"));
p = objc_msgSend(p, sel_registerName("init"));
交换方法
说明
可以通过RunTime来交换方法的实现。
比如以自定义的方法来替换替换默认的方法。
范例
@interface UIImage (Custom)
+(UIImage *)cus_imageNamed:(NSString *)name;
@end
#import "UIImage+Custom.h"
#import <objc/message.h>
@implementation UIImage (Custom)
+(void)load{
//获取并交换两个方法
Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));
Method cus_imageNamedMethod = class_getClassMethod(self, @selector(cus_imageNamed:));
method_exchangeImplementations(imageNamedMethod, cus_imageNamedMethod);
}
//自定义的cus_imageNamed方法
+(UIImage *)cus_imageNamed:(NSString *)name{
UIImage *image = [UIImage cus_imageNamed:name];
if(image){
NSLog(@"加载成功");
}else{
NSLog(@"加载失败");
}
return image;
}
@end
//使用imageNamed方法方法时会自动调用cus_imageNamed方法
UIImage *image = [UIImage imageNamed:@"1.png"];
动态添加方法
说明
OC中只要一个方法实现了,就会立即添加到方法列表中。
实际开发中,有一些功能是在满足某些条件下才可以使用,比如付费功能。
这时可通过动态添加方法来实现。
任何方法内都有self和_cmd两个隐式参数。
_cmd表示当前方法的方法编号。
方法
@interface Person : NSObject
@end
#import "Person.h"
#import <objc/message.h>
@implementation Person
void eat(id self, SEL _cmd){
NSLog(@"eat");
}
//只要一个对象调用了未实现的方法就会调用此方法。
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if(sel == NSSelectorFromString(@"eat")){
class_addMethod(self, sel, (IMP)eat, "v@:");
}
return [super resolveInstanceMethod:sel];
}
@end
//调用一个未实现的方法
Person *p = [[Person alloc]init];
[p performSelector:@selector(eat)];
给分类动态添加属性
说明
通过RunTime可以给一个对象动态添加属性,比如让一个NSObject类保存一个字符串。
动态添加属性的本质是让某个属性和对象产生关联。
范例
#import <Foundation/Foundation.h>
@interface NSObject (Property)
@property NSString *name;
@end
#import "NSObject+Property.h"
#import <objc/message.h>
@implementation NSObject (Property)
-(void)setName:(NSString *)name{
//给当前对象添加属性
objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSString *)name{
//获取当前对象动态添加的属性
return objc_getAssociatedObject(self, "name");
}
@end
使用方式
NSObject *objc = [[NSObject alloc]init];
objc.name = @"ABC";
NSLog(@"%@",objc.name);
字典转模型
说明
KVC的问题是字典中的key如果在模型中不存在的话会报错。
可以利用RunTime来实现字典转模型并避免此问题。
主要思路为遍历模型的属性,然后依据属性名从字典中获取数据并给模型赋值。
范例
@interface NSObject (Model)
+ (instancetype)modelWithDict:(NSDictionary *)dict;
@end
#import "NSObject+Model.h"
#import <objc/message.h>
@implementation NSObject (Model)
+ (instancetype)modelWithDict:(NSDictionary *)dict{
id objc = [[self alloc] init];
//定义成员变量的个数
unsigned int count = 0;
//获取成员变量的数组
Ivar *ivarList = class_copyIvarList(self, &count);
for (int i = 0; i<count; i++) {
//获取成员变量
Ivar ivar = ivarList[i];
//获取成员变量名字
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
//去掉成员变量名前的下划线
NSString *key = [ivarName substringFromIndex:1];
//从字典中获取值
id value = dict[key];
//获取当前成员变量的类型名
NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
//替换字符串中的@和"字符
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
//二级转换,如果dict的值为字典并且是自定义对象,则转换为子模型
if([value isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"NS"]){
//把类型名转换为类型
Class modelClass = NSClassFromString(ivarType);
//把value重新赋值为子模型
value = [modelClass modelWithDict:value];
}
//给模型中属性赋值
if(value){
[objc setValue:value forKey:key];
}
}
return objc;
}
@end
Xcode 使用
Xcode5于Xcode7区别
FrameWorks
Xcode5有个FrameWorks文件夹,用于存放框架。
Xcode7会在程序编译时自动导入框架,如需查看框架的头文件,也可在项目配置中导入。
LaunchScreen
Xcode7种多了一个LaunchScreen.storyboard用来设置启动页面。
如果没有在项目设置中指定LaunchScreen.storyboard,默认屏幕大小为4S大小。
Info.plist
Xcode5中此文件存放在 Supporting Files 文件夹内
Xcode5种还包含infoPlist.strings文件,用来做多语言本地化
Xcode7中存放在项目根目录下
Prefix.pch
Xcode7中无此文件
显示行号
Preferences -> Text Editing -> Show Line numbers
Xcode8关闭启动时的注释
Product -> Scheme -> Edit Scheme... -> Run
add the following environment variable:
Name:OS_ACTIVITY_MODE, Value: disable
插件
插件安装后的路径
~/Library/Application Support/Developer/Shared/Xcode/Plug-ins
删除插件
在安装目录下直接删除文件即可
插件管理
[Alcatraz][https://github.com/alcatraz/Alcatraz]
注释生成器
[VVDocumenter-Xcode](https://github.com/onevcat/VVDocumenter-Xcode)
图片文件名提示
[KSImageNamed-Xcode](https://github.com/ksuther/KSImageNamed-Xcode)
iOS9新特性
新增关键字
nulllable
说明
用来指定属性可为空。
可以用于属性,方法和参数中。
范例
@property (nonatomic, strong, nullable) NSString *str;
@property (nonatomic, strong) NSString * _Nullable str;
nonnull
说明
用来指定属性不可为空。
如果传null,nil会警告而不会报错
在NS_ASSUME_NONNULL_BEGIN,NS_ASSUME_NONNULL_END宏内的属性为nonnull
范例
@property (nonatomic, strong, nonnull) NSString *str;
@property (nonatomic, strong) NSString * _Nonnull str;
null_resettable
说明
get方法不能返回nil。必须在get方法内处理为空的情况。
set方法传入参数时可为nil。
范例
@property (nonatomic, strong, null_resettable) NSString *str;
null_unspecified
说明
不确定是否为空
范例
@property (nonatomic, strong, null_unspecified) NSString *str;
@property (nonatomic, strong) NSString * _Null_unspecified str;
泛型
说明
可用于限制集合内元素的类型,和Swift保持了一致。
在定义时不确定类型,在使用时才确定类型的情况可使用。
优点是提高了代码规范可可读性,减少了沟通成本。
在OC中不符合泛型要求会报警告,而不是报错。
协变和逆变
用于父子类型的转换。
协变关键字为 __covariant,允许子类赋值给父类
逆变关键字为 __contravariant,允许父类赋值给子类
范例
定义带有泛型的数组
NSMutableArray<NSString *> *arr = [NSMutableArray array];
在定义类中使用
@interface Person<T> : NSObject
@property (nonatomic, strong) T attrNmae;
@end
Person<UIButton *> *p = [[Person alloc]init];
p.attrNmae = [[UIButton alloc]init];
__kindof
说明
用来表示当前类或者它的子类。
范例
@interface Person : NSObject
+ (__kindof Person *)person;
@end