iOS开源&高仿项目精选iOS工作系列iOS

iOS高仿城觅项目(开发思路和代码)

2015-07-19  本文已影响39774人  Top_熊

前言

很早前就想和大家分享一些真正项目的开发思路和流程,一直没能鼓起勇气写,毕竟这不是一件轻松的事情,一个月前,公司的项目上线,鼓起勇气利用每天的休息时间写了这个半成品的项目,基本的UI与逻辑都打通了,剩下的细节需要时间修改,由于下周工作有新需求需要开发,可能很长一段时间没精力来写了,本想把所有的功能都实现,并且修改掉发现的bug后再给大家学习的,由于工作的情况,可能会延缓2个月左右的日子(后续我会把功能陆续实现),代码我传到github上了,相信读者会发现很多的不足之处,希望大家可以自行尝试修改一些bug和实现剩余的部分功能,说实话一直是每天晚上8点写到凌晨3点左右,因为有些朋友不希望看见过多的三方库,所以基本非常耗时的需求我都尽力自己封装的,当然我写的有很多不足之处,希望大家可以指出不足之处,共同进步,由于时间比较匆忙,我的code一次review都没,望大家包涵,项目使用OC写的,算是对OC的一个纪念吧。等忙完这段时间,我会再用swift来写一个新的项目分享给大家,喜欢的朋友可以继续关注我的博客,我会第一时间将新项目到博客上

这篇博客是配合代码来写的,大部分的图片都是我自己扣下来的,数据也是我直接写的假数据,在代码中基本每一步我都有详细的注释,唠叨的有点多,下面就先展示下我仿的这个项目吧,有兴趣的朋友可以下一下原版的app参照一下(写的过程中app更新了一点新功能)

项目展示,由于没有数据,所以所有的cell显示的都是我自己写的数据

抽屉抽屉
首页部分效果首页部分效果
首页效果首页效果
部分效果部分效果
发现发现 消息消息 搜索搜索 设置设置 模糊效果模糊效果 代码注释展示代码注释展示 代码注释展示代码注释展示

还有很多细节就不一一展示了,大家将代码运行下自己查看即可

由于内容比较多,我就按功能模块来介绍给大家了

首先是左边抽屉的效果以及点击按钮切换控制器

UIViewController *testVC = [UIViewController new];
[self.view addSubview:testVC.view];
[self addChildViewController:testVC];
    //暂时先做没有登陆的情况的点击
   WNXNavigationController *newNC = self.childViewControllers[toIndex];

   if (toIndex == WNXleftButtonTypeIcon) {
       newNC = self.childViewControllers[fromIndex];
   }
   //移除旧的控制器view
   WNXNavigationController *oldNC = self.childViewControllers[fromIndex];
   [oldNC.view removeFromSuperview];

   //添加新的控制器view
   [self.view addSubview:newNC.view];
   newNC.view.transform = oldNC.view.transform;

   self.showViewController = newNC.childViewControllers[0];

首页

[UINavigationBar appearanceWhenContainedIn:self, nil]
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    //隐藏系统的导航条,由于需要自定义的动画,自定义一个view来代替导航条
    [self.navigationController setNavigationBarHidden:YES animated:YES];
}

发现

 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    /*  拦截事件响应者,不论触发了cell中的哪个控件都交给iconButton来响应 */
    // 1.判断当前控件能否接收事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;

    // 2. 判断点在不在当前控件
    if ([self pointInside:point withEvent:event] == NO) return nil;

    return self.iconButton;
}
-(void)iconButtonClick:(UIButton *)sender
{
    //点击button通知代理
    if ([self.delegate respondsToSelector:@selector(foundCollectionViewCell:)]) {
        [self.delegate foundCollectionViewCell:self];
        }
}

登陆

消息

    [self.datas removeObjectAtIndex:indexPath.row];
    [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight];
    //加入延时调用是防止删除后过快的就刷新tableView
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.tableView reloadData];
    });

搜索

搜索搜索
(void)setHotDatas:(NSMutableArray *)hotDatas
{
    _hotDatas = hotDatas;
    //判断是长度是否是4,开发中可以这样写 应该服务器返回几条数据就赋值多少,而不是固定的写死数据,
    //万一服务器返回的数据有错误,会造成用户直接闪退的,有
    //时在某些不是很重要的东西无法确定返回的是否正确,建议用
    //@try    @catch来处理,
    //即便返回的数据有误,也可以让用户继续别的操作,
    //而不会在无关紧要的小细节上造成闪退

    if (hotDatas.count == 4) {
        [self.hotButton1 setTitle:hotDatas[1] forState:UIControlStateNormal];
        [self.hotButton2 setTitle:hotDatas[0] forState:UIControlStateNormal];
        [self.hotButton3 setTitle:hotDatas[2] forState:UIControlStateNormal];
        [self.hotButton4 setTitle:hotDatas[3] forState:UIControlStateNormal];
    }
    [self layoutIfNeeded];

    //算出间距
    CGFloat margin = (WNXAppWidth - 40 -
    self.hotButton1.bounds.size.width -
    self.hotButton2.bounds.size.width -
    self.hotButton3.bounds.size.width -
    self.hotButton3.bounds.size.width) / 3;

    //更新约束
    [self.hotButton2 updateConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.hotButton1.right).offset(margin);
    }];

    [self.hotButton3 updateConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.hotButton2.right).offset(margin);
    }];

    [self.hotButton4 updateConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.hotButton3.right).offset(margin);
    }];
}

模糊效果

模糊效果模糊效果

详情页

