软件设计 2017-05-11
此篇为针对ios中的UI基础知识,为了能让大家更清楚的理解,此整理中编写了采用知识点+案例的方式,配有非常详细的截图和代码注释,添加了许多大白话进行解释,如有错误之处,望指正,愿与您相互交流学习,共同进步!---"会飞的猴子_阿新"
本篇案例修改图为(后面会逐步优化细节问题)
主要内容从支付宝案例开始,重点在后面哦!
使用UITableViewControler控制器实现支付宝口碑案例 UITableViewControler 缺陷: 现阶段只适合全屏的..
创建一个demo体验一下: 1.准备:新建文件-->把默认的的viewController删掉-->新建LJXTableViewController-->把Main.storyboard中默认的控制器删掉-->重写拖拽一个UITableViewController-->指定初始箭头,指定class. 2.添加头部视图
- (void)viewDidLoad { [super viewDidLoad]; UIView *headerView = [[UIView alloc]init]; headerView.backgroundColor =[UIColor redColor]; //把某个控件设置到tableHeaderView上时,只有高度需要我们自己设置,前面 X , Y ,Width都可以直接给个0 headerView.frame = CGRectMake(0, 0, 0, 100); //设置tableView的最顶部视图 self.tableView.tableHeaderView = headerView; //-->这里只需要headerView赋值给tableHeaderView 这个属性,内部就在添加到父控件上我们就不用addSubview: }
3.添加数据验证一下头部视图跟 "一组和多组" 没有关系
// // LJXTableViewController.m // LJX_tableView的头部和尾部视图 #import "LJXTableViewController.h" @interface LJXTableViewController () @end //重用标识 static NSString *ID = @"cell"; @implementation LJXTableViewController - (void)viewDidLoad { [super viewDidLoad]; UIView *headerView = [[UIView alloc]init]; headerView.backgroundColor =[UIColor redColor]; //把某个控件设置到tableHeaderView上时,只有高度需要我们自己设置,前面 X , Y ,Width都可以直接给个0 headerView.frame = CGRectMake(0, 0, 0, 100); //设置tableView的最顶部视图 注意:跟一组和多组没有关系 self.tableView.tableHeaderView = headerView; //-->这里只需要headerView赋值给tableHeaderView 这个属性,内部就在添加到父控件上我们就不用addSubview: //注册cell [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:ID]; } //2组 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ return 2; } //每组4行 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return 4; } -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID forIndexPath:indexPath]; cell.textLabel.text =@(indexPath.row).description;//简单设置数据,每个cell显示一个行数 return cell; } @end
4.添加底部视图
UIButton *footerBtn = [[UIButton alloc]init]; footerBtn.backgroundColor = [UIColor purpleColor]; //紫色背景 //把某个控件设置到tableHeaderView上时,只有高度需要我们自己设置,前面 X , Y ,Width都可以直接给个0,但是x是可以改的,改了会有影响但不建议去改 footerBtn.frame = CGRectMake(0, 0, 0, 100); //设置tableView的最底部视图 self.tableView.tableFooterView = footerBtn;
1.满星个数 "分数强转成整形,如4.5转成整形是4 那就表示有4颗满星" 2.半星个数 "分数 - 整星个数如果不为0那说明就有一个半星" 3.空心个数 "for(NSInteger i = 已添加星星个数; i < 5; i++)" 每一个商家的评分不一样,所以分值要动态传入,去动态设置星星样子
创建实体文件夹,方便日后移植 为了方便最后整合代码时的移植,不直接在控制器中实现,而是单独创建继承UIView的类,
// // ZFBLeverStar.h // LJX_星级评价 #import <UIKit/UIKit.h> @interface ZFBLeverStar : UIView ///接收评分 @property (nonatomic, assign) CGFloat lever; @end
// // ZFBLeverStar.m // LJX_星级评价 #import "ZFBLeverStar.h" @implementation ZFBLeverStar //重写set方法在里面处理显示星星图片的细节 -(void)setLever:(CGFloat)lever{ _lever = lever; //1.满星 //把传过来的分数强转为整型,整数值就是满星的个数 NSInteger fullStartCount = (NSInteger)lever; for(NSInteger i = 0 ; i < fullStartCount ; i++){ [self makeLeverStarWithImageName:@"full_star" andPosition:i]; } //2.半星 // (取传过来的个数) - (强转后的整数),如果 > 0 就表示有半星 if((lever - fullStartCount)>0){ [self makeLeverStarWithImageName:@"half_star" andPosition:fullStartCount]; //如果添加了一个半星那把满星个数的值做加1 用它当做 目前已经添加星星的总个数了 fullStartCount++; } //3.空星 "如果到这来还不够5颗星 那剩下的就是空星的个数了" for (NSInteger i = fullStartCount; i < 5; i++) { [self makeLeverStarWithImageName:@"empty_star" andPosition:i]; } } /** 创建星星imageView @param imageName 星星图片 @param position 星星位置 从0开始 */ -(void)makeLeverStarWithImageName:(NSString *)imageName andPosition:(NSInteger)position{ //1.创建imageView设置图片,那么imageView创建出来的就有尺寸 UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:imageName]]; //2.设置图片的x CGFloat imageViewX = position * imageView.image.size.width; imageView.frame = CGRectMake(imageViewX, 0, imageView.image.size.width, imageView.image.size.height); [self addSubview:imageView]; } @end
// // ViewController.m // LJX_星级评价 #import "ViewController.h" #import "ZFBLeverStar.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; ZFBLeverStar *leverStar = [[ZFBLeverStar alloc]init]; leverStar.frame = CGRectMake(100, 100, 60, 12);//素材中图片2X的是大小24*24,高24/2=12, 宽为5颗*12=60 leverStar.backgroundColor = [UIColor purpleColor]; leverStar.lever = 2.5;//先传一个2.5分验证一下 [self.view addSubview:leverStar]; } @end
我们只创建添加,没有管过移除啥的,顾导致传一次分,创建一次....
解决重复创建的问题 重写初始化方法(初始化方法它只会调一次),那样一创建leverStar这个控件,里面就自带5颗星星imageView,
即对ZFBLeverStar.m做了修改
// // ZFBLeverStar.m // LJX_星级评价 #import "ZFBLeverStar.h" @implementation ZFBLeverStar - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { //来了 直接把5个imageView创建添加好 for (NSInteger i = 0; i < 5; i++) { UIImageView *imageView =[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"full_star"]];//这个目的不是拿图片,目的是随便拿一张图片,图片就有了大小了 //设置图片的x CGFloat imageViewX = i * imageView.image.size.width; imageView.frame = CGRectMake(imageViewX, 0, imageView.image.size.width, imageView.image.size.height); [self addSubview:imageView]; } } return self; } //重写set方法在里面处理显示星星图片的细节 -(void)setLever:(CGFloat)lever{ _lever = lever; //1.满星 //把传过来的分数强转为整型,整数值就是满星的个数 NSInteger fullStartCount = (NSInteger)lever; for(NSInteger i = 0 ; i < fullStartCount ; i++){ [self makeLeverStarWithImageName:@"full_star" andPosition:i]; } //2.半星 // (取传过来的个数) - (强转后的整数),如果 > 0 就表示有半星 if((lever - fullStartCount)>0){ [self makeLeverStarWithImageName:@"half_star" andPosition:fullStartCount]; //如果添加了一个半星那把满星个数的值做加1 用它当做 目前已经添加星星的总个数了 fullStartCount++; } //3.空星 "如果到这来还不够5颗星 那剩下的就是空星的个数了" for (NSInteger i = fullStartCount; i < 5; i++) { [self makeLeverStarWithImageName:@"empty_star" andPosition:i]; } } /** 创建星星imageView @param imageName 星星图片 @param position 星星位置 从0开始 */ -(void)makeLeverStarWithImageName:(NSString *)imageName andPosition:(NSInteger)position{ //获取相应位置的子控件 UIImageView *imageView = self.subviews[position]; //设置图片 imageView.image = [UIImage imageNamed:imageName]; } @end
这次优化的目的是:日后星级评价更加的方便.--->外部不管用纯代码还有用xib还是用storyboard都比较方便.
如何不想使用代码方式 使用storyboard
运行ok
自定义cell有3种方式,分别是storyboard,xib,手写代码. 我们都使用一遍.这里我们先使用第一种方式使用-->storyboard方式
需求分析
1.顶部是一个TableViewHeaderView 2.组的头部标题可以停留,说明tableView是plain样式 3.cell中的数据比较多,系统无法满足,自定义cell
[blockquote]
1.项目准备: 指定类前缀(例如ZFB),分文件夹(目的是为了后面集成到一个项目中),删除原有Main.storyboard 自己创建一个storyboard(指定class进行关联,设置为启动界面),并别忘指定 Main interface
[/blockquote]
创建控制器: 注意继承UITableViewController(因为storyboard中我们拖拽的UITableViewControler,所以创建的类也要继承UITableViewControler)
如果运行后,是白色面板. 排错方法:打断点 ------>给数据源方法打断点------->(没执到时)-往上打断点 ------->[super viewDidLoad]----->(也没有执行到时) ------>查看有没有指定控制器class;
把准备好的plist文件和图片素材拖入案例中
使用了UITableViewController后,指定数据源和遵守协议都不用我们管了.下一步我们来实现相应的数据源方法
[blockquote]
2.实现相应的数据源方法
[/blockquote]
需要的数据,通过第4步会得到.
[blockquote]
3.有多少行应该根据数据来
[/blockquote]
[blockquote]
4.加载plist字典转换模型
[/blockquote]
去modal文件夹中,创建模型类
//--------------------ZFBBusinessModel.h------------------------ #import <Foundation/Foundation.h> @interface ZFBBusinessModel : NSObject ///头像 @property (nonatomic, copy) NSString *icon; ///优惠信息 @property (nonatomic, copy) NSString *discount; ///人均消费 @property (nonatomic, strong) NSNumber *averagePrice; ///距离 @property (nonatomic, strong) NSNumber *distance; ///打折 @property (nonatomic, strong) NSNumber *offNum; ///评分 @property (nonatomic, strong) NSNumber *level; ///店名 @property (nonatomic, copy) NSString *name; + (instancetype)businessWithDict:(NSDictionary *)dict; @end //--------------------ZFBBusinessModel.m------------------------ #import "ZFBBusinessModel.h" @implementation ZFBBusinessModel +(instancetype)businessWithDict:(NSDictionary *)dict{ id obj = [[self alloc]init]; [obj setValuesForKeysWithDictionary:dict]; //--->注意使用KVC编码,属性名和字典中的key的名字一定要相同. return obj; } @end
[blockquote]
5.在storyboard中给自带的UITableViewCell指定重用标识 一定要和返回cell的数据源方法是的标识要一样
[/blockquote]
UITableView *cell = [tableView dequeueReusableCellWithIdentifier:@"business" forIndexPath:indexPath];
[blockquote]
6.在storyboard中给cell添加拖拽相应的控件,并添加约束
[/blockquote]
6.1拖拽imageView (plus是3X的在这除以3 工作中一般都2x的,到时候除以2)
因为各个商家图片不一样大,顾为了统一
Scale:拉伸图片 Aspect:图片长宽的比例,保持图形的长宽比,保持图片不变形。
Aspect Fill:在保持长宽比的前提下,缩放图片,使图片充满容器。 Aspect Fit:在保持长宽比的前提下,缩放图片,使得图片在容器内完整显示出来。 Scale to Fill: 缩放图片,使图片充满容器。图片未必保持长宽比例协调,有可能会拉伸至变形。
6.1拖拽lable设置店名
6.3 先拖个view给星星占个位,后面再处理
得
6.4拖拽lable设置评分
设置约束
6.4拖拽lable设置人均消费 设置字体大小,设置约束
运行一下看效果,然后再继续
6.5拖拽lable设置折扣
设置约束 6.6拖拽lable设置距离 设置约束 x和Y 6.7拖拽lable设置减字 设置约束(上边距8,与上面左边对齐,)略 减字外面的是正方形,我们设置一下宽高比为1:1,文字居中就ok了
6.8拖拽lable设置随机立减... (设置字体及颜色,设置约束,略..) 6.9 修改一下cell的高度 上面修改了business中的高度,只是修改了模型的大小,运行起来还得看 table View的高度大小,所以还得修改table View的高度 6.10 把背景颜色去掉运行一下看看
[blockquote]
7.创建一个继承至UITableViewCell的类,并且指定SB中的UITableViewCell的class为自己创建出来的这个类
[/blockquote]
[blockquote]
8.把SB中Cell内部的子控件连线到指定class的.m中的延展中
[/blockquote]
8.1增加一个延展
8.2指定class 8.3 子控件连线
[blockquote]
9.把模型变成属性定义在自定义cell类的.h中,在.m重写模型属性的set方法在此方法设置数据
[/blockquote]
9.1 把模型变成属性定义在自定义cell类的.h中
// ZFBBusinessCell.h #import <UIKit/UIKit.h> @class ZFBBusinessModel; @interface ZFBBusinessCell : UITableViewCell ///模型属性 注意模型是个对象 用strong @property (nonatomic , strong) ZFBBusinessModel *businessModel; @end
9.2 在.m重写模型属性的set方法在此方法设置数据
// // ZFBBusinessCell.m #import "ZFBBusinessCell.h" #import "ZFBBusinessModel.h"//注意导入头文件 @interface ZFBBusinessCell () ///商店的头像 @property (weak, nonatomic) IBOutlet UIImageView *iconView; ///店名 @property (weak, nonatomic) IBOutlet UILabel *nameLabel; ///评分 @property (weak, nonatomic) IBOutlet UILabel *levelLabel; ///人均消费 @property (weak, nonatomic) IBOutlet UILabel *averagePriceLabel; ///打折 @property (weak, nonatomic) IBOutlet UILabel *offNumLabel; ///距离 @property (weak, nonatomic) IBOutlet UILabel *distanceLabel; ///优惠信息 @property (weak, nonatomic) IBOutlet UILabel *discountLabel; @end @implementation ZFBBusinessCell //重写模型属性的set方法在此方法中给子控件设置数据 -(void)setBusinessModel:(ZFBBusinessModel *)businessModel{ _businessModel =businessModel; _iconView.image = [UIImage imageNamed:businessModel.icon]; _nameLabel.text = businessModel.name; _levelLabel.text = businessModel.level.description;//level是NSNumber类型的,转字符串掉description方法 _averagePriceLabel.text = [NSString stringWithFormat:@"人均消费 %@ 元",businessModel.averagePrice]; //plist中只存了个数字,要显示人均消费**元,顾要拼接字符串 _offNumLabel.text = [NSString stringWithFormat:@"%@ 折",businessModel.offNum]; _distanceLabel.text = [NSString stringWithFormat:@"距离北京石油化工学院 %@ m",businessModel.distance]; _discountLabel.text = businessModel.discount; } @end
回到ZFBBusinessController.m中 修改创建的cell类型
设置数据
//2.设置数据 (传递模型,这里是单组数据展示, 当只有一组数据的时候用row,多组的时候才先用section再用row) cell.businessModel = self.businessData[indexPath.row];
运行看现阶段效果
数据类型有点小问题,8.8折和9.8折后面都一大串,接下来修改
[blockquote]
10.打折 把模型中的offNum 改float类型,前提要改一下引入系统框架 UIKit
[/blockquote]
10.1修改ZFBBusinessModel.h文件
10.2修改ZFBBusinessCell.m文件
运行看效果
[blockquote]
11.集成星星,把星星的文件拖拽出来后,指定Storyboard中代表星星的那个View的Class
[/blockquote]
[blockquote]
12.把用来表示星星view连线在自定义cell.m中,引入星星类的头文件
[/blockquote]
[blockquote]
13.在模型属性的set方法中,给星星view传level
[/blockquote]
_leverView.lever = businessModel.level;
上面报错是因为我们传的是CGFloat类型导致的 而 顾改一下 这是这里又报错 还要改一下 把星星后面黄色背景颜色去掉,运行看效果
[blockquote]
14.创建了一个xib 把xib中的view 宽361 高960
[/blockquote]
[blockquote]
15.在里面拖入一个imageView并且设置好图片, 注意先 com + = 自适应下 为了后面使用Aspect ratio后图片不会变形 16. 第一个imageView 上 左 右 8间距 + 宽高比
[/blockquote]
先使用 com + = 自适应 ,后面再设置宽高比,这样他会根据真实的尺寸算比例,图片不会变形
[blockquote]
17.再复制3个imageView 但注意它身上约束
[/blockquote]
即复制完后,clear复制的约束
[blockquote]
18.全部选中(com+a) 左对齐 等宽等高 上下间距8
[/blockquote]
左对齐
等宽等高
上下间距8:
注意在设置上下间距前,要保证两个图片之间不要有重合
[blockquote]
19.创建一个继承 至UIView的类,并且给xib指定class
[/blockquote]
继承关系是跟最顶层有关系的,最顶层是UIView,就继续UIView,最顶层是UIButton,那就继续UIButton. 这里是顶层是UIView.
给xib指定class(其实不指定也可以,因为之间没有连线,但最好指定一下,说明它们之间是同一类型)
[blockquote]
20.把加载xib细节封装在刚才创建的类中,提供一个可以让外部调用的类方法,目的降低耦合度.
[/blockquote]
//######################### // ZFBBusinessPictureView.h #import <UIKit/UIKit.h> @interface ZFBBusinessPictureView : UIView /** 加载xib创建头部视图 */ +(instancetype)businessPictureView; @end //######################### // ZFBBusinessPictureView.m #import "ZFBBusinessPictureView.h" @implementation ZFBBusinessPictureView /* 知识点回顾--两种加载xib的方式 1.NSBundle加载XIB iOS 2.0 的方法 2.使用UINib加载XIB iOS 4.0 的方法 做了内存优化"如果内存紧张"内存警告,可以自动释放,如果有需要会重新自动加载"" */ +(instancetype)businessPictureView{ UINib *nib = [UINib nibWithNibName:@"ZFBBusinessPicture" bundle:nil]; return [[nib instantiateWithOwner:nil options:nil]firstObject]; } @end
[blockquote]
21.给tableView设置tableHeaderView 注意要计算它将来真实的高度
[/blockquote]
回到ZFBBusinessController.m 回到控制器中,导入刚才创建类的头文件ZFBBusinessPictureView.h
在控制器设置tableView的头部 视图.
//2.设置tableView的头部视图 ZFBBusinessPictureView *pictureView = [ZFBBusinessPictureView businessPictureView]; //375 * 960 / 361 CGFloat pictureViewHeight = self.tableView.bounds.size.width * pictureView.bounds.size.height / pictureView.bounds.size.width; //根据比例计算真实高度 pictureView.frame = CGRectMake(0, 0, self.tableView.bounds.size.width, pictureViewHeight); //宽度最好不要给了,以防有问题 self.tableView.tableHeaderView = pictureView; //设置为头部视图
[blockquote]
22.给tableView设置tableFooterView
[/blockquote]
//3.设置tableView的尾部视图 UIButton *footerBtn = [[UIButton alloc]init]; [footerBtn setTitle:@"点击加载更多" forState:UIControlStateNormal]; [footerBtn setTitleColor:[UIColor colorWithWhite:0.3 alpha:1] forState:UIControlStateNormal]; [footerBtn setTitleColor:[UIColor colorWithWhite:0.7 alpha:1] forState:UIControlStateHighlighted]; footerBtn.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1]; footerBtn.frame = CGRectMake(0, 0, 0, 30); self.tableView.tableFooterView = footerBtn;//设置为尾部视图
[blockquote]
23.实现没点击加载更多一次,多输出一个cell
[/blockquote]
把之前定义是不可变数组,修改为可变数组,不然就会报警告
[blockquote]
24.优化让其自动滚动
[/blockquote]
1.atScrollPosition: 的参数为UITableViewScrollPositionMiddle时 NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.businessData.count-1 inSection:0]; [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
2.atScrollPosition: 的参数为UITableViewScrollPositionBottom时
[blockquote]
25.细节处理 让cell的分割线从最左边开始
[/blockquote]
self.tableView.separatorInset = UIEdgeInsetsZero;
后面还没会慢慢对细节进行处理
看看哪些没掌握:
1、能够说出tableViewController的view属性和tableView是一样的
2、能够使用xib布局口碑的头部视图
3、能够说出使用代码重新设置nib高度的含义是为了适配不同宽度的屏幕
4、能够使用代码设置尾部的按钮
5、能够说出代码会调用view的initWithFrame方法
6、能够说出sb/xib会调用view的aweakFromNib方法
7、能够将星级评价小框架移植到口碑控制器中
8、能够解决浮点数转字符串精度的问题
9、能够使用代码解析plist数据 ) 10、能够使用代码将字典转化成模型
11、能够使用代码设置tableView的headerView和footerView
1.商家配图图片大小不一样,所以最好给imageView设置宽高约束,达到配图尺寸统一 2.label如果只有一行时,只用设置位置即可,尺寸让它自适应 3.评分view,它的大小不能自适应,所以必须设置宽高约束"60,12" 4.减是个方形的,所以最好设置个宽高比 店名字体:14字体 其它是12号字体 到cell边距15 内部边距11
1.cell中的子控件不能连线到控制器,因为当前控制器只有一个,但是将来cell有很多个,所以会报重复连接"连线"错误 2.正确作法,应该创建一个继承至UITableViewCell的类,来管理"描述/表示",设置cell的customClass 3.把cell中子控件的连线,连入到管理cell类的.m的延展中
引入模型属性"strong" 重写模型属性set方法给cell内部子控件设置数据
- 在xib中调整好位置高度,并在视图加载完成之后根据'宽度'和'屏幕的宽度'和'屏幕的高度'设置**高度** - tableView的宽 / xib中HeaderView的宽 * xib中HeaderView的高 得到按比例缩放后的realHeaderView的高 - xib中的高 * tableView的宽 / xib中的宽 - xib的高 / xib的宽 * tableView的宽 2: 3 == 4: 6?
Editor -> Embedin -> Navgation Contorller
// 方法1 传统写法 _name = dict[@"name"]; // 字典不能保存基本数据类型 _age = [dict[@"age"] integerValue];
// 方法2 KVC - `间接`通过 key 设置数值 // 注意:基本数据类型,不需要转换,KVC 可以自动实现转换! [self setValue:dict[@"name"] forKey:@"name"]; [self setValue:dict[@"age"] forKey:@"age"];
// 方法3 KVC - 进阶,循环遍历字典 for (NSString *key in dict) { // 1. 通过 key 取值 id value = dict[key]; // 2. 通过 KVC 方法设置数值 // -key 对应对象的 `属性`,如果属性不存在就崩溃 [self setValue:value forKey:key]; }
// 方法4 KVC 字典转模型大招 -> 内部实际上就是方法3的实现 [self setValuesForKeysWithDictionary:dict];
附整体代码
/**总步骤: 1.指定类前缀,分文件夹'删除原有Main.sb' 自己创建一个,并别忘指定 Main interface 2.实现相应的数据源方法 3.有多少行应该根据数据来 4.加载plist字典转换模型 5.在sb中给自带的UITableViewCell指定重用标识 一定要和返回cell的数据源方法是的标识要一样 6.在SB中给cell添加拖拽相应的控件,并添加约束 7.创建一个继承至UITableViewCell的类,并且指定SB中的UITableViewCell的class为自己创建出来的这个类 8.把SB中Cell内部的子控件连线到指定class的.m中的延展中 9.把模型变成属性定义在自定义cell类的.h中,在.m重写模型属性的set方法在此方法设置数据 10.打折 把模型中的offNum 改float类型,前提要改一下引入系统框架 UIKit 11.集成星星,把星星的文件拖拽出来后,指定SB中代表星星的那个View的Class 12.把用来表示星星view连线在自定义cell.m中,引入星星类的头文件 13.在模型属性的set方法中,给星星view传level 14.创建了一个xib 把xib中的view 宽361 高960 15.在里面拖入一个imageView并且设置好图片, com + = 自适应下 为了不用改宽高比约束值 16. 第一个imageView 上 左 右 8间距 + 宽高比 17. 复制3个imageView 但注意它身上约束 18. 全部选中左对齐 等宽等高 上下间距8 19. 创建一个继承 至UIView的类,并且指定class 20. 把加载xib细节封装在此类中,提供一个可以让外部调用的类方法 21.给tableView设置tableHeaderView 注意要计算它将来真实的高度 22.给tableView设置tableFooterView 23.实现没点击加载更多一次,多输出一个cell 24.优化让其自动滚动 25.细节处理 让cell的分割线从最左边开始 */
//********************ZFBBusinessController.h***************************** // ZFBBusinessController.h #import <UIKit/UIKit.h> @interface ZFBBusinessController : UITableViewController @end //**********************ZFBBusinessController.m*************************** // // ZFBBusinessController.m // LJX_支付宝口碑(stroyboard搭建) #import "ZFBBusinessController.h" #import "ZFBBusinessModel.h" #import "ZFBBusinessCell.h" #import "ZFBBusinessPictureView.h" @interface ZFBBusinessController () //数据在多个数据源方法中使用,顾声明一个属性 @property (nonatomic ,strong)NSMutableArray *businessData; @end @implementation ZFBBusinessController - (void)viewDidLoad { [super viewDidLoad]; //1.加载数据 self.businessData = [self loadBusinessData]; //-->使用方式2时,直接调用方法就行[self loadBusinessData]; //2.设置tableView的头部视图 ZFBBusinessPictureView *pictureView = [ZFBBusinessPictureView businessPictureView]; //如果不设置,因为xib中设置的宽度是361,如果在375的手机屏幕上显示,因为xib中的图片的宽度没有写死,当到了宽屏幕下,变宽,高也会变高,导致图片会超出父控件, 出了头部视图的边界. //375 * 960 / 361 CGFloat pictureViewHeight = self.tableView.bounds.size.width * pictureView.bounds.size.height / pictureView.bounds.size.width; //计算真实高度 pictureView.frame = CGRectMake(0, 0, self.tableView.bounds.size.width, pictureViewHeight); //宽度最好不要给了,以防有问题 self.tableView.tableHeaderView = pictureView; //设置为头部视图 //3.设置tableView的尾部视图 UIButton *footerBtn = [[UIButton alloc]init]; [footerBtn setTitle:@"点击加载更多" forState:UIControlStateNormal]; // colorWithWhite:0.3 alpha:1 通过灰度来设置颜色 第一个参数如果 传0就是黑色 如果传1就表示白色 [footerBtn setTitleColor:[UIColor colorWithWhite:0.3 alpha:1] forState:UIControlStateNormal]; [footerBtn setTitleColor:[UIColor colorWithWhite:0.7 alpha:1] forState:UIControlStateHighlighted]; footerBtn.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1]; footerBtn.frame = CGRectMake(0, 0, 0, 30); [footerBtn addTarget:self action:@selector(loadMoreData) forControlEvents:UIControlEventTouchUpInside]; //添加点击事件 self.tableView.tableFooterView = footerBtn;//设置为尾部视图 //小细节处理 :让分割线从最左边开始 self.tableView.separatorInset = UIEdgeInsetsZero; } #pragma mark -尾部按钮点击后会调用此方法 //1.加载更多数据 -(void)loadMoreData{ //1.创建一个新的模型 ZFBBusinessModel *businessModel = [[ZFBBusinessModel alloc]init]; businessModel.icon = @"ss"; businessModel.discount = @"全场大派送,2元一件,快来买!"; businessModel.averagePrice = @2; businessModel.distance = @20; businessModel.offNum = 2; businessModel.level = 5; businessModel.name = @"西单大悦城!"; //2.把新模型添加到保存所以数据的数组中 [self.businessData addObject:businessModel]; //3.让数据源方法重新执行 [self.tableView reloadData]; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.businessData.count-1 inSection:0]; [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; } #pragma mark -数据源方法 // 应该口碑案例表格就1组,默认就1组,所以不用再写 //- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ // return 1; //} // 行数 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.businessData.count; //--->使用方式2时: return _businessList.count; } // -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ //1.创建cell ZFBBusinessCell *cell = [tableView dequeueReusableCellWithIdentifier:@"business" forIndexPath:indexPath]; //-->这里在storyboard中进行注册cell //2.设置数据 (传递模型,这里是单组数据展示, 当只有一组数据的时候用row,多组的时候才先用section再用row) cell.businessModel = self.businessData[indexPath.row]; //3.返回cell return cell; } #pragma mark -加载数据 - (NSMutableArray *)loadBusinessData{ //1.加载plist NSMutableArray *dictArr = [NSMutableArray arrayWithContentsOfURL:[[NSBundle mainBundle]URLForResource:@"business.plist" withExtension:nil]]; //2. 创建可变数组保存模型 /kə'pæsɪtɪ/ 容量 NSMutableArray *arryM = [NSMutableArray arrayWithCapacity:dictArr.count]; //3.字典转模型 for(NSDictionary *dict in dictArr){ [arryM addObject:[ZFBBusinessModel businessWithDict:dict]]; /* 分步写 //3.1 把字典传递给模型,让模型设置自己的属性 ZFBBusinessModel *groupModel = [ZFBBusinessModel businessWithDict:dict]; //3.2 把创建好的模型添加到可变数组中. [arryM addObject: groupModel]; */ } return arryM; } /* 方式2. 加载数据方法的返回值设置为空 ,声明一个成员变量(给成员变量设置泛型),把可变数组赋值给成员变量 1. NSArray<ZFBBusinessModel *> *_businessList; //声明成员变量,设置泛型 2. - (void)loadBusinessData{ ...code略.... _businessList = arryM; } */ @end
// // ZFBBusinessModel.h #import <UIKit/UIKit.h> @interface ZFBBusinessModel : NSObject ///头像 @property (nonatomic, copy) NSString *icon; ///优惠信息 @property (nonatomic, copy) NSString *discount; ///人均消费 @property (nonatomic, strong) NSNumber *averagePrice; ///距离 @property (nonatomic, strong) NSNumber *distance; ///打折 //浮点数字转出成字符串时,可能会出现问题,把它转换成float @property (nonatomic, assign) float offNum; ///评分 @property (nonatomic, assign) CGFloat level; ///店名 @property (nonatomic, copy) NSString *name; + (instancetype)businessWithDict:(NSDictionary *)dict; @end
#import "ZFBBusinessModel.h" @implementation ZFBBusinessModel +(instancetype)businessWithDict:(NSDictionary *)dict{ id obj = [[self alloc]init]; [obj setValuesForKeysWithDictionary:dict]; //--->注意使用KVC编码,属性名和字典中的key的名字一定要相同. return obj; } @end
// // ZFBLeverStar.h // LJX_星级评价 #import <UIKit/UIKit.h> @interface ZFBLeverStar : UIView ///接收评分 @property (nonatomic, assign) CGFloat lever; @end
// // ZFBLeverStar.m // LJX_星级评价 #import "ZFBLeverStar.h" @implementation ZFBLeverStar //当一个视图从xib或storyboard中创建完成之后就会调用此方法 -(void)awakeFromNib{ [super awakeFromNib]; //来了 直接把5个imageView创建添加好 for (NSInteger i = 0; i < 5; i++) { UIImageView *imageView =[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"full_star"]];//这个目的不是拿图片,目的是随便拿一张图片,图片就有了大小了 //设置图片的x CGFloat imageViewX = i * imageView.image.size.width; imageView.frame = CGRectMake(imageViewX, 0, imageView.image.size.width, imageView.image.size.height); [self addSubview:imageView]; } } //此方法只有用代码创建时才会调用 - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { //来了 直接把5个imageView创建添加好 for (NSInteger i = 0; i < 5; i++) { UIImageView *imageView =[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"full_star"]];//这个目的不是拿图片,目的是随便拿一张图片,图片就有了大小了 //设置图片的x CGFloat imageViewX = i * imageView.image.size.width; imageView.frame = CGRectMake(imageViewX, 0, imageView.image.size.width, imageView.image.size.height); [self addSubview:imageView]; } } return self; } //重写set方法在里面处理显示星星图片的细节 -(void)setLever:(CGFloat)lever{ _lever = lever; //1.满星 //把传过来的分数强转为整型,整数值就是满星的个数 NSInteger fullStartCount = (NSInteger)lever; for(NSInteger i = 0 ; i < fullStartCount ; i++){ [self makeLeverStarWithImageName:@"full_star" andPosition:i]; } //2.半星 // (取传过来的个数) - (强转后的整数),如果 > 0 就表示有半星 if((lever - fullStartCount)>0){ [self makeLeverStarWithImageName:@"half_star" andPosition:fullStartCount]; //如果添加了一个半星那把满星个数的值做加1 用它当做 目前已经添加星星的总个数了 fullStartCount++; } //3.空星 "如果到这来还不够5颗星 那剩下的就是空星的个数了" for (NSInteger i = fullStartCount; i < 5; i++) { [self makeLeverStarWithImageName:@"empty_star" andPosition:i]; } } /** 创建星星imageView @param imageName 星星图片 @param position 星星位置 从0开始 */ -(void)makeLeverStarWithImageName:(NSString *)imageName andPosition:(NSInteger)position{ //获取相应位置的子控件 UIImageView *imageView = self.subviews[position]; //设置图片 imageView.image = [UIImage imageNamed:imageName]; } @end /* //1.创建imageView设置图片,那么imageView创建出来的就有尺寸 UIImageView *imageView = [[UII