首页 > 代码库 > [iOS UI进阶 - 2.2] 彩票Demo v1.2 UICollectionView基本

[iOS UI进阶 - 2.2] 彩票Demo v1.2 UICollectionView基本

A.需要掌握的
  1. 设计、实现设置界面
  2. cell的封装
  3. UICollectionView的使用
  4. 自定义UICollectionView
  5. 抽取控制器父类
  6. “帮助”功能
 code source: https://github.com/hellovoidworld/HelloLottery
 
B.实现
1.探讨“设置”界面的实现方案
(1)“设置”界面可以采用的做法
  • static cell(呆板,完全没有动态)
  • 使用代码,条件判断逐个编写(麻烦,代码冗长)
  • 使用模型、plist加载(能够动态配置跳转控制器,不能配置请求代码;由于使用字符串配置跳转控制器名,容易出现运行时错误)
 
(2)“设置”界面最终的做法
a.使用模型封装每个cell的数据(item),使用Class作为跳转控制器属性(这样就能经过编译检测)
b.在“设置”控制器延迟加载数据,设置每个cell的数据
 
 
2.“设置”界面控制器基类
(1)创建cell的数据模型group
组:包含了头部和尾部数据,还有最重要的内容数据items
group:
  • 头部标题
  • 尾部标题
  • items

(2)创建cell的数据模型item
包含了每个cell的内容,有图标、标题,根据有部分控件的不同,创建不同的item类
item:
  • 图标
  • 标题
  • 跳转目标控制器类
 
各种不同类型的item数据模型类:
技术分享
 
(3)创建自定义cell,包含图标、标题、右部分控件(暂时有跳转箭头、开关、文本)
  • 设置数据成员(item模型数据属性)
  • 加载数据
  • 提供静态初始化方法cellWithTableView,使用缓冲池重用
  • 细分子类自定义“开关”item(不能点击跳转,拥有一个开关)
  • 细分子类自定义“箭头”item(点击跳转,没有开关)
  • 默认的请求item(不能点击跳转,没有开关,内置block成员)
 
