一.工程以及开发流程链接
链接:
https://pan.baidu.com/s/1LETv8wJNkO7ynDQk-H_KWQ
密码:9z4s
二.开发流程
1.设置工程目录
2.添加素材:图片和plist文件
3.使用CocoaPods导入第三方库
- 1.进入终端 进入工程目录 pod init
- 2.open Podfile配置文件 导入需要的第三方库
pod 'MJExtension'
pod 'MJRefresh'
pod 'Masonry'
pod 'ProgressHUD'
- 3.pod install安装
- 4.使用.xcworkspace文件打开
4.使用PCH文件
创建一个新的PCH文件
配置PCH文件路径
a.工程-Build Setting-搜索Prefix Header
b. Precompile Prefix Header 由NO改为YES
c. Prefix Header 告诉系统你的pch文件路径
d. $SRCROOT/工程名/该文件名(直接拖动)
#ifndef PCH___pch
#define PCH___pch
//只导入多个文件都需要导入的类
#import <Masonry.h>
#import <MJRefresh/MJRefresh.h>
#import <MJExtension/MJExtension.h>
#import <ProgressHUD.h>
#import "Constant.h"
#import "UIView+XLFrameExtension.h"
#endif /* PCH___pch */
5.创建模型类
XLCarGroupModel
#import <Foundation/Foundation.h>
#import "XLCarModel.h"
@interface XLCarGroupModel : NSObject
/**一类车对应的字母标题*/
@property (nonatomic,copy) NSString *title;
/**这类车的模型组成的数组*/
@property (nonatomic,copy) NSArray<XLCarModel *> *cars;
@end
XLCarModel
#import <Foundation/Foundation.h>
@interface XLCarModel : NSObject
/**车对应的图片名字*/
@property (nonatomic,copy) NSString *icon;
/**车的名称*/
@property (nonatomic,copy) NSString *name;
@end
六.添加数据管理类
管理数据的读取和其它操作
#import <Foundation/Foundation.h>
@interface XLDataManager : NSObject
//获得数据并返回
+(NSArray *)loadData;
@end
#import "XLDataManager.h"
#import "XLCarGroupModel.h"
#import "XLCarModel.h"
//静态全局变量 只创建一次
static NSArray *dataArray = nil;
@implementation XLDataManager
//返回数据模型的方法的实现
+(NSArray *)loadData{
if (!dataArray) {
//设置模型数组里面对应的模型
[XLCarGroupModel mj_setupObjectClassInArray:^NSDictionary *{
return @{@"cars":@"XLCarModel"};
}];
}
//解析数据
dataArray = [XLCarGroupModel mj_objectArrayWithFilename:@"cars_total.plist"];
return dataArray;
}
@end
七.导入导航栏控制器
八.添加一个头文件 用来管理程序界面配置内容
#ifndef Constant_h
#define Constant_h
//配置导航条的颜色
#define BAR_COLOR [UIColor colorWithRed:37/255.0 green:224/255.0 blue:203/255.0 alpha:1]
//标题的字体
#define TITLE_FONT [UIFont systemFontOfSize:20]
//标题的颜色
#define TITLE_COLOR [UIColor whiteColor]
//标题视图的高度
#define HEAD_VIEW_HEIGHT 60
#endif /* Constant_h */
九.配置标题信息
//设置导航栏显示的颜色
self.navigationController.navigationBar.barTintColor = BAR_COLOR;
//设置导航栏显示的文本
self.title = @"易车";
//设置文本样式
self.navigationController.navigationBar.titleTextAttributes = @{NSFontAttributeName:TITLE_FONT,NSForegroundColorAttributeName:TITLE_COLOR};
十.定义好XLContentScrollView和XLHeadScrollView 相互回调数据的模式
#import <UIKit/UIKit.h>
//定义一个block类型
typedef void(^HeadViewBlock) (NSInteger index);
@interface XLHeadScrollView : UIView
/**回调数据*/
@property (nonatomic,copy) HeadViewBlock block;
/**接收数据*/
//ContentView滚动过程中,传递过来滚动方向和滚动比例的数据
-(void)setScaleRate:(CGFloat)rate Direction:(kDirection)direction;
//ContentView滚动停止,设置我们该选中的
-(void)selectItemAtIndex:(NSInteger)index;
/**头部标题的数组*/
@property (nonatomic,copy) NSArray *itemsArray;
@end
#import <UIKit/UIKit.h>
#import "XLCarGroupModel.h"
//定义一个block类型
typedef void(^ContentBlock) (NSInteger page,kDirection direction,CGFloat rate,BOOL isStop);
@interface XLContentScrollView : UIView
//接受数据,设置滚动到第几页
/**设置显示第几页*/
@property (nonatomic,assign) NSInteger currentPage;
/**回调数据*/
@property (nonatomic,copy) ContentBlock block;
/**显示的数组*/
@property (nonatomic,copy) NSArray *modelsArray;
@end
十一.在ViewController里面创建两个视图,并加载数据
1.创建视图
//创建头部视图 传数据 标题 frame
self.headView = [XLHeadScrollView new];
//_headView.backgroundColor = [UIColor orangeColor];
[self.view addSubview:_headView];
//创建内容视图
self.contentView = [XLContentScrollView new];
_contentView.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:_contentView];
__weak typeof(self) weakSelf = self;
//接受HeadView传递过来的数据,contentView做出改变
[_headView setBlock:^(NSInteger index) {
//告诉内容视图滚动到对应的page
weakSelf.contentView.currentPage = index;
}];
//接受contentView传递过来的数据,HeadView做出改变
[_contentView setBlock:^(NSInteger page, kDirection direction, CGFloat rate, BOOL isStop) {
//判断页面是否停止滚动
if (isStop) {
//告诉滚动视图选择哪一个栏目
[weakSelf.headView selectItemAtIndex:page];
}else{
//滚动过程中
[weakSelf.headView setScaleRate:rate Direction:direction];
}
}];
2.加载数据
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
//添加约束
[self addConstant];
//添加一个视图提示用户下载
[ProgressHUD show:@"正在努力加载 请耐心等待..."];
//开启一个线程 下载数据
//创建一个队列 1.并行 2.串行
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
//在这个队列上开启一个异步线程
dispatch_async(queue, ^{
//下载数据
self.dataArray = [XLDataManager loadData];
//模拟数据下载
[NSThread sleepForTimeInterval:1];
//告诉主线程刷新界面
dispatch_sync(dispatch_get_main_queue(), ^{
//将数据传递给ContentView
self.contentView.modelsArray = self.dataArray;
//将数据传递给HeadView
self.headView.itemsArray = self.dataArray;
//暂停小菊花
[ProgressHUD dismiss];
});
});
}
//添加约束
-(void)addConstant{
[_headView mas_makeConstraints:^(MASConstraintMaker *make) {
//左右间距0
make.leading.trailing.equalTo(self.view).offset(0);
//高度
make.height.mas_equalTo(HEAD_VIEW_HEIGHT);
//和导航栏间距为0
make.top.equalTo(self.navigationController.navigationBar.mas_bottom).offset(0);
}];
[_contentView mas_makeConstraints:^(MASConstraintMaker *make) {
//左右间距0
make.leading.trailing.bottom.mas_equalTo(0);
//和导航栏间距为0
make.top.equalTo(_headView.mas_bottom).offset(0);
}];
}
十二.XLContentScrollView数据的配置
1.当一个类管理的数据过于庞大,就应该创建一个UIView管理,所以我们自定义一个继承于UIView的XLContentTableView管理UITableView
#import <UIKit/UIKit.h>
#import "XLCarGroupModel.h"
@interface XLContentTableView : UIView
/**模型*/
@property (nonatomic,strong) XLCarGroupModel *model;
@end
#import "XLContentTableView.h"
#import "XLContentViewCell.h"
@interface XLContentTableView ()<UITableViewDelegate,UITableViewDataSource>
/**创建UITableView*/
@property (nonatomic,strong) UITableView *tableView;
@end
@implementation XLContentTableView
//重写initWithFrame方法 布局
-(instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
//创建tableView
self.tableView = [[UITableView alloc] init];
_tableView.delegate = self;
_tableView.dataSource = self;
[self addSubview:_tableView];
//注册
[_tableView registerNib:[UINib nibWithNibName:@"XLContentViewCell" bundle:nil] forCellReuseIdentifier:@"cellID"];
}
return self;
}
-(void)layoutSubviews{
_tableView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
}
-(void)setModel:(XLCarGroupModel *)model{
_model = model;
//有了数据
[_tableView reloadData];
[self setNeedsLayout];
[self layoutIfNeeded];
}
//row的数量
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return _model.cars.count;
}
//row显示的cell
-(XLContentViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//队列里面获取重复利用的cell 没有就自己创建
XLContentViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellID"];
//设置显示的数据
cell.model = _model.cars[indexPath.row];
return cell;
}
@end
2.UITableView中显示的自定义的Cell
#import <UIKit/UIKit.h>
#import "XLCarModel.h"
@interface XLContentViewCell : UITableViewCell
/**一辆车的具体数据模型*/
@property (nonatomic,strong) XLCarModel *model;
@end
#import "XLContentViewCell.h"
@interface XLContentViewCell ()
//图片
@property (weak, nonatomic) IBOutlet UIImageView *iconImageView;
//文本
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@end
@implementation XLContentViewCell
- (void)awakeFromNib {
[super awakeFromNib];
}
//重写模型的set方法
-(void)setModel:(XLCarModel *)model{
_model = model;
//给控件赋值
_iconImageView.image = [UIImage imageNamed:_model.icon];
_nameLabel.text = _model.name;
}
@end
3.XLContentScrollView数据的具体配置
#import "XLContentScrollView.h"
#import "XLContentTableView.h"
@interface XLContentScrollView ()<UIScrollViewDelegate>
/**作为容器的UIScrollView*/
@property (nonatomic,strong) UIScrollView *scrollView;
/**记录上一个滚动的点的x*/
@property (nonatomic,assign) CGFloat lastOffsetX;
/**存放放在容器里面的UITableView*/
@property (nonatomic,strong) NSMutableArray *tableViewArray;
/**记录是否正在滚动*/
@property (nonatomic,assign) BOOL flag;
@end
@implementation XLContentScrollView
//重写initWithFrame方法 布局
-(instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
//创建scrollView
self.scrollView = [[UIScrollView alloc] init];
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.bounces = NO;
_scrollView.pagingEnabled = YES;
_scrollView.delegate = self;
[self addSubview:_scrollView];
}
return self;
}
-(void)layoutSubviews{
[super layoutSubviews];
//设置scrollView的frame
_scrollView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
//设置22个tableView的frame
for (int i = 0; i < self.tableViewArray.count; i++) {
XLContentTableView *tableView = [self.tableViewArray objectAtIndex:i];
tableView.frame = CGRectMake(i*self.width, 0, self.width, self.height);
_scrollView.contentSize = CGSizeMake(self.tableViewArray.count*self.width, 0);
}
}
//重写model的set方法
-(void)setModelsArray:(NSArray *)modelsArray{
_modelsArray = modelsArray;
self.tableViewArray = [NSMutableArray array];
for (int i = 0; i < _modelsArray.count; i++) {
//当一个类管理的数据过于庞大,就应该创建一个UIView管理-》XLContentTableView
//创建自定义的tableView视图
XLContentTableView *tableView = [[XLContentTableView alloc] init];
//设置数据
tableView.model = _modelsArray[I];
[self.tableViewArray addObject:tableView];
//将tableView添加到scrollView上面去
[_scrollView addSubview:tableView];
}
[self setNeedsLayout];
[self layoutIfNeeded];
}
//正在滚动
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
//只有正在滚动才会触发这里
if (_flag == YES) {
//滚动的方向 滚动的比例
// kDirection direction;
// //方向
// if (scrollView.contentOffset.x > _lastOffsetX) {
// //右边
// direction = kDirectionRight;
// } else {
// //左边
// direction = kDirectionLeft;
// }
kDirection direction = kDirectionLeft;
//方向
if (scrollView.contentOffset.x > _lastOffsetX) {
//右边
direction = kDirectionRight;
}
//确定比例
CGFloat distance = fabs([scrollView.panGestureRecognizer translationInView:scrollView].x);
CGFloat rate = distance/scrollView.width;
//回调数据
if (self.block) {
self.block(0,direction,rate,NO);
}
_lastOffsetX = scrollView.contentOffset.x;
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
//开始滚动视图
_flag = YES;
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
//停止拖拽
_flag = NO;
}
//停止减速
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
//获取页数
NSInteger page = scrollView.contentOffset.x/scrollView.width;
//回调当前第几页的事情
if (self.block) {
self.block(page, kDirectionNone, 0, YES);
}
}
//重写currentPage的set方法
-(void)setCurrentPage:(NSInteger)currentPage{
_currentPage = currentPage;
//设置显示的页数
[_scrollView setContentOffset:CGPointMake(_currentPage*_scrollView.width, 0) animated:YES];
}
@end
十三.XLHeadScrollView数据的配置
#import "XLHeadScrollView.h"
#import "XLCarGroupModel.h"
@interface XLHeadScrollView ()
/**滚动视图*/
@property (nonatomic,strong) UIScrollView *scrollView;
/**记录选中的item*/
@property (nonatomic,strong) UIButton *currentSelectButton;
/**记录将要选中的item*/
@property (nonatomic, strong) UIButton * willSelectBtn;
/**记录上一次滑动的方向*/
@property (nonatomic, assign) kDirection lastDirection;
@end
@implementation XLHeadScrollView
//重写initWithFrame方法 布局
-(instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
self.scrollView = [[UIScrollView alloc] init];
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.bounces = NO;
[self addSubview:_scrollView];
_scrollView.layer.borderColor = BAR_COLOR.CGColor;
_scrollView.layer.borderWidth = 1;
}
return self;
}
-(void)layoutSubviews{
//给具体的frame
_scrollView.frame = CGRectMake(0, 0, self.width, self.height);
}
-(void)setItemsArray:(NSArray *)itemsArray{
_itemsArray = itemsArray;
for (int i = 0; i < itemsArray.count; i++) {
//读取模型
XLCarGroupModel *model = [itemsArray objectAtIndex:i];
//创建一个按钮
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(i*kItemWidth, 0, kItemWidth, self.height)];
btn.tag = I+1;
[btn addTarget:self action:@selector(itemDidClicked:) forControlEvents:UIControlEventTouchUpInside];
[btn setTitle:model.title forState:UIControlStateNormal];
[btn setTitleColor:ITEM_NORMAL_COLOR forState:UIControlStateNormal];
[btn setTitleColor:ITEM_SELECT_COLOR forState:UIControlStateSelected];
[_scrollView addSubview:btn];
if (i == 0) {
//默认选中第一个
[self selectItemAtIndex:0];
}
//设置scrollView的内容尺寸
_scrollView.contentSize = CGSizeMake(itemsArray.count*kItemWidth, 0);
}
}
//设置选中第几个
-(void)selectItemAtIndex:(NSInteger)index{
//通过index获取对应的button对象
UIButton *btn = [_scrollView viewWithTag:index+1];
if (![btn isEqual:_currentSelectButton]) {
//先将原来的还原
_currentSelectButton.selected = NO;
[UIView animateWithDuration:0.3 animations:^{
//回到原来的大小
_currentSelectButton.transform = CGAffineTransformIdentity;
}];
btn.selected = YES;
[UIView animateWithDuration:0.3 animations:^{
btn.transform = CGAffineTransformMakeScale(kMaxScale, kMaxScale);
}];
self.currentSelectButton = btn;
[self scrollToButton:btn];
}
}
-(void)itemDidClicked:(UIButton *)sender{
[self selectItemAtIndex:sender.tag-1];
//回调当前是第几个
if (self.block) {
self.block(sender.tag-1);
}
[self scrollToButton:sender];
}
//让点击的item滚动到中心位置
-(void)scrollToButton:(UIButton *)sender{
if (sender.center.x > _scrollView.width/2.0) {
if (_scrollView.contentSize.width-sender.center.x < _scrollView.width/2.0) {
//最右边不能滚动
CGFloat offset = _scrollView.contentSize.width-_scrollView.width;
[_scrollView setContentOffset:CGPointMake(offset, 0) animated:YES];
} else{
//中间需要滚动
CGFloat offset = sender.center.x-_scrollView.width/2;
[_scrollView setContentOffset:CGPointMake(offset, 0) animated:YES];
}
}else{
CGFloat offset = 0;
[_scrollView setContentOffset:CGPointMake(offset, 0) animated:YES];
}
}
//正在滚动
-(void)setScaleRate:(CGFloat)rate Direction:(kDirection)direction{
if (direction != _lastDirection) {
//1.第一次滑动
//2.第一次向右 现在向左
//3.第一次向左 现在向右
if (_lastDirection != kDirectionNone){
_willSelectBtn.selected = NO;
_willSelectBtn.transform = CGAffineTransformIdentity;
}
}
NSInteger tag = _currentSelectButton.tag ;
if (direction == kDirectionRight) {
//当前这个是不是最后一个
if(_currentSelectButton.tag != _itemsArray.count){
//获取下一个按钮的tag值
tag ++;
}
} else{
//判断是不是第一个
if(_currentSelectButton.tag != 1){
//获取前面一个按钮的tag值
tag --;
}
}
//即将选中的按钮
self.willSelectBtn = [_scrollView viewWithTag:tag];
//即将选中的按钮放大
_willSelectBtn.transform = CGAffineTransformMakeScale(1+(kMaxScale-1)*rate, 1+(kMaxScale-1)*rate);
//当前选中的按钮
_currentSelectButton.selected = NO;
//当前选中的按钮缩小
_currentSelectButton.transform = CGAffineTransformMakeScale(kMaxScale-(kMaxScale-1)*rate,kMaxScale-(kMaxScale-1)*rate);
_willSelectBtn.selected = YES;
_lastDirection = direction;
}
@end