这是我参与「第四届青训营 」笔记创作活动的的第8天。
前几天因为要准备考试断更了,今天开始加更。
iOS App 入门与 UIKit 简介
iOS APP
1.iOS App 入门
main函数
main函数中会初始化一个UIApplication实例(UIApplication函数会创建一个UIApplication对象)以及一个AppDelegate代理。
UIApplicationMain
UIApplication 是单例模式,一个应用程序只有一个 对象
main函数中,关键方法为UIApplicationMain创建application对象,和application的代理,设置事件循环。
此方法包含4个参数,对应的参数表为:
@param argc 参数个数
@param argv 参数
@param principalClassName 根据该参数初始化一个UIApplication或其子类的对象并开始接收事件(传入nil, 意味使用默认的UIApplication)
@param delegateClassName 该参数指定AppDelegate类作为委托, delegate对象主要用于监听, 类似于生命周期的回调函数
@return 返回值为int, 但是并不会返回(runloop), 会一直在内存中 直到程序终止
我们重点关注最后两个参数
- principalClassName(主要类名):如果传空,会尝试从info.plist获取值,如果info.plist也没有,则默认为UIApplication。pricipalClass类除了管理整个App的生命周期之外什么也不做,只负责监听事件,然后将其交给delegateClass去处理
- delegateClassName(委托类名):将在工程新建时实例化一个对象
NSStringFromClass([AppDelegate class])
AppDelegate
AppDelegate对象实际上也是一个单例实例对象,在App推出前永远也不会被释放,AppDelegate在程序启动时就不断跟踪App的状态变化,处理包括从其他应用跳转、启动应用、后台运行等动作。
AppDelegate作为App的根对象,主要有以下功能:
- 在启动时运行用以初始化App的代码
- 响应App在各个生命周期状态的指定方法
- 接受推送通知或者本地通知启动App的情况
- 处理低内存警告
- 处理App的状态保存和恢复
- 响应由App处理的事件
- 处理数据的存储
2、App的生命周期
App从启动到退出的过程中,iOS应用程序不断从系统接收各种事件,如:用户点击了屏幕、用户点击了Home键,并对这些事件进行响应。接收事件是UIApplication对象的工作,但是,响应事件需要由程序员编写的代码来处理。为了理解事件响应需要在哪里处理,就必须对iOS应用程序的整个生命周期和事件周期有所了解。
我们在ppt中讲述了一个App的生命周期包含了5种状态:
- Not Running
- Inactive
- Active
- Background
- Suspended
🌰Not Running 到Inactive状态下加载 到Active状态 点击home button从Active到Inactive,到Background后台 被挂起Suspended 内存不足被杀死 从挂起到Not Running
而App的生命周期,就是根据这些状态之间进行变化而在UIApplicationDelegate中响应的回调方法。最常用的回调方法分为以下7种:
- application : willFinishLaunchingWithOptions: 在App启动时调用表示应用加载进程已经开始,常用来处理应用状态的存储和恢复
- application : didFinishLaunchingWithOptions: 表示App将从未运行状态进入运行状态,用于对App的初始化操作
- applicationDidBecomeActive : 当应用即将进入前台运行时调用
- applicationWillResignActive : 当应用即将进从前台退出时调用
- applicationDidEnterBackground : 当应用开始在后台运行的时候调用
- applicationWillEnterForeground : 当程序从后台将要重新回到前台(但是还没变成Active状态)时候调用
- applicationWillTerminate : 当前应用即将被终止,在终止前调用的函数。通常是用来保存数据和一些退出前的清理工作。如果应用当前处在suspended,此方法不会被调用。 该方法最长运行时限为5秒,过期应用即被kill掉并且移除内存。
UIKit
iOS系统框架中最重要和最基本的两个框架
- Foundation 无关界面 提供基础API
- UIKit 提供基础的视图类库
视图(UIView)与图层(CALayer)
-
iOS中所有的视图都是由UIView的基类派生来的 每一个UIView背后 都有一个CALayer支撑 UIciew实际上只是CALayer的封装 在层级关系上 二者也有着平行关系
-
平行关系:
-
为什么要提供UIView和CALayer两个平行的层级关系
为了职责分离
- CALayer不处理用户的交互,只负责管理子图层的位置,包含了一些方法和属性来执行动画和变换(前缀也是来自于CoreAnimation)
- uIView的封装使得动画和操作变得简单,并可以处理用户的点击事件,但简单的代价是灵活上的缺陷——如果UIView没有实现你需要的接口功能,那么你只能进入CALayer层进行操作
-
-
-
UIView可以处理布局和触摸事件,可以支持基于CoreGraphics绘图,可以做仿射变换,或者做滑动或者渐变动画,但是不直接处理绘图和动画
-
生命周期
创建➡️子视图布局➡️将自己加入父视图➡️将自己移出父视图➡️添加到窗口➡️移出到窗口
-
- CALayer类被UIView所封装(iOS上,在macOS上是被NSView所封装),CALayer不处理用户的交互,他只负责管理子涂层位置,包含一些方法和属性用来做动画和变换,负责绘画和动画,不关注触摸事件。CALayer不清楚具体响应链,不能响应事件
-
UIView不能做,但CALayer可以做的:
- 阴影、圆角、带颜色边框
- 3D变换
- 非矩形范围
- 透明遮罩
- 多级非线性动画
-
UIViewController
- 生命周期
被创建➡️呈现在屏幕上➡️从屏幕上消失
执行流程
init -> loadView ->viewWillAppear ->wiowDidAppear->(开始退出)viewWillDisappear -> viewDidDisappear ->viewWillUnload -> viewDidUnload->dealloc
-
daimazuzhi
- init中最好不要出现创建view的代码,应当进行数据初始化
- loadView中适合进行view的创建和初始化
- viewDidiLoad中适合创建一些附加的view和空间
- vieiWillAppear可以进行一些显示前的处理,比如键盘弹出或恃殊动画
- viewDidAppear在显示动画后需要做的操作
-
图层几何学
- UIView三个属性 frame bounds center CALayer对应为 frame bounds position
- 锚点 理解为移动图形的把柄 可以背移动 左上角00 右下角11 可以通过设置令其处于图层之外
-
图形变换 坐标系 一个图层的position依赖于 父图层 如果父图层发生了移动 所有子图层跟着移动 CALayer也提供了一套不同坐标系之间转换工具类方法
-
视图布局
- 放置一个控件(x,y,height,width),一种手动布局的方式,是一次性计算出这些值,并设置进去
- 自动布局 不再关心具体值 而是相对位置(约束/线性关系)/ 自动布局高效 但是代码复杂度高 当视图元素增加 代码膨胀程度增加 Masonry 对系统NSLayoutConstranint进行封装的第三方自动布局框架 系统AutoLayout支持的操作
视觉效果
- 圆角 如果想创建有些圆角有些直角的图层或视图的时候,可能需要用到图层蒙版或者是CAShapeLayer
- 图层边框 CALayer的两个属性borderWidth和borderColor 默认borderWidth是0. ,borderColor是黑色 borderColor是CGColorRef类型,而不是UIColor,不是Cocoa的内置对象 边框不会把寄宿图或者是子图层的形状计算进来,如果涂层的子图层超过了边界,边框依然会沿着涂层的边界绘制出来
- 阴影 shadowOpacity,一个必须在0.0(不可见)和1.0(完全不透明)之间的浮点数 可以使用CALayer的另外三个属性改变阴影表现:shadowColor、shadowOffset、shadowRadius shadowRadius越大,阴影越模糊,看上去深度就会更明显
- 阴影裁剪 涂层的阴影继承自内容的外形,而不是根据边界和角半径确定的 计算阴影时,CoreAnimation会将寄宿图(包括子视图)考虑在内,这就是我们在课上所举的例子,我们只为红色方块添加阴影,蓝色方块作为红色方块的子视图,也同时被添加上了阴影
- shadowPath属性 如果预知了阴影的形状,可以通过提供一个CGPathRef给shadowPath属性,单独于涂层的形状之外指定阴影的形状
-
图层蒙版 maskToBounds可以沿着边界裁剪图形,如果想要展现自定义的框架,可以用CALayer的mask属性解决问题,这个属性本身也是一个CALayer类型
蒙版图层真正历代的地方在于蒙版图不局限于静态图,任何有图层构成的都可以作为mask属性,这意味着蒙版甚至可以通过代码甚至动画实时生成
-
拉伸过滤 当图片需要展示不同大小的时候,有一种叫做拉伸过滤的算法起作用了 minification(缩小图片)和magnification(放大图片)默认的过滤器都是
kCAFilterLinear,这个过滤器采用双线性滤波算法,它在大多数情况下都表现良好 CALayer提供三种拉伸过滤方法:- kCAFilterLinear 双线性过滤
- kCAFilterNearest 最近邻过滤
- kCAFilterTrilinear 三线过滤
-
组透明 常用alpha控制透明
当你显示一个50%透明度的图层时,如果图层包含一个同样显示50%透明的子图层时,你所看到的视图,50%来自子视图,25%来了图层本身的颜色,另外的25%则来自背景色
如果你希望包含了整个图层树像一个整体一样呈现透明效果,可以在info.plist的UIViewGroupOpacity设置为YES来实现这个效果,CALayer的shouldRasterize实现组透明效果
Core Graphics(核心图形)
Core Graphics也被称为Quartz 2D,是一个先进的二维绘图引擎,是iOS的核心图形库
iOS本身提供两套绘图框架,即UIBezierPath和Core Graphics,前者属于UIKit,其实是对Core Graphics框架关于path的进一步封装,所以使用起来更加简单
当然,Core Graphics更接近底层,所以更加强大
Quartz2D是使用CPU绘制,在基于View的绘图过程中,view的bound和center被改动时会触发drawRect:来重新绘制位图。这种方式需要CPU在主线程执行,比较耗时(并不明显,除非有CPU密集绘制操作)
支持绘制
- 贝塞尔线条,基本几何图形
- 文字
- 绘制、生成图片
- 读取、生成PDF
- 裁剪图片
- 自定义UI控件
API由C语言写成,不受ARC管控
常见的UI控件
常用组件
普通控件
- TextField、TextView输入框 它是一个文本域的编辑视图,可以在该区域上进行编辑(包括删除、剪贴、复制、修改等),它与文本框UITextField的不同之处是:当它里面的每一行内容超出时,可以自动换行,而且带有滚动条,可以滚动查看其他无法显示的内容。
- DatePicker 日期选择器
// 日期转为字符串
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy年MM月dd日"; // HH:mm:ss 时分秒
NSString *str = [formatter stringFromDate:date];
复制代码
- UIScrollView 滚动视图 功能: 滚动视图 概述: UIScrollView是用来在屏幕上显示那些在有限区域内放不下的内容。例如,在手机屏幕上显示内容丰富的网页或者很大的图片。在这种情况下,需要用户对屏幕内容进行拖动或缩放来查看屏幕或窗口区域外的内容。
-
UITableView表格视图
概念: 把内容按行分组显示的控件,适合于内容不断加载的场景,比如今日头条的Feed流。
关于单元格cell
UITableView的每行数据都是一个UITableViewCell:
- 每个Cell使用IndexPath来表示位置
- IndexPath又分为section和 row ,每个节section为一组,其中可以包含多个行row。
- 系统Cell的UI和布局
样式
右侧的访问器
UITableViewCell右侧可以显示不同的图标,在iOS中称之为访问器,点击可以触发不同的事件,例如设置功能:
要设置这些图标只需要设置UITableViewCell的accesoryType属性:
typedef NS_ENUM(NSInteger, UITableViewCellAccessoryType) {
UITableViewCellAccessoryNone, // 不显示任何图标
UITableViewCellAccessoryDisclosureIndicator, // 跳转指示图标
UITableViewCellAccessoryDetailDisclosureButton, // 内容详情图标和跳转指示图标
UITableViewCellAccessoryCheckmark, // 勾选图标
UITableViewCellAccessoryDetailButton NS_ENUM_AVAILABLE_IOS(7_0) // 内容详情图标
};
复制代码
用户界面显示的行数有限,比如现在显示了7行,然后向下滚动时,上面的 行row就会被“销毁”并放到回收池pool,之后滚动回来可以重用。
UICollectionView
三大组成部分
-
Layout(布局)
- 常见对象 UICollectionViewFlowLayout 流式布局
-
DataSource(数据代理)
- 数据代理实现需要协议 展示CollectionView 会调用制定对象的代理方法获取基本数据 并以此渲染Cell
- 可以同时注册不同类 或者 不同Identifier的Cell
- 使用时 自动从缓存池中取
UITableView和UICollectionView都有许多需要显示的cell 但是屏幕只显示少数几个 如果所有的数据都加载进去 暂时看不到浪费内存
避免方法:cell的重用机制 实现了数据和显示的分离 并不会为每一个要显示的数据都创建一个Cell
每当有一个cell从屏幕消失 就放到缓存池 如果有新的cell出现 就去缓存池中取 如果缓存池没有 再创建
CollectionView必须先注册再使用
UITableView可以不事先注册 允许手动判断是否存在后主动加入缓存池
-
Delegate(行为代理)
行为代理实现需要UICollectionViewDelegate协议
当不同Cell接收到用户的点击或高亮事件后 将调用制定代理的对应方法实现业务行为