高仿美团<二>

亲爱的粉友们我又回来了。

继上一次高仿美团主界面之后我说我会持续更新的,还说重要的事情说三遍。由于最近项目比较紧就很比较难挤出时间去coding;
不话痨了:上次就想给大家分享一个项目的最初搭建但是,由于太匆忙就没上,今天补上 哈哈!先分析一下项目的目录结构吧:
其他的文件夹就不多说了,主要的类都在Class里面按照功能模块建一个文件夹,最好建一个工具类,和分类这样业务更加分明。其中工具类把整个项目都用到的功能抽取出来。比如说快速加载XIB,plist,网络等等。


屏幕快照 2015-08-30 15.55.25.png

接下来在每个功能模块的文件夹里面放MVC。

屏幕快照 2015-08-30 16.01.12.png

这些都只是个人习惯,个人觉得比较爽比较舒服,没有好坏之分。
有图有真相:

主界面-首页
上门
商家
我的
更多
运行效果

接下来我按一个功能模块一个模块讲解整个项目

首页

首先先声明下本项目中的下啦刷新用的是MJRefresh,还有的第三方框架有MJExtension,AFN,SDWebImage(顺便个MJ打个广告)
#pragma mark - 设置下啦刷新
-(void)setRefreshIntableView{
[self.firstTableView addGifHeaderWithRefreshingTarget:self refreshingAction:@selector(refreshData)];`

//设置普通状态的动画图片
NSMutableArray *idleImages = [NSMutableArray array];
for (NSUInteger i = 1; i<=60; ++i) {
    UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"dropdown_anim__000%zd",i]];
    [idleImages addObject:image];
}
[self.firstTableView.gifHeader setImages:idleImages forState:MJRefreshHeaderStateIdle];

//设置即将刷新状态的动画图片
NSMutableArray *refreshingImages = [NSMutableArray array];
for (NSInteger i = 1; i<=3; i++) {
    UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"dropdown_loading_0%zd",i]];
    [refreshingImages addObject:image];
}
[self.firstTableView.gifHeader setImages:refreshingImages forState:MJRefreshHeaderStatePulling];

//设置正在刷新是的动画图片
[self.firstTableView.gifHeader setImages:refreshingImages forState:MJRefreshHeaderStateRefreshing];

//马上进入刷新状态
[self.firstTableView.gifHeader beginRefreshing];

}
解释如上代码注释,已经非常清楚了,就是给当前的tableView, [self.firstTableView addGifHeaderWithRefreshingTarget:self refreshingAction:@selector(refreshData)];
然后将所需要的动画图放在一个数组里面。然后设置三种状态的刷新。
说白了首页就是一个tableView。这个tableView分为5个section.
每个section里面的cell进行自定义。就是这么简单粗暴。

再说下怎么封装tableViewCell,为什么我们要封装他呢?为什么要封装他,原因通俗一点就是,cell是个视图,视图怎么创建cell自己最清楚。没必要让控制器知道。
送大家我之前说我的一句话:

控制器知道的越少越好,否则你将死的很惨!!!。

举个例子:
1.创建自定义的tableViewCell要不要XIB随你(我这个例子用XIB)。

#import <UIKit/UIKit.h>
@class JFHotQueueModel;
@interface JFHotQueueCell : UITableViewCell
@property (weak, nonatomic) IBOutlet UIImageView *hotQueueImageView;
@property (weak, nonatomic) IBOutlet UILabel *mianLabel;
@property (weak, nonatomic) IBOutlet UILabel *subtitleLabel;
//提供一个模型的set方法
@property (nonatomic, strong)JFHotQueueModel *hotQueueModel;
//提供一个类方法传一个tableView快速创建一个cell
+ (instancetype)cellWithTableView:(UITableView *)tableView;

这个地方再送你一句话:tableView,collectionView,地图标注等都可以这个干,(举一返三,这是一个能力)几百万个项目,每个项目N种做法,难到我们都要敲一遍?(SB才这个干)起初我们可以模仿,在模仿中总结。一步一个脚印。我去扯的优点远了.

tableViewcell.m里面的实现

+ (instancetype)cellWithTableView:(UITableView *)tableView
{
    static NSString *ID = @"hotqueue";
    JFHotQueueCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        // 从xib中加载cell
        cell = [[[NSBundle mainBundle] loadNibNamed:@"JFHotQueueCell" owner:nil options:nil] lastObject];
    }
    return cell;
}
#重写model的set方法,给XIB里面的控件赋值
-(void)setHotQueueModel:(JFHotQueueModel *)hotQueueModel{
    _hotQueueModel = hotQueueModel;
    NSURL *urlStr = [NSURL URLWithString:hotQueueModel.imageUrl];
    [self.hotQueueImageView sd_setImageWithURL:urlStr  placeholderImage:nil];
    self.mianLabel.text = hotQueueModel.title;
    self.subtitleLabel.text = hotQueueModel.comment;
}

好了第二个界面:

上门

这个界面主要讲下,这么封装图片轮播器

cell上的轮播器

毫无疑问这个轮播器在cell上面。
只要将cell创建好后将封装好的轮播器加在上面就完事了
自定义轮播器的视图
.h文件

@interface JFImageScrollView : UIView
@property (nonatomic, strong)UIScrollView *scrollView;
@property (nonatomic, strong)UIPageControl *pageControl;
@property (nonatomic, strong)NSArray *imgArray;
-(void)setImageArray:(NSArray *)imageArray;
/**
 *  创建一个ScrollView
 *
 *  @param frame      供外界提供一个frame
 *  @param imageArray 供外界提供一个图片数组
 *
 *  @return 返回一个自定义的ScrollView
 */
-(JFImageScrollView * )initWithFrame:(CGRect)frame imageArray:(NSArray *)imageArray;

.m文件

@interface JFImageScrollView ()<UIScrollViewDelegate>{
    NSTimer *_timer;
    int _pageNumber;
}
@end
@implementation JFImageScrollView
-(JFImageScrollView * )initWithFrame:(CGRect)frame imageArray:(NSArray *)imageArray{
    self = [super initWithFrame:frame];
    if (self) {
        self.scrollView = [[UIScrollView alloc]initWithFrame:frame];
        self.scrollView.contentSize = CGSizeMake(4 * SCREENWIDTH, frame.size.height);
        self.scrollView.pagingEnabled = YES;
        self.scrollView.delegate = self;
        self.scrollView.showsHorizontalScrollIndicator = NO;
        //添加图片
        for(int i = 0 ; i < 10; i++){
            UIImageView *imageView = [[UIImageView alloc] init];
            imageView.frame = CGRectMake(i*SCREENWIDTH, 0, SCREENWIDTH, frame.size.height);
            imageView.tag = i+10;
           // UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(OnTapImage:)];
            //[imageView addGestureRecognizer:tap];
            imageView.userInteractionEnabled = YES;
            [self.scrollView addSubview:imageView];
        }
        [self addSubview:self.scrollView];
        
        //
        self.pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(SCREENWIDTH/2-40, frame.size.height-40, 80, 30)];
        self.pageControl.currentPage = 0;
        self.pageControl.numberOfPages = 6;
        [self addSubview:self.pageControl];
        
        [self addTimer];

        
    }
    return self;

}

-(void)setImageArray:(NSArray *)imageArray{
    _pageNumber = (int)imageArray.count;
    self.scrollView.contentSize = CGSizeMake(imageArray.count * SCREENWIDTH, self.frame.size.height);
    self.pageControl.numberOfPages = imageArray.count;
    //添加图片
    for(int i = 0 ; i < imageArray.count; i++){
        UIImageView *imageView = (UIImageView *)[self.scrollView viewWithTag:i+10];
        imageView.backgroundColor = [UIColor redColor];

        NSString *imageName =[NSString stringWithFormat:@"%@",imageArray[i]];
        NSLog(@"%@",imageName);
        [imageView sd_setImageWithURL:[NSURL URLWithString:imageName] placeholderImage:[UIImage imageNamed:@"bg_customReview_image_default"]];        
    }
}
//添加计时器
-(void)addTimer{
    _timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(netxPage) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
}
//移除计时器
-(void)removeTimer{
    [_timer invalidate];
    _timer = nil;
}
//计时器每一秒的响应方法
-(void)netxPage{
    int page = (int)self.pageControl.currentPage;
    if (page == _pageNumber-1) {
        page = 0;
    }else{
        page++;
    }
    //滚动scrollview
    CGFloat x = page * self.scrollView.frame.size.width;
    self.scrollView.contentOffset = CGPointMake(x, 0);
}

#pragma mark - UIScrollViewDelegate
//滑动时scrollView代理方法,计算出当前滑动到哪里。加上视图宽度的一半,再整除滚动视图宽度,算出第几页。
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
    CGFloat scrollViewW = scrollView.frame.size.width;
    CGFloat x = scrollView.contentOffset.x;
    int page = (x + scrollViewW/2)/scrollViewW;
    self.pageControl.currentPage = page;
}
//开始拖动时,就关闭计时器,因为这个时候用户在交互。
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    [self removeTimer];
}
//结束拖动的时候就立马再加上计时器
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    [self addTimer];
}

/**
 *  移除定时器
 */
-(void)dealloc{
    [self removeTimer];
}

接下来:就是创建八个视图给每个视图加手势,之前已经说过了,就不赘述。


八个视图

商家

分类

这个界面主要讲下这个自定义视图怎么自定义的
如图所示很明显就是两个tableVew.
上代码:
.h

@protocol JFMerchantFilterViewDelegate <NSObject>

@optional
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath withId:(NSNumber *)ID ;

@end
@interface JFMerchantFilterView : UIView
/**
 *  全部分类的tableView
 */
@property(nonatomic, strong) UITableView *tableViewOfGroup;
/**
 *  每组详情的tableView
 */
@property(nonatomic, strong) UITableView *tableViewOfDetail;
@property (nonatomic, weak) id <JFMerchantFilterViewDelegate> delegate;

.m
既然是两个tableView那么就提供两个数据源,两个下标,用来识别那个tableView被点击了哪一行。

@interface JFMerchantFilterView ()<UITableViewDataSource, UITableViewDelegate>
{
    NSMutableArray *_bigGroupArray;//左边分组tableview的数据源
    NSMutableArray *_smallGroupArray;//右边分组tableview的数据源
    NSInteger _bigSelectedIndex;
    NSInteger _smallSelectedIndex;
}
@end
@implementation JFMerchantFilterView
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/
-(instancetype)initWithFrame:(CGRect)frame{
    self =[super initWithFrame:frame];
    if (self) {
        _bigGroupArray = [[NSMutableArray alloc] init];
        _smallGroupArray = [[NSMutableArray alloc] init];
        [self initViews];
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [self getCateListData];
        });
    }
    return self;
}

-(void)initViews{
    for (int i = 0 ; i < 2; i ++) {
        
        UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(i *TableViewW/2,  0, TableViewW/2, TableViewH) style:UITableViewStylePlain];
        tableView.tag = 10 + i ;
        tableView.delegate = self;
        tableView.dataSource = self;
        if (i == 0 ) {
            self.tableViewOfGroup =tableView;
            self.tableViewOfGroup.backgroundColor = [UIColor whiteColor];

        }else{
            self.tableViewOfDetail =tableView;
            self.tableViewOfDetail.backgroundColor = RGB(242, 242, 242);

        }
        tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
        [self addSubview:tableView];

    }

}

-(void)getCateListData{
    NSString *urlStr = [[GetUrlString sharedManager]urlWithCateListStr];
    [NetWork sendGetUrl:urlStr withParams:nil success:^(id responseBody) {
        JFLog(@"%@", responseBody);
        NSMutableArray *dataArray = [responseBody objectForKey:@"data"];
        for (int i = 0; i < dataArray.count; i++) {
            JFMerchantCataGroupModel *cateModel = [JFMerchantCataGroupModel objectWithKeyValues:dataArray[i]];
            [_bigGroupArray addObject:cateModel];
        }
        [self.tableViewOfGroup reloadData];
    } failure:^(NSError *error) {
        
    }];
}

#pragma mark - UITableViewDataSource
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    if (tableView.tag == 10) {
        return _bigGroupArray.count;
    }else{
        if (_bigGroupArray.count == 0) {
            return 0;
        }
        JFMerchantCataGroupModel *subclassCateM = (JFMerchantCataGroupModel *)_bigGroupArray[_bigSelectedIndex];
        if (subclassCateM.list == nil) {
            return 0;
        }else{
            return subclassCateM.list.count;
        }
    }
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 42;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    if (tableView.tag == 10) {
        JFKindFilterCell *cell = [JFKindFilterCell cellWithTableView:tableView];
        cell.merchantCataGroupModel = _bigGroupArray[indexPath.row];
        return cell;
    }else{
         JFMerchantCataGroupModel *cateModel = (JFMerchantCataGroupModel *)_bigGroupArray[_bigSelectedIndex];
        JFKindSubclassFilterCell *cell = [JFKindSubclassFilterCell cellWithTableView:tableView indexPath:indexPath model:cateModel];
        return cell;
    }
    
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    if (tableView.tag == 10) {
        _bigSelectedIndex = indexPath.row;
        JFMerchantCataGroupModel *cataModel =  _bigGroupArray[_bigSelectedIndex];
        [self.tableViewOfDetail reloadData];
        if (cataModel.list == nil) {
            [self.tableViewOfDetail reloadData];
            [self.delegate tableView:tableView didSelectRowAtIndexPath:indexPath withId:cataModel.id];
        }else{
            [self.tableViewOfDetail reloadData];
        }
    }else{
        _smallSelectedIndex = indexPath.row;
        JFMerchantCataGroupModel *cataModel =  _bigGroupArray[_bigSelectedIndex];
        NSDictionary *dict =  cataModel.list[_smallSelectedIndex];
        NSNumber *ID = [dict objectForKey:@"id"];
//        NSString *name = [dic objectForKey:@"name"];
        [self.delegate tableView:tableView didSelectRowAtIndexPath:indexPath withId:ID ];
        
    }
}

我的和更多

最后两个界面都是比较简单的就不多说了。
如果不懂的话可以直接简信,或者直接评论,我会一一解答。
有好的建议我会非常感谢。
完整的项目源码会在 会下一次放在github上。(这次就不放了哦,下一次会是一个比较完整的JF团购 呈现给大家)

希望你可以继续关注我。

最后重申一下本项目持续更新中。。。

2015 - 08 - 30

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,029评论 4 62
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,125评论 29 470
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,513评论 25 707
  • 在逻辑学中,逻辑门的工作方式非常简单 ---- 让电流通过或者阻止电流通过 布尔表达式可以在电路中实现,这个等价关...
    钱塘阅读 304评论 0 0
  • 你在跑,我在追 小阿妹啊小阿妹 忘记了天黑
    小小仲马阅读 101评论 0 2