iOS-UI框架-笔记

基础概念

相关框架
    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