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去解析展示
原文地址:https://www.cnblogs.com/oc-bowen/p/5084161.html