此篇为针对ios中的UI基础知识,为了能让大家更清楚的理解,此整理中编写了采用知识点+案例的方式,配有非常详细的截图和代码注释,添加了许多大白话进行解释,如有错误之处,望指正,愿与您相互交流学习,共同进步!---"会飞的猴子_阿新"
本篇案例修改图为(后面会逐步优化细节问题)
主要内容从支付宝案例开始,重点在后面哦!
使用UITableViewControler控制器实现支付宝口碑案例
UITableViewControler 缺陷: 现阶段只适合全屏的..
01-UITableViewController体验
- 相当于一个控制器自带tableView
- viewController管理的是view
- tableViewController管理的是tableView
- 也就是在tableViewController下,self.view = self.tableView
02-tableView的HeaderView和FooterView
- tableView的header - (x, y, width) 可以随便指定,(height) 实际数值
- tableView的footer - (y, width) 可以随便指定,(x, height) 实际数值
创建一个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;
03-星级评价-需求分析
1.满星个数 "分数强转成整形,如4.5转成整形是4 那就表示有4颗满星"
2.半星个数 "分数 - 整星个数如果不为0那说明就有一个半星"
3.空心个数 "for(NSInteger i = 已添加星星个数; i < 5; i++)"
每一个商家的评分不一样,所以分值要动态传入,去动态设置星星样子
04-星级评价
4.1星级评价实现
创建实体文件夹,方便日后移植
为了方便最后整合代码时的移植,不直接在控制器中实现,而是单独创建继承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
4.2解决星星重复创建问题
我们只创建添加,没有管过移除啥的,顾导致传一次分,创建一次....
解决重复创建的问题
重写初始化方法(初始化方法它只会调一次),那样一创建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
4.3 最后深入的优化
这次优化的目的是:日后星级评价更加的方便.--->外部不管用纯代码还有用xib还是用storyboard都比较方便.
如何不想使用代码方式
使用storyboard
运行ok
支付宝口碑案例开始
自定义cell有3种方式,分别是storyboard,xib,手写代码.
我们都使用一遍.这里我们先使用第一种方式使用-->storyboard方式
需求分析
1.顶部是一个TableViewHeaderView
2.组的头部标题可以停留,说明tableView是plain样式
3.cell中的数据比较多,系统无法满足,自定义cell
1.项目准备:
指定类前缀(例如ZFB),分文件夹(目的是为了后面集成到一个项目中),删除原有Main.storyboard 自己创建一个storyboard(指定class进行关联,设置为启动界面),并别忘指定 Main interface
创建控制器: 注意继承UITableViewController(因为storyboard中我们拖拽的UITableViewControler,所以创建的类也要继承UITableViewControler)
如果运行后,是白色面板. 排错方法:打断点
------>给数据源方法打断点------->(没执到时)-往上打断点
------->[super viewDidLoad]----->(也没有执行到时)
------>查看有没有指定控制器class;
把准备好的plist文件和图片素材拖入案例中
使用了UITableViewController后,指定数据源和遵守协议都不用我们管了.下一步我们来实现相应的数据源方法
2.实现相应的数据源方法
需要的数据,通过第4步会得到.
3.有多少行应该根据数据来
4.加载plist字典转换模型
去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
5.在storyboard中给自带的UITableViewCell指定重用标识 一定要和返回cell的数据源方法是的标识要一样
UITableView *cell = [tableView dequeueReusableCellWithIdentifier:@"business" forIndexPath:indexPath];
6.在storyboard中给cell添加拖拽相应的控件,并添加约束
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 把背景颜色去掉运行一下看看
7.创建一个继承至UITableViewCell的类,并且指定SB中的UITableViewCell的class为自己创建出来的这个类
8.把SB中Cell内部的子控件连线到指定class的.m中的延展中
8.1增加一个延展
8.2指定class
8.3 子控件连线
9.把模型变成属性定义在自定义cell类的.h中,在.m重写模型属性的set方法在此方法设置数据
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折后面都一大串,接下来修改
10.打折 把模型中的offNum 改float类型,前提要改一下引入系统框架 UIKit
10.1修改ZFBBusinessModel.h文件
10.2修改ZFBBusinessCell.m文件
运行看效果
11.集成星星,把星星的文件拖拽出来后,指定Storyboard中代表星星的那个View的Class
12.把用来表示星星view连线在自定义cell.m中,引入星星类的头文件
13.在模型属性的set方法中,给星星view传level
_leverView.lever = businessModel.level;
上面报错是因为我们传的是CGFloat类型导致的
而
顾改一下
这是这里又报错
还要改一下
把星星后面黄色背景颜色去掉,运行看效果
14.创建了一个xib 把xib中的view 宽361 高960
15.在里面拖入一个imageView并且设置好图片, 注意先 com + = 自适应下 为了后面使用Aspect ratio后图片不会变形
16. 第一个imageView 上 左 右 8间距 + 宽高比
先使用 com + = 自适应 ,后面再设置宽高比,这样他会根据真实的尺寸算比例,图片不会变形
17.再复制3个imageView 但注意它身上约束
即复制完后,clear复制的约束
18.全部选中(com+a) 左对齐 等宽等高 上下间距8
左对齐
等宽等高
上下间距8:
注意在设置上下间距前,要保证两个图片之间不要有重合
19.创建一个继承 至UIView的类,并且给xib指定class
继承关系是跟最顶层有关系的,最顶层是UIView,就继续UIView,最顶层是UIButton,那就继续UIButton. 这里是顶层是UIView.
给xib指定class(其实不指定也可以,因为之间没有连线,但最好指定一下,说明它们之间是同一类型)
20.把加载xib细节封装在刚才创建的类中,提供一个可以让外部调用的类方法,目的降低耦合度.
//#########################
// 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
21.给tableView设置tableHeaderView 注意要计算它将来真实的高度
回到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; //设置为头部视图
22.给tableView设置tableFooterView
//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;//设置为尾部视图
23.实现没点击加载更多一次,多输出一个cell
把之前定义是不可变数组,修改为可变数组,不然就会报警告
24.优化让其自动滚动
1.atScrollPosition: 的参数为UITableViewScrollPositionMiddle时
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.businessData.count-1 inSection:0];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
2.atScrollPosition: 的参数为UITableViewScrollPositionBottom时
25.细节处理 让cell的分割线从最左边开始
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.口碑-数据分析
2.口碑-创建模型
- 类前缀
- 分文件夹
- 素材
3.口碑-解析数据
4.口碑-通过sb加载cell
- 通过sb加载cell,需要设置cell的Identifier,给sb中的cell设置重用标识即完成注册单元格
5.口碑-cell的布局
1.商家配图图片大小不一样,所以最好给imageView设置宽高约束,达到配图尺寸统一
2.label如果只有一行时,只用设置位置即可,尺寸让它自适应
3.评分view,它的大小不能自适应,所以必须设置宽高约束"60,12"
4.减是个方形的,所以最好设置个宽高比
店名字体:14字体
其它是12号字体
到cell边距15
内部边距11
6.口碑-自定义cell控件连线
1.cell中的子控件不能连线到控制器,因为当前控制器只有一个,但是将来cell有很多个,所以会报重复连接"连线"错误
2.正确作法,应该创建一个继承至UITableViewCell的类,来管理"描述/表示",设置cell的customClass
3.把cell中子控件的连线,连入到管理cell类的.m的延展中
7.口碑-设置cell数据
引入模型属性"strong"
重写模型属性set方法给cell内部子控件设置数据
8.口碑-移植星级评价
- 拖拽文件,设置sb中的view为自定义view,连线并在set数据的方法中把数据放在控件上
- 用自定义星星的类来描述cell中的星星view,加载sb中的小星星view创建出来的对象,就是他指定类创建来出的对象"他们是同一个东西了"
- 再给level传入分值即可
9.口碑-头部视图-布局
- 第一个视图分别设置 上,左,右,宽高比
- 后面的视图与前一个等宽,等高,垂直间距,水平居中
10.口碑-头部视图-代码设置缩放比例
- 在xib中调整好位置高度,并在视图加载完成之后根据'宽度'和'屏幕的宽度'和'屏幕的高度'设置**高度**
- tableView的宽 / xib中HeaderView的宽 * xib中HeaderView的高 得到按比例缩放后的realHeaderView的高
- xib中的高 * tableView的宽 / xib中的宽
- xib的高 / xib的宽 * tableView的宽
2: 3 == 4: 6?
11.口碑-尾部视图
- 灰度,R=G=B
- 1为白色 0为黑色
12.口碑-组头
- tableview两种样式组头的区别?
- grouped跟着tableView滚动出去
- plain可以悬停在上方
13.口碑-添加导航控制器
Editor -> Embedin -> Navgation Contorller
20-MarkMan测量截图位置
21-KVC
// 方法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];
- 在保证模型的属性和字典中的key相同的情况下,字典转模型使用setValuesForKeysWithDictionary:方法
- keys value coding 键值编码
附整体代码
/**总步骤:
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 = [[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];
*/
//
// ZFBBusinessCell.h
#import <UIKit/UIKit.h>
@class ZFBBusinessModel;
@interface ZFBBusinessCell : UITableViewCell
///模型属性 注意模型是个对象 用strong
@property (nonatomic , strong) ZFBBusinessModel *businessModel;
@end
//
// ZFBBusinessCell.m
#import "ZFBBusinessCell.h"
#import "ZFBBusinessModel.h"//注意导入头文件
#import "ZFBLeverStar.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;
///星级评价
@property (weak, nonatomic) IBOutlet ZFBLeverStar *leverView;
@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).description];
_distanceLabel.text = [NSString stringWithFormat:@"距离北京石油化工学院 %@ m",businessModel.distance];
_discountLabel.text = businessModel.discount;
//传分
_leverView.lever = businessModel.level;
}
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
@end
//
// 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