• iOS UI-九宫格


      1 第一节课:
      2 1.复习
      3 2.运行App应用管理,简单界面分析
      4 3.一个应用为一个整体,直接创建一个appView然后计算frame
      5 4.说明弊端,应该根据数据的个数来for循环创建
      6 
      7 第二节课:
      8 5.加载plist文件字典转模型
      9 6.分析计算frame 宽高固定,x,y动态去计算
     10 6.1.  行号 row = i / columnCount
     11 6.2 列号 col = i % columnCount
     12 leftMargin = (屏幕的宽 - (appW * columnCount) - appColMagrin * (columnCount - 1)) * 0.5;
     13 appX = leftMargin + (appW + appColMagrin) * col;
     14 appY = topMargin + (appY + appRowMagrin) * row
     15 
     16 第三节:
     17 6.3创建appView内部三个字控件,iconView, nameLabel , downloadBtn
     18 6.4 先创建设置背景色,计算frame添加到appView中
     19 6.5 给子控件设置数据,设置label字体和文字对齐方法
     20 注意点:给按钮设置图片或文字还有文字颜色时,要用set方法指定不同状态的文字或图片,不能直接访问titleLabel去给按钮设置文字
     21 7.分析懒加载代码中的问题封装字典转模型细节,
     22 
     23 
     24 instancetype 是和id
     25 1、相同点
     26 都可以作为方法的返回类型
     27 
     28 2、不同点
     29  1> instancetype可以返回和方法所在类相同类型的对象,id只能返回未知类型的对象;
     30 
     31  2> instancetype只能作为返回值,不能像id那样作为参数,和声明属性
     32 
     33 id是个万能指针编译时不确定真实类型,只有在运行时才知道真实的类型
     34 instancetype在编译时就可以确定真实类型,类型一般为所在类的对象类型
     35 
     36 下午:
     37 
     38 第一节课:
     39 
     40 7.1 自定义构造方法,并提供一个类方法
     41 7.2 自定义构造方法时的方法命名规范,注意点
     42 7.3 代码进一步优化,引出xib
     43 8.xib和storyboard的区别及开发中如何去选择
     44 9.MVC概念的引入
     45 9.1用xib去封装appView,并创建一个继承至UIView的类,和xib的类型关联
     46 9.2把xib中的控件连线到自定义类的.h中可以在外面直接访问去给子控件设置数据
     47 9.3.弊端,把模型变成属性引入到自定义view类,当控制器为自定义view类的模型属性赋值时会调用模型属性的set方法,如果我们重写了属性得set方法就会调用我们重写得set方法,在此方法中给appView中的子控件设置数据
     48 
     49 第二节课:
     50 10.添加下载按钮点击事件
     51 10.1 点击下载按钮弹出提示标签,并加入动画
     52 
     53 第三节课:
     54 11.回顾用xib自定义view的步骤
     55 1.创建一个xib文件,在xib文件中布局好子控件,并设置好对应控件的属性
     56 2.创建一个和xib文件相同的类,此类继承至那个类,取决于xib文件中最顶层控件的类型,
     57 3.指定xib文件的类型为我们自定义的类,然后把需要修改的控件拖线到所关联类的.m文件中
     58 4.自定义类,定义模型属性,并重写模型属性的set方法,在此方法给子控件设置数据
     59 5.在自定义类提供一个可供外部访问的类方法,把加载xib创建appView的过程封装到自定义类中
     60 
     61 
     62 
     63 
     64 
     65 
     66 
     67 
     68 ***********  UIViewAnimationOption(动画选项,默认为匀速) ********
     69 常规动画属性设置(可以同时选择多个进行设置)
     70 
     71 UIViewAnimationOptionLayoutSubviews:动画过程中保证子视图跟随运动。
     72 
     73 UIViewAnimationOptionAllowUserInteraction:动画过程中允许用户交互。
     74 
     75 UIViewAnimationOptionBeginFromCurrentState:所有视图从当前状态开始运行。
     76 
     77 UIViewAnimationOptionRepeat:重复运行动画。
     78 
     79 UIViewAnimationOptionAutoreverse :动画运行到结束点后仍然以动画方式回到初始点。
     80 
     81 UIViewAnimationOptionOverrideInheritedDuration:忽略嵌套动画时间设置。
     82 
     83 UIViewAnimationOptionOverrideInheritedCurve:忽略嵌套动画速度设置。
     84 
     85 UIViewAnimationOptionAllowAnimatedContent:动画过程中重绘视图(注意仅仅适用于转场动画)。
     86 
     87 UIViewAnimationOptionShowHideTransitionViews:视图切换时直接隐藏旧视图、显示新视图,而不是将旧视图从父视图移除(仅仅适用于转场动画)
     88 UIViewAnimationOptionOverrideInheritedOptions :不继承父动画设置或动画类型。
     89 
     90 2.动画速度控制(可从其中选择一个设置)
     91 
     92 UIViewAnimationOptionCurveEaseInOut:动画先缓慢,然后逐渐加速。
     93 
     94 UIViewAnimationOptionCurveEaseIn :动画逐渐变慢。
     95 
     96 UIViewAnimationOptionCurveEaseOut:动画逐渐加速。
     97 
     98 UIViewAnimationOptionCurveLinear :动画匀速执行,默认值。
     99 
    100 3.转场类型(仅适用于转场动画设置,可以从中选择一个进行设置,基本动画、关键帧动画不需要设置)
    101 
    102 UIViewAnimationOptionTransitionNone:没有转场动画效果。
    103 
    104 UIViewAnimationOptionTransitionFlipFromLeft :从左侧翻转效果。
    105 
    106 UIViewAnimationOptionTransitionFlipFromRight:从右侧翻转效果。
    107 
    108 UIViewAnimationOptionTransitionCurlUp:向后翻页的动画过渡效果。
    109 
    110 UIViewAnimationOptionTransitionCurlDown :向前翻页的动画过渡效果。
    111 
    112 UIViewAnimationOptionTransitionCrossDissolve:旧视图溶解消失显示下一个新视图的效果。
    113 
    114 UIViewAnimationOptionTransitionFlipFromTop :从上方翻转效果。
    115 
    116 UIViewAnimationOptionTransitionFlipFromBottom:从底部翻转效果。

    一、创建九宫格 

    实现思路

    (1)明确每一块用得是什么view

    (2)明确每个view之间的父子关系,每个视图都只有一个父视图,拥有很多的子视图。

    (3)可以先尝试逐个的添加格子,最后考虑使用for循环,完成所有uiview的创建

    (4)加载app数据,根据数据长度创建对应个数的格子

    (5)添加格子内部的子控件

    (6)给内部的子控件装配数据

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 设置每行应用的个数
        int clumns = 3;
        // 获取控制器所管理的view的宽度
        CGFloat viewWidth = self.view.frame.size.width;
        
        // 每个应用的宽和高
        CGFloat appW = 75;
        CGFloat appH = 90;
        CGFloat marginTop = 30;//第一行距顶部的距离
        CGFloat marginX = (viewWidth - appW*clumns)/(clumns+1);
        CGFloat marginY = marginX;//设每行之间的间距与marginX相等
        
        for (int i=0; i<9; i++) {
            //1.创建每个应用(UIView)
            UIView *appView = [[UIView alloc] init];
            
            //2.设置appView的属性
            
            //2.1设置appView的背景色
            appView.backgroundColor = [UIColor orangeColor];
            //2.2设置appView的frame属性
            
            //计算每个单元格的列索引
            int colIdx = i%clumns;
            //计算每个单元格的行索引
            int rowIdy = i/clumns;
            
            CGFloat appX = marginX+(appW+marginX)*colIdx;
            CGFloat appY = marginTop+(appH+marginY)*rowIdy;
            
            appView.frame = CGRectMake(appX, appY, appW, appH);
            
            //3.将appView加到self.view
            [self.view addSubview:appView];
            
        }
    }

    二、向九宫格内加控件

      1 #import "ViewController.h"
      2 
      3 @interface ViewController ()
      4 /**
      5  *  用来存放所有数据的数组
      6  */
      7 @property (nonatomic, strong) NSArray *appes;
      8 @end
      9 
     10 @implementation ViewController
     11 
     12 /**
     13  *  控制器的view加载完成之后就会调用此方法
     14  */
     15 - (void)viewDidLoad {
     16     [super viewDidLoad];
     17     // 格子之间的间距
     18     CGFloat margin = 20;
     19     // 格子的宽
     20     CGFloat appViewW = 100;
     21     // 格子的高
     22     CGFloat appViewH = 120;
     23     // 一行中用三个格子
     24     NSInteger column = 3;
     25     // 最左边的间距  =  (控制器view的宽 - 一行中所有格子的宽 - 格子间的间距 * (一行中格子的个数 - 1)) * 0.5
     26     CGFloat leftMargin = (self.view.bounds.size.width - appViewW * column - (column - 1) *margin) * 0.5;
     27     CGFloat topMargin = leftMargin;
     28     // 根据数据的个数来动画创建每一个应用
     29     for (NSInteger i = 0; i < self.appes.count; i++) {
     30         
     31         // 0>  先取出每一个应该的数据
     32         NSDictionary *dict = self.appes[i];
     33         
     34         // 1.创建appView 用来装里面的子控件
     35         UIView *appView = [[UIView alloc] init];
     36         // 2.设置背景色
     37 //        appView.backgroundColor = [UIColor blueColor];
     38         
     39         // 计算列号
     40         NSInteger col = i % column;
     41         // 计算行号
     42         NSInteger row = i / column;
     43         // 3.设置frame
     44         // 计算appViewX = 左边间距  + (appView宽 + 格子间距) *当前格子是在当前行中的第几个
     45         CGFloat appViewX = leftMargin + (appViewW + margin) * col;
     46         // appViewY = 顶部间距  + (appViewH + margin) * 当前格子在当前列中是第几个
     47         CGFloat appViewY = topMargin + (appViewH + margin) * row;
     48         
     49         appView.frame = CGRectMake(appViewX, appViewY, appViewW, appViewH);
     50         
     51         // 4.添加到控制器的view中
     52         [self.view addSubview:appView];
     53         
     54         // 5.创建应用图片
     55         UIImageView *iconView = [[UIImageView alloc] init];
     56         // 设置背景色
     57 //        iconView.backgroundColor = [UIColor purpleColor];
     58         CGFloat iconW = 70;
     59         CGFloat iconH = iconW;
     60         CGFloat iconX = (appViewW - iconW) * 0.5;
     61         CGFloat iconY = 0;
     62         iconView.frame = CGRectMake(iconX, iconY, iconW, iconH);
     63         [appView addSubview:iconView];
     64         
     65         // 设置应用图片
     66         iconView.image = [UIImage imageNamed:dict[@"icon"]];
     67         
     68         // 6.应用的名称
     69         UILabel *nameLabel = [[UILabel alloc] init];
     70         // 设置背景色
     71 //        nameLabel.backgroundColor = [UIColor yellowColor];
     72         CGFloat nameX = 0;
     73         //        CGFloat nameY = iconY + iconH;
     74         CGFloat nameY = CGRectGetMaxY(iconView.frame);
     75         CGFloat nameW = appViewW;
     76         CGFloat nameH = 21;
     77         // 设置frame
     78         nameLabel.frame = CGRectMake(nameX, nameY, nameW, nameH);
     79         // 把名称标签添加到父控件中
     80         [appView addSubview:nameLabel];
     81         
     82         // 设置应用名称
     83         nameLabel.text = dict[@"name"];
     84         // 设置字体大小
     85         nameLabel.font = [UIFont systemFontOfSize:13.0];
     86         // 设置文字居中
     87         nameLabel.textAlignment = NSTextAlignmentCenter;
     88         
     89         // 7.下载按钮
     90         UIButton *downloadBtn = [[UIButton alloc] init];
     91 //        downloadBtn.backgroundColor = [UIColor redColor];
     92         CGFloat downloadX = iconX;
     93         CGFloat downloadY = CGRectGetMaxY(nameLabel.frame);
     94         CGFloat downlaodW = iconW;
     95         CGFloat downlaodH = 30;
     96         downloadBtn.frame = CGRectMake(downloadX, downloadY, downlaodW, downlaodH);
     97         [appView addSubview:downloadBtn];
     98         
     99         // 设置按钮背景图片
    100         [downloadBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen"] forState:UIControlStateNormal];
    101         [downloadBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen_highlighted"] forState:UIControlStateDisabled];
    102         // 设置按钮的文字
    103         [downloadBtn setTitle:@"下载" forState:UIControlStateNormal];
    104         [downloadBtn setTitle:@"已下载" forState:UIControlStateDisabled];
    105         
    106         downloadBtn.titleLabel.font = [UIFont systemFontOfSize:15];
    107     }
    108 }
    109 
    110 
    111 #pragma mark - 懒加载
    112 - (NSArray *)appes {
    113     // 1.判断当前的数组属性是否为空,如果为空的时候再去加载数据
    114     if (_appes == nil) {
    115         // 2.获取plist文件路径
    116         NSString *path = [[NSBundle mainBundle] pathForResource:@"app.plist" ofType:nil];
    117         // 3.加载plist文件
    118         NSArray *dictArr = [NSArray arrayWithContentsOfFile:path];
    119         _appes = dictArr;
    120         
    121     }
    122     
    123     return _appes;
    124     
    125 }
    126 
    127 @end

    三、向九宫格内加数据

      1 #import "ViewController.h"
      2 
      3 @interface ViewController ()
      4 /**
      5  *  用来存放所有数据的数组
      6  */
      7 @property (nonatomic, strong) NSArray *appes;
      8 @end
      9 
     10 @implementation ViewController
     11 
     12 /**
     13  *  控制器的view加载完成之后就会调用此方法
     14  */
     15 - (void)viewDidLoad {
     16     [super viewDidLoad];
     17     // 格子之间的间距
     18     CGFloat margin = 20;
     19     // 格子的宽
     20     CGFloat appViewW = 100;
     21     // 格子的高
     22     CGFloat appViewH = 120;
     23     // 一行中用三个格子
     24     NSInteger column = 3;
     25     // 最左边的间距  =  (控制器view的宽 - 一行中所有格子的宽 - 格子间的间距 * (一行中格子的个数 - 1)) * 0.5
     26     CGFloat leftMargin = (self.view.bounds.size.width - appViewW * column - (column - 1) *margin) * 0.5;
     27     CGFloat topMargin = leftMargin;
     28     // 根据数据的个数来动画创建每一个应用
     29     for (NSInteger i = 0; i < self.appes.count; i++) {
     30         
     31         // 0>  先取出每一个应该的数据
     32         NSDictionary *dict = self.appes[i];
     33         
     34         // 1.创建appView 用来装里面的子控件
     35         UIView *appView = [[UIView alloc] init];
     36         // 2.设置背景色
     37 //        appView.backgroundColor = [UIColor blueColor];
     38         
     39         // 计算列号
     40         NSInteger col = i % column;
     41         // 计算行号
     42         NSInteger row = i / column;
     43         // 3.设置frame
     44         // 计算appViewX = 左边间距  + (appView宽 + 格子间距) *当前格子是在当前行中的第几个
     45         CGFloat appViewX = leftMargin + (appViewW + margin) * col;
     46         // appViewY = 顶部间距  + (appViewH + margin) * 当前格子在当前列中是第几个
     47         CGFloat appViewY = topMargin + (appViewH + margin) * row;
     48         
     49         appView.frame = CGRectMake(appViewX, appViewY, appViewW, appViewH);
     50         
     51         // 4.添加到控制器的view中
     52         [self.view addSubview:appView];
     53         
     54         // 5.创建应用图片
     55         UIImageView *iconView = [[UIImageView alloc] init];
     56         // 设置背景色
     57 //        iconView.backgroundColor = [UIColor purpleColor];
     58         CGFloat iconW = 70;
     59         CGFloat iconH = iconW;
     60         CGFloat iconX = (appViewW - iconW) * 0.5;
     61         CGFloat iconY = 0;
     62         iconView.frame = CGRectMake(iconX, iconY, iconW, iconH);
     63         [appView addSubview:iconView];
     64         
     65         // 设置应用图片
     66         iconView.image = [UIImage imageNamed:dict[@"icon"]];
     67         
     68         // 6.应用的名称
     69         UILabel *nameLabel = [[UILabel alloc] init];
     70         // 设置背景色
     71 //        nameLabel.backgroundColor = [UIColor yellowColor];
     72         CGFloat nameX = 0;
     73         //        CGFloat nameY = iconY + iconH;
     74         CGFloat nameY = CGRectGetMaxY(iconView.frame);
     75         CGFloat nameW = appViewW;
     76         CGFloat nameH = 21;
     77         // 设置frame
     78         nameLabel.frame = CGRectMake(nameX, nameY, nameW, nameH);
     79         // 把名称标签添加到父控件中
     80         [appView addSubview:nameLabel];
     81         
     82         // 设置应用名称
     83         nameLabel.text = dict[@"name"];
     84         // 设置字体大小
     85         nameLabel.font = [UIFont systemFontOfSize:13.0];
     86         // 设置文字居中
     87         nameLabel.textAlignment = NSTextAlignmentCenter;
     88         
     89         // 7.下载按钮
     90         UIButton *downloadBtn = [[UIButton alloc] init];
     91 //        downloadBtn.backgroundColor = [UIColor redColor];
     92         CGFloat downloadX = iconX;
     93         CGFloat downloadY = CGRectGetMaxY(nameLabel.frame);
     94         CGFloat downlaodW = iconW;
     95         CGFloat downlaodH = 30;
     96         downloadBtn.frame = CGRectMake(downloadX, downloadY, downlaodW, downlaodH);
     97         [appView addSubview:downloadBtn];
     98         
     99         // 设置按钮背景图片
    100         [downloadBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen"] forState:UIControlStateNormal];
    101         [downloadBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen_highlighted"] forState:UIControlStateDisabled];
    102         // 设置按钮的文字
    103         [downloadBtn setTitle:@"下载" forState:UIControlStateNormal];
    104         [downloadBtn setTitle:@"已下载" forState:UIControlStateDisabled];
    105         
    106         downloadBtn.titleLabel.font = [UIFont systemFontOfSize:15];
    107     }
    108 }
    109 
    110 
    111 #pragma mark - 懒加载
    112 - (NSArray *)appes {
    113     // 1.判断当前的数组属性是否为空,如果为空的时候再去加载数据
    114     if (_appes == nil) {
    115         // 2.获取plist文件路径
    116         NSString *path = [[NSBundle mainBundle] pathForResource:@"app.plist" ofType:nil];
    117         // 3.加载plist文件
    118         NSArray *dictArr = [NSArray arrayWithContentsOfFile:path];
    119         _appes = dictArr;
    120         
    121     }
    122     
    123     return _appes;
    124     
    125 }

    代码问题

    我们是直接通过字典的键名获取plist中的数据信息,在viewController中需要直接和数据打交道,如果需要多次使用可能会因为不小心把键名写错,而程序并不报错。鉴于此,可以考虑把字典数据转换成一个模型,把数据封装到一个模型中去,让viewController不再直接和数据打交道,而是和模型交互。

    一般情况下,设置数据和取出数据都使用“字符串类型的key”,编写这些key时,编辑器没有智能提示,需要手敲。如:

    dict[@"name"] = @"Jack";

    NSString *name = dict[@"name"];

    手敲字符串key,key容易写错

    Key如果写错了,编译器不会有任何警告和报错,造成设错数据或者取错数据

    四、字典转模型

    字典转模型的好处:

    (1)降低代码的耦合度

    (2)所有字典转模型部分的代码统一集中在一处处理,降低代码出错的几率

    (3)在程序中直接使用模型的属性操作,提高编码效率 

    (4)调用方不用关心模型内部的任何处理细节

    字典转模型的注意点:

    模型应该提供一个可以传入字典参数的构造方法

    - (instancetype)initWithDict:(NSDictionary *)dict;

    + (instancetype)xxxWithDict:(NSDictionary *)dict;

    提示:在模型中合理地使用只读属性,可以进一步降低代码的耦合度。

    #import <Foundation/Foundation.h>
    /** NSString block用copy
     除了字符串和 block 控件 其它基本OC对象都用strong
     weak  控件最好都用
     assgin 基本数据类型
     */
    @interface HMApp : NSObject
    /** 应用图片 */
    @property (nonatomic, copy) NSString *icon;
    /** 应用名称 */
    @property (nonatomic, copy) NSString *name;
    
    // id他instancetype有什么区别
    // 1.相同点:他们都可以当方法的返回值
    // 2.不同点:id可以来声明变量,id可以当参数类型,
    // id是一个万能指针,他在编译的时候不会确定真实的类型,只有在运行时候才知道真实类型
    // instancetype在编译的时候就能确定他的返回值真实类型,这种更安全
    //clang 3.5
    /**  */
    - (instancetype)initWithDict:(NSDictionary *)dict;//用字典实例化对象的成员方法
    + (instancetype)appWithDict:(NSDictionary *)dict;//用字典实例化对象的类方法,又称工厂方法
    @end
    
    #import "HMApp.h"
    
    @implementation HMApp
    - (instancetype)initWithDict:(NSDictionary *)dict {
        if (self = [super init]) {
            self.icon = dict[@"icon"];
            self.name = dict[@"name"];

            [self setValuesForKeysWithDictionary:dict];//加载所有属性

        }
        
        return self;
    }
    
    + (instancetype)appWithDict:(NSDictionary *)dict {
        return [[self alloc] initWithDict:dict];
    }
    @end
    
    /**
     xib和stroyboard的区别
     1.相同点:都可以用来我们软件界面
     2.不同点:在storyboard可以用来搭建整个应用的所有界面,最少也是一个界面
     xib可以用来描述一个界面中的某一个部分,或一个应用的一个界面,及整个界面
     */
    #import "ViewController.h"
    #import "HMApp.h"
    
    @interface ViewController ()
    /**
     *  用来存放所有数据的数组
     */
    @property (nonatomic, strong) NSArray *appes;
    @end
    
    @implementation ViewController
    
    /**
     *  控制器的view加载完成之后就会调用此方法
     */
    - (void)viewDidLoad {
        [super viewDidLoad];
        // 格子之间的间距
        CGFloat margin = 20;
        // 格子的宽
        CGFloat appViewW = 100;
        // 格子的高
        CGFloat appViewH = 120;
        // 一行中用三个格子
        NSInteger column = 3;
        // 最左边的间距  =  (控制器view的宽 - 一行中所有格子的宽 - 格子间的间距 * (一行中格子的个数 - 1)) * 0.5
        CGFloat leftMargin = (self.view.bounds.size.width - appViewW * column - (column - 1) *margin) * 0.5;
        CGFloat topMargin = leftMargin;
        // 根据数据的个数来动画创建每一个应用
        for (NSInteger i = 0; i < self.appes.count; i++) {
            
            
      
            // 0>  先取出每一个应该的数据
    //        NSDictionary *dict = self.appes[i];
            // 取数组中每一个用来表示数据的模型对象
            HMApp *app = self.appes[i];
            
            // 1.创建appView 用来装里面的子控件
            UIView *appView = [[UIView alloc] init];
            // 2.设置背景色
    //        appView.backgroundColor = [UIColor blueColor];
            
            // 计算列号
            NSInteger col = i % column;
            // 计算行号
            NSInteger row = i / column;
            // 3.设置frame
            // 计算appViewX = 左边间距  + (appView宽 + 格子间距) *当前格子是在当前行中的第几个
            CGFloat appViewX = leftMargin + (appViewW + margin) * col;
            // appViewY = 顶部间距  + (appViewH + margin) * 当前格子在当前列中是第几个
            CGFloat appViewY = topMargin + (appViewH + margin) * row;
            
            appView.frame = CGRectMake(appViewX, appViewY, appViewW, appViewH);
            
            // 4.添加到控制器的view中
            [self.view addSubview:appView];
            
            // 5.创建应用图片
            UIImageView *iconView = [[UIImageView alloc] init];
            // 设置背景色
    //        iconView.backgroundColor = [UIColor purpleColor];
            CGFloat iconW = 70;
            CGFloat iconH = iconW;
            CGFloat iconX = (appViewW - iconW) * 0.5;
            CGFloat iconY = 0;
            iconView.frame = CGRectMake(iconX, iconY, iconW, iconH);
            [appView addSubview:iconView];
            
            // 设置应用图片
            iconView.image = [UIImage imageNamed:app.icon];
            
            // 6.应用的名称
            UILabel *nameLabel = [[UILabel alloc] init];
            // 设置背景色
    //        nameLabel.backgroundColor = [UIColor yellowColor];
            CGFloat nameX = 0;
            //        CGFloat nameY = iconY + iconH;
            CGFloat nameY = CGRectGetMaxY(iconView.frame);
            CGFloat nameW = appViewW;
            CGFloat nameH = 21;
            // 设置frame
            nameLabel.frame = CGRectMake(nameX, nameY, nameW, nameH);
            // 把名称标签添加到父控件中
            [appView addSubview:nameLabel];
            
            // 设置应用名称
            nameLabel.text = app.name;
            // 设置字体大小
            nameLabel.font = [UIFont systemFontOfSize:13.0];
            // 设置文字居中
            nameLabel.textAlignment = NSTextAlignmentCenter;
            
            // 7.下载按钮
            UIButton *downloadBtn = [[UIButton alloc] init];
    //        downloadBtn.backgroundColor = [UIColor redColor];
            CGFloat downloadX = iconX;
            CGFloat downloadY = CGRectGetMaxY(nameLabel.frame);
            CGFloat downlaodW = iconW;
            CGFloat downlaodH = 30;
            downloadBtn.frame = CGRectMake(downloadX, downloadY, downlaodW, downlaodH);
            [appView addSubview:downloadBtn];
            
            // 设置按钮背景图片
            [downloadBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen"] forState:UIControlStateNormal];
            [downloadBtn setBackgroundImage:[UIImage imageNamed:@"buttongreen_highlighted"] forState:UIControlStateDisabled];
            // 设置按钮的文字
            [downloadBtn setTitle:@"下载" forState:UIControlStateNormal];
            [downloadBtn setTitle:@"已下载" forState:UIControlStateDisabled];
            
            downloadBtn.titleLabel.font = [UIFont systemFontOfSize:15];
        }
    }
    
    
    #pragma mark - 懒加载
    - (NSArray *)appes {
        // 1.判断当前的数组属性是否为空,如果为空的时候再去加载数据
        if (_appes == nil) {
            // 2.获取plist文件路径
            NSString *path = [[NSBundle mainBundle] pathForResource:@"app.plist" ofType:nil];
            // 3.加载plist文件
            NSArray *dictArr = [NSArray arrayWithContentsOfFile:path];
    
            // 创建一个可变数据用来保存每一个模型对象(创建可变数组时并给其分配好容量)
            NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:dictArr.count];
            // 遍历字典数组把每一个字典转换成一个模型对象
            for (NSDictionary *dict in dictArr) {
                // 创建模型对象
    //            HMApp *app = [[HMApp alloc] initWithDict:dict];
                HMApp *app = [HMApp appWithDict:dict];
                // 给模型对象中的属性赋值
    //            app.icon = dict[@"icon"];
    //            app.name = dict[@"name"];
                // 把模型添加到数组
                [arrM addObject:app];
            }
            // 把装有所有模型的数组赋值给我们的数组属性
            _appes = arrM;
            
        }
        
        return _appes;
        
    }
    @end

    五、用xib自定义view

    HMAppView.xib文件

      1 /*--------------------Model-------------------*/
      2 
      3 #import <Foundation/Foundation.h>
      4 /** NSString block用copy
      5  除了字符串和 block 控件 其它基本OC对象都用strong
      6  weak  控件最好都用
      7  assgin 基本数据类型
      8  */
      9 @interface HMApp : NSObject
     10 /** 应用图片 */
     11 @property (nonatomic, copy) NSString *icon;
     12 /** 应用名称 */
     13 @property (nonatomic, copy) NSString *name;
     14 
     15 // id他instancetype有什么区别
     16 // 1.相同点:他们都可以当方法的返回值
     17 // 2.不同点:id可以来声明变量,id可以当参数类型,
     18 // id是一个万能指针,他在编译的时候不会确定真实的类型,只有在运行时候才知道真实类型
     19 // instancetype在编译的时候就能确定他的返回值真实类型,这种更安全
     20 //clang 3.5
     21 - (instancetype)initWithDict:(NSDictionary *)dict;
     22 + (instancetype)appWithDict:(NSDictionary *)dict;
     23 @end
     24 
     25 
     26 
     27 #import "HMApp.h"
     28 
     29 @implementation HMApp
     30 - (instancetype)initWithDict:(NSDictionary *)dict {
     31     if (self = [super init]) {
     32         self.icon = dict[@"icon"];
     33         self.name = dict[@"name"];
     34     }
     35     
     36     return self;
     37 }
     38 
     39 + (instancetype)appWithDict:(NSDictionary *)dict {
     40     return [[self alloc] initWithDict:dict];
     41 }
     42 @end
     43 
     44 /*-----------------View---------------------*/
     45 #import <UIKit/UIKit.h>
     46 @class HMApp;
     47 @interface HMAppView : UIView
     48 // 把模型变成一个属性引用到自定义视图中
     49 @property (nonatomic, strong) HMApp *app;
     50 
     51 + (instancetype)appView;
     52 @end
     53 
     54 
     55 
     56 #import "HMAppView.h"
     57 #import "HMApp.h"
     58 
     59 @interface HMAppView ()
     60 /** 应用的图片 */
     61 @property (weak, nonatomic) IBOutlet UIImageView *iconView;
     62 /** 应用的名称 */
     63 @property (weak, nonatomic) IBOutlet UILabel *nameLabel;
     64 @end
     65 
     66 @implementation HMAppView
     67 // 当点击下载按钮之后调用的方法
     68 - (IBAction)downloadBtnClick:(UIButton *)downBtn {
     69     NSLog(@"%ld----%@", self.tag ,self);
     70     // 1.把按钮设置为禁用状态
     71     downBtn.enabled = NO;
     72     // 2.创建一个label
     73     UILabel *downLabel = [[UILabel alloc] init];
     74     downLabel.text = [NSString stringWithFormat:@"%@下载完成", self.app.name];
     75     // 设置字体
     76     downLabel.font = [UIFont systemFontOfSize:13.0];
     77     // 设置文字居中
     78     downLabel.textAlignment = NSTextAlignmentCenter;
     79     // 设置label的文字颜色
     80     downLabel.textColor = [UIColor redColor];
     81     // 设置背景色
     82     downLabel.backgroundColor = [UIColor blackColor];
     83     // 设置bounds
     84     downLabel.bounds = CGRectMake(0, 0, 200, 21);
     85     // 设置标签的位置在屏幕的中心
     86     downLabel.center = self.superview.center;
     87     // 把提示标签添加在控制器的view上
     88     [self.superview addSubview:downLabel];
     89     // 设置label透明度
     90     downLabel.alpha = 0.0;
     91     // 设置圆角半径
     92     downLabel.layer.cornerRadius = 5;
     93     // 把超出边界的部分裁剪掉
     94     downLabel.clipsToBounds = YES;
     95 //    downLabel.layer.masksToBounds = YES;
     96     
     97     //执行一个两秒中的动画,让标签慢慢的显示
     98     [UIView animateWithDuration:2.0 animations:^{ // 表示要执行的动画代码
     99         // 设置label的透明度
    100         downLabel.alpha = 0.7;
    101     } completion:^(BOOL finished) { // 表示动画执行完成之后要做得事情
    102         // 上面的动画执行完成之后推迟2秒之后执行一个2秒的一个动画
    103         [UIView animateWithDuration:2.0 delay:2.0 options:UIViewAnimationOptionCurveLinear animations:^{
    104             // 设置label透明度
    105             downLabel.alpha = 0.0;
    106         } completion:^(BOOL finished) { // 动画执行完成之后把label从父控件中移除
    107             // 把这个label从它的父控件中移除掉
    108             [downLabel removeFromSuperview];
    109         }];
    110     }];
    111 }
    112 
    113 
    114 + (instancetype)appView {
    115     return [[[NSBundle mainBundle] loadNibNamed:@"HMAppView" owner:nil options:nil]lastObject];
    116 }
    117 // 重写模型属性的set方法,当外部给自定义视图的模型属性赋值时就会调用此方法,
    118 - (void)setApp:(HMApp *)app {
    119 #warning mark - 重写set方法一定要注意给属性下划线的成员变量赋值
    120     _app = app;
    121     // 1.设置应用图片
    122     self.iconView.image = [UIImage imageNamed:app.icon];
    123     // 2.设置应用名称
    124     self.nameLabel.text = app.name;
    125     
    126 
    127 }
    128 
    129 
    130 @end
    131 
    132 /*-------------------Controller--------------*/
    133 /**
    134  xib和stroyboard的区别
    135  1.相同点:都可以用来我们软件界面
    136  2.不同点:在storyboard可以用来搭建整个应用的所有界面,最少也是一个界面
    137  xib可以用来描述一个界面中的某一个部分,或一个应用的一个界面,及整个界面
    138  */
    139 #import "ViewController.h"
    140 #import "HMApp.h"
    141 #import "HMAppView.h"
    142 
    143 @interface ViewController ()
    144 /**
    145  *  用来存放所有数据的数组
    146  */
    147 @property (nonatomic, strong) NSArray *appes;
    148 @end
    149 
    150 @implementation ViewController
    151 
    152 /**
    153  *  控制器的view加载完成之后就会调用此方法
    154  */
    155 - (void)viewDidLoad {
    156     [super viewDidLoad];
    157     // 格子之间的间距
    158     CGFloat margin = 20;
    159 //    // 格子的宽
    160 //    CGFloat appViewW = 100;
    161 //    // 格子的高
    162 //    CGFloat appViewH = 120;
    163     // 一行中用三个格子
    164     NSInteger column = 3;
    165     // 根据数据的个数来动画创建每一个应用
    166     for (NSInteger i = 0; i < self.appes.count; i++) {
    167         
    168         // 1.创建appView
    169         HMAppView *appView = [HMAppView appView];
    170         appView.tag = i;
    171         // 2.取数组中每一个用来表示数据的模型对象
    172         HMApp *app = self.appes[i];
    173         // 3.给自定义视图传递模型
    174         appView.app = app;
    175         // 自定义视图的大小应该根据xib中的视图的大小来动态的设置而不应该直接固定死
    176         // 4.拿到xib中appView的宽
    177         CGFloat appViewW = appView.frame.size.width;
    178         // xib中appView的高来表示我们一个视图的高
    179         CGFloat appViewH = appView.frame.size.height;
    180         // 最左边的间距  =  (控制器view的宽 - 一行中所有格子的宽 - 格子间的间距 * (一行中格子的个数 - 1)) * 0.5
    181         CGFloat leftMargin = (self.view.bounds.size.width - appViewW * column - (column - 1) *margin) * 0.5;
    182         // 头部间距
    183         CGFloat topMargin = leftMargin;
    184         // 计算列号
    185         NSInteger col = i % column;
    186         // 计算行号
    187         NSInteger row = i / column;
    188         // 3.设置frame
    189         // 计算appViewX = 左边间距  + (appView宽 + 格子间距) *当前格子是在当前行中的第几个
    190         CGFloat appViewX = leftMargin + (appViewW + margin) * col;
    191         // appViewY = 顶部间距  + (appViewH + margin) * 当前格子在当前列中是第几个
    192         CGFloat appViewY = topMargin + (appViewH + margin) * row;
    193         
    194         appView.frame = CGRectMake(appViewX, appViewY, appViewW, appViewH);
    195         
    196         // 4.添加到控制器的view中
    197         [self.view addSubview:appView];
    198         
    199     }
    200 }
    201 
    202 
    203 #pragma mark - 懒加载
    204 - (NSArray *)appes {
    205     // 1.判断当前的数组属性是否为空,如果为空的时候再去加载数据
    206     if (_appes == nil) {
    207         // 2.获取plist文件路径
    208         NSString *path = [[NSBundle mainBundle] pathForResource:@"app.plist" ofType:nil];
    209         // 3.加载plist文件
    210         NSArray *dictArr = [NSArray arrayWithContentsOfFile:path];
    211         
    212         // 创建一个可变数据用来保存每一个模型对象(创建可变数组时并给其分配好容量)
    213         NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:dictArr.count];
    214         // 遍历字典数组把每一个字典转换成一个模型对象
    215         for (NSDictionary *dict in dictArr) {
    216             // 创建模型对象
    217             //            HMApp *app = [[HMApp alloc] initWithDict:dict];
    218             HMApp *app = [HMApp appWithDict:dict];
    219             // 给模型对象中的属性赋值
    220             //            app.icon = dict[@"icon"];
    221             //            app.name = dict[@"name"];
    222             // 把模型添加到数组
    223             [arrM addObject:app];
    224         }
    225         // 把装有所有模型的数组赋值给我们的数组属性
    226         _appes = arrM;
    227         
    228     }
    229     
    230     return _appes;
    231     
    232 }
    233 
    234 
    235 /** 用xib来自定义一个视图的步骤
    236 1.创建一个xib文件用来描述我们局部界面(并在里面摆放好所有的子控件,并设置好它们的属性)
    237 2.创建一个类来和我们的xib文件进行关联(这个类也是用来管理我们的xib文件,创建的自定义类的类名最好和我们xib的文件名称一样)(创建的类它要继承至什么类,取决于xib文件中最顶层控件的类型)
    238 3.指定xib中class类型,如果不指定创建出来的xib和我们的自定义类没有任何关系
    239  4.把需要修改或需要设置的控件连线到我们自定义类.m中
    240  5.在自定义view类的.h文件引入模型,就是把模型当成一个属性定义在我们的自定义view类中
    241  6.重写模型属性set方法,当外部给这个模型属性赋值的时候就会调用模型属性的set方法,我们在重写的模型属性的set方法中给自定义视图的子控件去设置数据
    242  
    243 */
    244 
    245 @end

    补充说明

     View的封装思路

    (1) 如果一个view内部的子控件比较多,一般会考虑自定义一个view,把它内部子控件的创建屏蔽起来,不让外界关心

    (2) 外界可以传入对应的模型数据给view,view拿到模型数据后给内部的子控件设置对应的数据

    mvc机制简单说明

    说明:

    (1)在开发过程中,作为控制器处理的量级应该很轻,不该操心的不操心。协调好模型和视图就ok了,要学会当一个好老板。

    (2)三个部分各司其职,数据模型只负责数据的处理,视图部分只负责把拿到的数据进行显示,两个部分都是被动的,等待着大管家控制器的调遣。

    (3)在OC中,如果视图和数据模型之间有通道,那控制器是否处于失控状态呢?

    1.MVC是一种设计思想,贯穿于整个iOS开发中,需要积累一定的项目经验,才能深刻体会其中的含义和好处
      MVC中的三个角色
      M:Model,模型数据
      V:View,视图(界面)
      C:Control,控制中心
     
    2.MVC的几个明显的特征和体现:
     View上面显示什么东西,取决于Model
     只要Model数据改了,View的显示状态会跟着更改
     Control负责初始化Model,并将Model传递给View去解析展示
  • 相关阅读:
    第十五周学习进度
    梦断代码阅读笔记03
    第二阶段绩效评估
    团队十日冲刺20
    团队十日冲刺19
    团队十日冲刺18
    Java中的变量之成员变量、本地变量与类变量
    Java中new一个对象是一个怎样的过程?JVM中发生了什么?
    HashMap三百问
    容器之List接口下各实现类(Vector,ArrayList 和LinkedList)的线程安全问题
  • 原文地址:https://www.cnblogs.com/oc-bowen/p/5084161.html
Copyright © 2020-2023  润新知