根据item的类型,描绘cell的外观:
 1 /** 设置右部分控件 */ 2 - (void) setupRightView { 3     if ([self.item isKindOfClass:[HVWArrowSettingItem class]]) { // 跳转箭头类型 4         self.accessoryView = self.arrowView; 5     } else if ([self.item isKindOfClass:[HVWSwitchSettingItem class]]) { // 开关类型 6         self.accessoryView = self.switchView; 7     } else if ([self.item isKindOfClass:[HVWLabelSettingItem class]]) { // 标签类型 8         self.accessoryView = self.labelView; 9     } else {10         self.accessoryView = nil;11     }12 }
 
技术分享
 
 
(4)执行代码属性block
  • 使用copy修饰
  • 使用MBProgressHUD外部包进行提示信息显示,模拟交互效果
 
技术分享
 
a.定义block成员
 1 // 2 //  HVWSettingItem.h 3 //  HelloLottery 4 // 5 //  Created by hellovoidworld on 15/1/6. 6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved. 7 // 8  9 #import <Foundation/Foundation.h>10 11 typedef void (^RunningBlock)();12 13 /** item基类 */14 @interface HVWSettingItem : NSObject15 16 /** 图标 */17 @property(nonatomic, copy) NSString *icon;18 19 /** 标题 */20 @property(nonatomic, copy) NSString *title;21 22 /** block代码 */23 @property(nonatomic, copy) RunningBlock runningBlock;24 25 + (instancetype) itemWithIcon:(NSString *) icon title:(NSString *) title;26 + (instancetype) itemWithTitle:(NSString *) title;27 28 @end
 
b.配置block
使用外部包辅助进行弹窗信息的显示
 
 1    // 第1组 2     HVWSettingItem *updateCheckItem = [HVWArrowSettingItem itemWithIcon:@"MoreUpdate" title:@"检查新版本"]; 3     4     // 检查新版本配置一个block,模拟更新过程 5     updateCheckItem.runningBlock = ^{ 6         // 弹窗提示 7         [MBProgressHUD showMessage:@"正在使出吃奶的劲儿检查中..."]; 8         9         // 模拟发送网络请求延迟10         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{11             // 隐藏消息框12             [MBProgressHUD hideHUD];13            14             // 提醒版本信息15             [MBProgressHUD showError:@"没有发现新版本!"];16         });17     };
 
(5)自定义继承UITableViewController的HVWBaseSettingViewController作为“设置”界面的控制器父类,为“设置”的若干个子界面提供公共逻辑代码。
包括功能
  • 加载解析group和item数据
  • 根据group数据创建table的样式
  • 根据不同的item模型创建不同的cell外观
  • 如果item类是跳转类型的,需要配置跳转目标控制器
  • 如果item类是代码执行型的,需要配置block代码
 
a.使用同一个BaseSettingViewController作为父类,配置了不同数据的界面
技术分享
 
b.所有类“设置”界面控制器:
技术分享
c.MVC
技术分享
 
 
3.部分代码
a.“设置”跳转控制器父类:
  1 //  2 //  HVWBaseSettingViewController.m  3 //  HelloLottery  4 //  5 //  Created by hellovoidworld on 15/1/6.  6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.  7 //  8   9 #import "HVWBaseSettingViewController.h" 10 #import "HVWSettingGroup.h" 11 #import "HVWSettingItem.h" 12 #import "HVWSettingCell.h" 13 #import "HVWArrowSettingItem.h" 14  15 @interface HVWBaseSettingViewController () 16  17 @end 18  19 @implementation HVWBaseSettingViewController 20  21 - (void)viewDidLoad { 22     [super viewDidLoad]; 23     24     25 } 26  27 - (void)didReceiveMemoryWarning { 28     [super didReceiveMemoryWarning]; 29     // Dispose of any resources that can be recreated. 30 } 31  32 /** 重写初始化方法  33 * 一定要使用group样式 34 */ 35 - (instancetype)init { 36     return [super initWithStyle:UITableViewStyleGrouped]; 37 } 38  39 - (instancetype)initWithStyle:(UITableViewStyle)style { 40     return [super initWithStyle:UITableViewStyleGrouped]; 41 } 42  43 /** 加载空数据 */ 44 - (NSMutableArray *)data { 45     if (nil == _data) { 46         _data =http://www.mamicode.com/ [NSMutableArray array]; 47     } 48     49     return _data; 50 } 51  52 #pragma mark - Table view data source 53  54 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 55     return self.data.count; 56 } 57  58 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 59     HVWSettingGroup *group = self.data[section]; 60     return group.items.count; 61 } 62  63 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 64     HVWSettingCell *cell = [HVWSettingCell cellWithTableView:tableView]; 65     HVWSettingGroup *group = self.data[indexPath.section]; 66     cell.item = group.items[indexPath.row]; 67     return cell; 68 } 69  70 #pragma mark - 代理方法 71 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 72     // 1.取消选中,不要保持选中状态 73     [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; 74     75     // 2.1加载点击事件 76     HVWSettingGroup *group = self.data[indexPath.section]; 77     HVWSettingItem *item = group.items[indexPath.row]; 78     79     // 2.2如果配置有block, 运行block 80     if (item.runningBlock) { 81         item.runningBlock(); 82     } 83     84     // 2.3配置跳转控制器 85     if ([item isKindOfClass:[HVWArrowSettingItem class]]) { // 如果是跳转类型的item 86         HVWArrowSettingItem *arrowItem = (HVWArrowSettingItem *) item; 87  88         89         if (nil != arrowItem.destinationViewControllerClass) { 90             UIViewController *viewController = [[arrowItem.destinationViewControllerClass alloc] init]; 91             viewController.title = arrowItem.title; 92             [self.navigationController pushViewController:viewController animated:YES]; 93         } 94     } 95 } 96  97 /** 组头部 */ 98 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 99     HVWSettingGroup *group = self.data[section];100     return group.headerTitle;101 }102 103 /** 组尾部 */104 - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {105     HVWSettingGroup *group = self.data[section];106     return group.tailTitle;107 }108 109 @end
 
b.“设置”界面控制器
 1 // 2 //  HVWSettingViewController.m 3 //  HelloLottery 4 // 5 //  Created by hellovoidworld on 15/1/6. 6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved. 7 // 8  9 #import "HVWSettingViewController.h"10 #import "HVWSettingGroup.h"11 #import "HVWArrowSettingItem.h"12 #import "HVWSwitchSettingItem.h"13 #import "HVWPushNoticeViewController.h"14 #import "MBProgressHUD+MJ.h"15 16 @interface HVWSettingViewController ()17 18 @end19 20 @implementation HVWSettingViewController21 22 - (void)viewDidLoad {23     [super viewDidLoad];24     // Do any additional setup after loading the view.25 26     // 设置标题27     self.title = @"设置";28 29     // 配置数据30     [self setupGroup0];31     [self setupGroup1];32 }33 34 - (void) setupGroup0 {35     // 第0组36     HVWSettingItem *pushItem = [HVWArrowSettingItem itemWithIcon:@"MorePush" title:@"推送和提醒" destinationViewControllerClass:[HVWPushNoticeViewController class]];37     HVWSettingItem *shakeItem = [HVWSwitchSettingItem itemWithIcon:@"handShake" title:@"摇一摇机选"];38     HVWSettingItem *soundItem = [HVWSwitchSettingItem itemWithIcon:@"sound_Effect" title:@"声音效果"];39     HVWSettingItem *assistantItem = [HVWSwitchSettingItem itemWithIcon:@"IDInfo" title:@"购彩小助手"];40    41     HVWSettingGroup *group = [[HVWSettingGroup alloc] init];42     group.items = @[pushItem, shakeItem, soundItem, assistantItem];43    44     [self.data addObject:group];45 }46 47 - (void) setupGroup1 {48     // 第1组49     HVWSettingItem *updateCheckItem = [HVWArrowSettingItem itemWithIcon:@"MoreUpdate" title:@"检查新版本"];50    51     // 检查新版本配置一个block,模拟更新过程52     updateCheckItem.runningBlock = ^{53         // 弹窗提示54         [MBProgressHUD showMessage:@"正在使出吃奶的劲儿检查中..."];55        56         // 模拟发送网络请求延迟57         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{58             // 隐藏消息框59             [MBProgressHUD hideHUD];60            61             // 提醒版本信息62             [MBProgressHUD showError:@"没有发现新版本!"];63         });64     };65    66     HVWSettingItem *checkMailItem = [HVWArrowSettingItem itemWithIcon:@"MoreMessage" title:@"查看邮箱"];67     HVWSettingItem *shareItem = [HVWArrowSettingItem itemWithIcon:@"MoreShare" title:@"分享"];68     HVWSettingItem *productRecommandItem = [HVWArrowSettingItem itemWithIcon:@"MoreNetease" title:@"产品推荐"];69     HVWSettingItem *aboutItem = [HVWArrowSettingItem itemWithIcon:@"MoreAbout" title:@"关于"];70    71     HVWSettingGroup *group = [[HVWSettingGroup alloc] init];72     group.items = @[updateCheckItem, checkMailItem, shareItem, productRecommandItem, aboutItem];73     [self.data addObject:group];74 }75 76 - (void)didReceiveMemoryWarning {77     [super didReceiveMemoryWarning];78     // Dispose of any resources that can be recreated.79 }80 81 @end
 
 
4.“产品推荐”界面
技术分享
 
基本框架:
技术分享
 
(1)UICollectionView的基本使用
a.注册cell(告诉collectionView将来创建怎样的cell)
1 [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"product"];


b.从缓存池中取出cell
1 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath2 {3 UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"product" forIndexPath:indexPath];4 5     return cell;6 }
 
b.重写init方法,创建布局参数
1 - (id)init2 {3     // 1.流水布局4     UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];5     // 2.每个cell的尺寸6     layout.itemSize = CGSizeMake(100, 100);7     return [super initWithCollectionViewLayout:layout];8 }
 
 
(2)UICollectionViewFlowLayout
UICollectionViewFlowLayout称为”流水布局”, 用来约束cell的显示

常见属性
Cell的尺寸
@property (nonatomic) CGSize itemSize;

cell之间的水平间距
@property (nonatomic) CGFloat minimumInteritemSpacing;

cell之间的垂直间距
@property (nonatomic) CGFloat minimumLineSpacing;

四周的内边距
@property (nonatomic) UIEdgeInsets sectionInset;
 
 
(3)自定义UICollectionViewCell(每个“产品”图标)
a.通过一个json文件读取数据,使用“产品”模型封装
  • 使用NSJSONSerialization解析JSON数据,得到装有字典数据的数组
  • 字典转模型
 
b.使用xib设计自定义UICollectionCell
  • 自定义类
  • xib设计
  • 自定义类的初始化、方法设计

技术分享
 
 
c.UICollectionViewController对于xib自定义UICollectionCell的适配
  • 修改重用方法
  • 在init方法中,设置cell的尺寸、水平间距、垂直间距
  • 设置四周间距
 
HVWUICollectionViewController:
  1 //  2 //  HVWProductViewController.m  3 //  HelloLottery  4 //  5 //  Created by hellovoidworld on 15/1/7.  6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.  7 //  8   9 #import "HVWProductViewController.h" 10 #import "HVWProduct.h" 11 #import "HVWProductCell.h" 12  13 @interface HVWProductViewController () 14  15 /** 数据 */ 16 @property(nonatomic, strong) NSArray *products; 17  18 @end 19  20 @implementation HVWProductViewController 21  22 static NSString * const reuseIdentifier = @"HVWProductCell"; 23  24 - (void)viewDidLoad { 25     [super viewDidLoad]; 26     27     // 1.Register cell classes 注册cell,要使用哪个CollectionViewCell 28     UINib *nib = [UINib nibWithNibName:@"HVWProductCell" bundle:[NSBundle mainBundle]]; 29     [self.collectionView registerNib:nib forCellWithReuseIdentifier:reuseIdentifier]; 30     31     // 2.设置背景色 32     self.collectionView.backgroundColor = [UIColor whiteColor]; 33 } 34  35 - (void)didReceiveMemoryWarning { 36     [super didReceiveMemoryWarning]; 37     // Dispose of any resources that can be recreated. 38 } 39  40 /** 加载JSON数据 */ 41 - (NSArray *)products { 42     if (nil == _products) { 43         // 1.读取json 44         NSString *jsonPath = [[NSBundle mainBundle] pathForResource:@"more_project.json" ofType:nil]; 45         46         // 2.加载数据 47         NSData *data =http://www.mamicode.com/ [NSData dataWithContentsOfFile:jsonPath]; 48         49         // 3.将json数据转成数组、字典,使用一个json工具类 50         NSArray *dictArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; 51         52         // 4.字典转模型 53         NSMutableArray *productArray = [NSMutableArray array]; 54         for (NSDictionary *dict in dictArray) { 55             HVWProduct *product = [HVWProduct productWithDictionary:dict]; 56             [productArray addObject:product]; 57         } 58         59         _products = productArray; 60     } 61     return _products; 62 } 63  64 /** 初始化,配置布局 */ 65 - (instancetype)init { 66     // 1.使用流水布局 67     UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; 68     69     // 2.设置cell尺寸 70     layout.itemSize = CGSizeMake(80, 80); 71     72     // 3.设置cell水平间距 73     layout.minimumInteritemSpacing = 0; 74     75     // 4.设置cell垂直间距 76     layout.minimumLineSpacing = 10; 77     78     // 5.设置四周边距 79     layout.sectionInset = UIEdgeInsetsMake(layout.minimumLineSpacing, 0, 0, 0); 80     81     // 6.配置布局方式 82     return [super initWithCollectionViewLayout:layout]; 83 } 84  85  86  87 #pragma mark <UICollectionViewDataSource> 88  89 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { 90     return 1; 91 } 92  93  94 - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 95     return self.products.count; 96 } 97  98 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { 99 100     // 1.获得cell101     HVWProductCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];102    103     // 2.配置模型数据104     cell.product = self.products[indexPath.item];105    106     return cell;107 }108 109 #pragma mark <UICollectionViewDelegate>110 /** 选择事件 */111 - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {112     HVWProduct *product = self.products[indexPath.item];113     NSLog(@"选择了app: %@", product.title);114 }115 116 @end
 
8.“帮助”模块
 
 
(1)UIWebView
加载网页
// 创建URL
NSURL *url = [[NSBundle mainBundle] URLForResource:@”abc.html” withExtension:nil];
// 创建请求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 发送请求加载网页
[webView loadRequest:request];

执行JavaScript(要等网页加载完毕才能执行)
[webView stringByEvaluatingJavaScriptFromString:js];

监听webView的加载
设置代理监听:webView.delegate = self;
 
 
(2)“帮助”界面
创建类“设置”界面控制器:HVWHelpViewController
a.加载json数据,使用HVWHtml模型封装
b.根据HVWHtml模型数据,封装出tableView的group和item用于显示
 
技术分享
 
 
(3)“帮助”条目点击查看
a.HVWHelpViewController根据html数据动态配置了所有cell的显示内容,所以要再动态配置每个cell的跳转页面,跳转用控制器HVWHtmlViewController(这个是继承UIViewController的普通控制器)
b.创建一个UIViewController,使用UIWebView加载html网页
c.HVWHtmlViewController加载完html后,执行js代码跳转到html网页的指定位置
技术分享
 
 1 // 2 //  HVWHelpViewController.m 3 //  HelloLottery 4 // 5 //  Created by hellovoidworld on 15/1/7. 6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved. 7 // 8  9 #import "HVWHelpViewController.h"10 #import "HVWHtml.h"11 #import "HVWArrowSettingItem.h"12 #import "HVWSettingGroup.h"13 #import "HVWHtmlViewController.h"14 #import "HVWNavigationController.h"15 16 @interface HVWHelpViewController ()17 18 /** html数据 */19 @property(nonatomic, strong) NSArray *htmls;20 21 @end22 23 @implementation HVWHelpViewController24 25 - (void)viewDidLoad {26     [super viewDidLoad];27     // Do any additional setup after loading the view.28    29     // 配置html数据30     // 1.创建item31     NSMutableArray *items = [NSMutableArray array];32     for (HVWHtml *html in self.htmls) {33         HVWSettingItem *item = [HVWArrowSettingItem itemWithTitle:html.title destinationViewControllerClass:nil];34         [items addObject:item];35     }36    37     // 2.创建group38     HVWSettingGroup *group = [[HVWSettingGroup alloc] init];39     group.items = items;40    41     // 3.配置到tableView42     [self.data addObject:group];43 }44 45 /** 加载数据 */46 - (NSArray *)htmls {47     if (nil == _htmls) {48         // 1.读取json49         NSString *jsonPath = [[NSBundle mainBundle] pathForResource:@"help.json" ofType:nil];50        51         // 2.解析json52         NSData *jsonData =http://www.mamicode.com/ [NSData dataWithContentsOfFile:jsonPath];53         NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];54        55         // 3.字典转模型56         NSMutableArray *htmlArray = [NSMutableArray array];57         for(NSDictionary *dict in jsonArray) {58             HVWHtml *html = [HVWHtml htmlWithDictionary:dict];59             [htmlArray addObject:html];60         }61         _htmls = htmlArray;62        63     }64     return _htmls;65 }66 67 - (void)didReceiveMemoryWarning {68     [super didReceiveMemoryWarning];69     // Dispose of any resources that can be recreated.70 }71 72 73 #pragma mark - 代理方法74 /** cell点击跳转 */75 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {76    77     HVWHtmlViewController *htmlViewController = [[HVWHtmlViewController alloc] init];78     htmlViewController.html = self.htmls[indexPath.row];79    80     /** 为了实现向上弹出效果,不使用push,而是用modal (present**);81      *  使用NaviationController是为了配置一个头部导航栏82      */83     HVWNavigationController *nv = [[HVWNavigationController alloc] initWithRootViewController:htmlViewController];84     [self.navigationController presentViewController:nv animated:YES completion:nil];85 }86 87 @end
 
 1 // 2 //  HVWHtmlViewController.m 3 //  HelloLottery 4 // 5 //  Created by hellovoidworld on 15/1/7. 6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved. 7 // 8  9 #import "HVWHtmlViewController.h"10 #import "HVWHtml.h"11 12 @interface HVWHtmlViewController ()<UIWebViewDelegate>13 14 @end15 16 @implementation HVWHtmlViewController17 18 19 - (void)loadView {20     // 改变内置view为UIWebView21     self.view = [[UIWebView alloc] init];22 }23 24 - (void)viewDidLoad {25     [super viewDidLoad];26     // Do any additional setup after loading the view.27    28 }29 30 31 /** 加载数据 */32 - (void)setHtml:(HVWHtml *)html {33     _html = html;34    35     // 1.设置标题36     self.title = self.html.title;37    38     // 2.获取UIWebView39     UIWebView *webView = (UIWebView *) self.view;40     // 设置webView代理41     webView.delegate = self;42    43     // 3.创建URL44     NSURL *url = [[NSBundle mainBundle] URLForResource:self.html.html withExtension:nil];45    46     // 4.创建请求47     NSURLRequest *request = [NSURLRequest requestWithURL:url];48    49     // 5.发送请求50     [webView loadRequest:request];51    52     // 6.添加关闭按钮53     self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"关闭" style:UIBarButtonItemStylePlain target:self action:@selector(close)];54 }55 56 - (void)didReceiveMemoryWarning {57     [super didReceiveMemoryWarning];58     // Dispose of any resources that can be recreated.59 }60 61 - (void) close {62     [self dismissViewControllerAnimated:YES completion:nil];63 }64 65 /** 加载html完毕,跳转到指定部分 */66 - (void)webViewDidFinishLoad:(UIWebView *)webView {67     // 1.合成js代码68     NSString *js = [NSString stringWithFormat:@"window.location.href = http://www.mamicode.com/‘#%@‘;", self.html.ID];69    70     // 2.执行js代码71     [webView stringByEvaluatingJavaScriptFromString:js];72 }73 74 @end

 

 

[iOS UI进阶 - 2.2] 彩票Demo v1.2 UICollectionView基本