详情页展示详情页展示
  -(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView == self.rmdTableView || scrollView == self.infoTableView) {//说明是tableView在滚动

        //记录当前展示的是那个tableView
        self.showingTableView = (UITableView *)scrollView;

        //记录出上一次滑动的距离,因为是在tableView的contentInset中偏移的ScrollHeadViewHeight,所以都得加回来
        CGFloat offsetY = scrollView.contentOffset.y;
        CGFloat seleOffsetY = offsetY - self.scrollY;
        self.scrollY = offsetY;

        //修改顶部的scrollHeadView位置 并且通知scrollHeadView内的控件也修改位置
        CGRect headRect = self.topView.frame;
        headRect.origin.y -= seleOffsetY;
        self.topView.frame = headRect;


        //根据偏移量算出alpha的值,渐隐,当偏移量大于-180开始计算消失的值
        CGFloat startF = -180;
        //初始的偏移量Y值为 顶部俩个控件的高度
        CGFloat initY = SelectViewHeight + ScrollHeadViewHeight;
        //缺少的那一段渐变Y值
        CGFloat lackY = initY + startF;
        //自定义导航条高度
        CGFloat naviH = 64;

        //渐隐alpha值
        CGFloat alphaScaleHide = 1 - (offsetY + initY- lackY) / (initY- naviH - SelectViewHeight - lackY);
        //渐现alph值
        CGFloat alphaScaleShow = (offsetY + initY - lackY) /  (initY - naviH - SelectViewHeight - lackY) ;

        if (alphaScaleShow >= 0.98) {
            //显示导航条
            [UIView animateWithDuration:0.04 animations:^{
                self.naviView.alpha = 1;
            }];
        } else {
            self.naviView.alpha = 0;
        }
        self.topScrollView.naviView.alpha = alphaScaleShow;
        self.subTitleLabel.alpha = alphaScaleHide;
        self.smallImageView.alpha = alphaScaleHide;

        /* 这段代码很有深意啊。。最开始是直接用偏移量算的,但是回来的时候速度比较快时偏移量会偏度很大
         然后就悲剧了。换了好多方法。。最后才开窍T——T,这一段我会在blog里面详细描述我用的各种错误的方法
         用了KVO监听偏移量的值,切换了selectView的父控件,切换tableview的headView。。。
         */
        if (offsetY >= -(naviH + SelectViewHeight)) {
            self.selectView.frame = CGRectMake(0, naviH, WNXAppWidth, SelectViewHeight);
        } else {
            self.selectView.frame = CGRectMake(0, CGRectGetMaxY(self.topView.frame), WNXAppWidth, SelectViewHeight);
        }

        CGFloat scaleTopView = 1 - (offsetY + SelectViewHeight + ScrollHeadViewHeight) / 100;
        scaleTopView = scaleTopView > 1 ? scaleTopView : 1;

        //算出头部的变形 这里的动画不是很准确,好的动画是一点一点试出来了  这里可能还需要配合锚点来进行动画,关于这种动画我会在以后单开一个项目配合blog来讲解的 这里这就不细调了
        CGAffineTransform transform = CGAffineTransformMakeScale(scaleTopView, scaleTopView );
        CGFloat ty = (scaleTopView - 1) * ScrollHeadViewHeight;
        self.topView.transform = CGAffineTransformTranslate(transform, 0, -ty * 0.2);

        //记录selectViewY轴的偏移量,这个是用来计算每次切换tableView,让新出来的tableView总是在头部用的,
        //现在脑子有点迷糊 算不出来了。。凌晨2.57分~
        CGFloat selectViewOffsetY = self.selectView.frame.origin.y - ScrollHeadViewHeight;

        if (selectViewOffsetY != -ScrollHeadViewHeight && selectViewOffsetY <= 0) {

            if (scrollView == self.rmdTableView) {

                self.infoTableView.contentOffset = CGPointMake(0, -245 - selectViewOffsetY);

            } else {

                self.rmdTableView.contentOffset = CGPointMake(0, -245 - selectViewOffsetY);

            }
        }

    } else {
        //说明是backgroundScrollView在滚动

        CGFloat selectViewOffsetY = self.selectView.frame.origin.y - ScrollHeadViewHeight;
        //让新出来的tableView的contentOffset正好卡在selectView的头上,还是有bug
        if (selectViewOffsetY != -ScrollHeadViewHeight && selectViewOffsetY <= 0) {

            if (self.showingTableView == self.rmdTableView) {

                self.infoTableView.contentOffset = CGPointMake(0, -245 - selectViewOffsetY);

            } else {

                self.rmdTableView.contentOffset = CGPointMake(0, -245 - selectViewOffsetY);

            }
        }

        CGFloat offsetX = self.backgroundScrollView.contentOffset.x;
        NSInteger index = offsetX / WNXAppWidth;

        CGFloat seleOffsetX = offsetX - self.scrollX;
        self.scrollX = offsetX;

        //根据scrollViewX偏移量算出顶部selectViewline的位置
        if (seleOffsetX > 0 && offsetX / WNXAppWidth >= (0.5 + index)) {
            [self.selectView lineToIndex:index + 1];
        } else if (seleOffsetX < 0 && offsetX / WNXAppWidth <= (0.5 + index)) {
            [self.selectView lineToIndex:index];
        }
    }
}

这些就是这个项目的大体思路,当然还有很多很多的细节都在代码中,感觉自己有很多想要表达的但是没法说出来,所以我在代码中加的很详细的注释,第一次尝试将思路写出来,感觉有很多不足,本应该每完成一个功能就总结一下,而我是在发布的晚上回头总结的,有很多当时的思路不是很清晰了...以后我会改善的,大家有什么意见可以直接留言,我看到会一一回复的!

注意下载完工程请直接打开 点击运行点击运行

附上代码的下载地址

点击下载代码

我的微博链接

上一篇 下一篇

猜你喜欢

热点阅读