本文共 5539 字,大约阅读时间需要 18 分钟。
什么是MVC?
MVC逻辑示意图
我们接下来通过MVC模式要实现的界面:
文件夹结构:
自定义视图AppView.xib示意图:
控制层:
"ViewController.h"
//// ViewController.m//// Created by Long.// Copyright © 2016年 LongChuang. All rights reserved.//#import "ViewController.h"#import "AppData.h"#import "AppView.h"@interface ViewController ()@end@implementation ViewController{ // 用于接收loadAppData从网络解析得到的数据 // 可以接受可变数组,因为可变数组继承于NSArray NSArray * _appData;}- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // 1.加载数据 _appData = [self loadAppData]; // 2.刷新UI [self updateUI];}- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}// 一.第一步加载数据-(NSArray *)loadAppData{ // 1.(获取地址)获取url,得到plist文件的位置 NSURL *url = [[NSBundle mainBundle] URLForResource:@"apps.plist" withExtension:nil]; // 2.(通过地址下载)通过数组或者字典的url解析方法加载plist文件, // *使用nsarray或者NSDictionary的方法,因为集合类型只有这两种 NSArray *arr = [NSArray arrayWithContentsOfURL:url]; // 3.(定义容器用于保存解压得到的数据)解析plist文件中的内容 // 定义一个容器,用于接收解压得到的内容,用可变数组就可以,因为可接收oc对象类型的任何数据 NSMutableArray *arrM = [NSMutableArray array]; // 4.(解压下载得到的压缩包,既未拆封的集合)开始遍历解压,解析下载得到的NSArray中的NSDictionary内容 for (NSDictionary *dic in arr) { // 5.定义一个解压工具(数据层的功能) // 解析数据的方法不应该出现在view层,所以应该把解析数据的方法写在model层 // 既写到appData类的方法中 // 首先需要创建一个数据层的类对象,并且把解压得到的数据保存到类的属性中去 // 这里通过调用类方法,在类的方法内部实现了 1.类对象的创建 2.类对象属性的赋值 // 并且类方法的返回值是当前类的实例对象 // 这里定义解压工具的原因是,从网络下载得到的数据,要么是字典,要么是数组,不管是什么集合 // 一般都是提前规定好的,但是里面的key在之后的开发中可能会发生修改或者增加其他key // 如果plist文件的内容发生改变,我们只需要修改数据的解压工具即可,降低耦合度,增加联动性 AppData *app = [AppData appWithDict:dic]; // 6.把解压得到的数据保存到我们事先准备好的容器中 // 如果我们不通过数据解压工具,那么这里保存的是一个字典 // 现在arrM中保存的是AppData类对象,而且这些类对象中都有和字典key相对应的属性,并且已经赋值 [arrM addObject:app]; } // 7.返回从网络加载并且解析后得到的所有数据 return arrM;}// 二.刷新UI界面-(void)updateUI{ // 1.我们可以创建UIButton,UIView // 这里创建的是我们自定义的视图,名字叫AppView // 通过调用一个类方法来返回自定义视图,不能使用alloc init是因为需要一些操作来找到我们自定义的视图 // 然后才能初始化,我们把这一系列的操作都封装到了appView的这个类方法中 AppView *appView = [AppView createAppView]; // 2.这里需要一些计算,来根据生成的个数自动的设置我们生成的AppView应该在什么位置 // 定义行数,我们规定一行显示3个应用图标,所以这里设置为3 NSInteger colNum = 3; // 3.获取自定义视图模板的宽和高 CGFloat appW = appView.bounds.size.width; CGFloat appH = appView.bounds.size.height; // 4.计算应用图标与手机屏幕的间距:屏幕的宽度-3个应用的宽度,再除以应用个数+1,因为3个应用会有4个间距 CGFloat margin = (self.view.bounds.size.width - (appW * colNum)) / (colNum + 1); // 5.根据从网络获取到的数据_appData是一个数组,根据它的长度循环创建自定义视图 for (NSInteger i = 0 ; i < _appData.count; i++) { // (1) 创建自定义的appView视图 AppView *appView = [AppView createAppView]; // (2) 计算当前的视图属于第几竖列 NSInteger col = i % colNum; // (3)计算视图x的位置:左间距margin+(自定义视图的宽+间距margin)*当前第几列竖列 CGFloat appX = margin + (appW + margin) * col; // (4)计算当前的视图属于第几横行 NSInteger row = i / colNum; // (5)计算视图y的位置:上间距margin+(自定义视图的高+间距margin)*当前第几横行 CGFloat appY = margin + (appH + margin) * row; // (6)x,y,宽和高,都已经计算好了,直接设置生成的视图位置 appView.frame = CGRectMake(appX, appY, appW, appH); // (7)给属性appData赋值,使用了set方法,我们在set方法中做了设置 // _appData[i]是一个数组,我们在从网络拉取数据的时候,就使用的AppData类进行的解析 // 所以数组中保存的数据不是字典,是AppData类型的,所以这里可以使用_appData[i]进行赋值 // 赋值的同时通过set方法,修改自定义视图的应用图标和应用名称 appView.appData = _appData[i]; // (8)把生成的自定义视图添加到view主视图上 [self.view addSubview:appView]; } }@end
模型层:
"AppData.h"
//// AppData.m//// Created by Long.// Copyright © 2016年 LongChuang. All rights reserved.//#import "AppData.h"@implementation AppData+(instancetype)appWithDict:(NSDictionary *) dict{ // 1.创建appData对象,并且将从字典获取的数据赋值给对象的属性 // 这里使用self创建对象,谁调用谁创建,返回父类 // 根据多态的特性可知,我们这里定义的是父类AppData类型,但实际创建的是调用此类方法的类对象的类型 // 如:一个集成AppData的子类调用,那么创建的就是子类类型 AppData *app = [[self alloc] init]; // 这里用于给类对象的属性赋值,现在假设字典中只有这2个key,我们完全可以手动写入 // 如果字典中有几百个key,写起来就不那么方便了 // app.name = dict[@"name"]; // app.icon = dict[@"icon"]; // 所以,所以,所以,可以调用系统自带的方法 // 可以自动识别app对象中的属性与dict字典中key 并且自动赋值 // 注意:类对象中的属性名称一定要与字典中的key关键字相同 [app setValuesForKeysWithDictionary:dict]; // 这里返回类对象,因为类对象中保存了从字典中解析得到的数据,并且赋值给类对象的属性 // 返回一个属性已经赋值的类对象给view视图 return app;}@end
显示层:
"AppView.h"
//// AppView.m//// Created by Long.// Copyright © 2016年 LongChuang. All rights reserved.//#import "AppView.h"#import "AppData.h"@interface AppView()// 自定义视图中的:应用图标@property (weak, nonatomic) IBOutlet UIImageView *iconName;// 自定义视图中的:应用名字@property (weak, nonatomic) IBOutlet UILabel *contextLabel;@end@implementation AppView// 返回当前自定义视图的类对象+(instancetype)createAppView{ // 这里相当于我们自定义了一个视图,也是UIView类型的,但是我们不想用系统的UIView类型 // 需要一个自定义的模板 // 第一步就是找到我们自定义的模板 // 第二步就是根据模板创建对象 // 1.(找到自定义模板)自定义的视图是xib格式,与代码中UINib是同一个东西,也叫nib // 首先需要找到我们自定义的视图nib 这里的@"AppView"不是当前类的名字,是自定义xib的名字AppView.xib // bundel设置为nil,就表示使用的是mainBundle UINib *nib = [UINib nibWithNibName:@"AppView" bundle:nil]; // 2.(通过自定义模板创建对象)通过nib实例化对象 AppView *appView = [[nib instantiateWithOwner:nil options:nil]firstObject]; // 3.返回自定义视图对象给方法调用者 return appView;}// 在控制层循环创建视图的时候,把从网络获取的数据复制给这个AppData-(void)setAppData:(AppData *)appData{ // 把网络数据中的应用图片名字和应用程序名字赋值给视图的属性 _appData = appData; self.iconName.image = [UIImage imageNamed:appData.icon]; self.contextLabel.text = appData.name;}@end