iOS高仿下厨房(Objective-C)

作者: _Sven | 来源:发表于2016-05-02 18:49 被阅读22643次

    2017.5.19编辑:因为官方接口变动,所以一些需要根据返回数据进行动态调整布局的地方会崩溃,如果有兴趣可以自行抓取最新接口,然后根据返回数据进行调整。

    前言

    本开源项目讲解了一些App常见功能界面的搭建以及实现思路,适合新手。

    为什么是下厨房?

    下厨房:一个集合了工具、社区与平台电商属性的家庭美食入口。很棒的一个平台,App界面也很好看!

    关于项目(Github地址在文章结尾)

    • 开发环境:Xcode 7.2,语言:Objective-C
    • 用到的工具:Charles抓包工具
    • 仿写程度:只是作为一个练习项目,除了没有接口的界面,以及一些小细节没有调整,其他大部分主要功能都实现了。其他部分童鞋们可以自己尝试实现~
    • 刚开始写的时候控件是用纯代码写的,后来发现太耗时间就改用Xib了
    • 代码如果有不合理的地方(如:命名),请见谅,学习实现思路就好,代码请不要借鉴。
    • 这个开源项目适合新手,基本的界面布局以及业务逻辑都有,看完这个基本也会写简单的App啦
    • 备注:因为当时对block有特殊偏好所以通篇没有一个protocol...代码也是比较新手,嘛主要以实现思路为主!希望能对各位有帮助!

    效果预览

    首页.gif 关注动态.gif 菜谱.gif 帖子与作品.gif 商品界面.gif 商品分类选择.gif 商品界面-图片展示动画.gif 购物车.gif 上传作品.gif 收货地址.gif 搜索界面.gif 菜谱-创建与删除.gif

    一、首页

    首页.png
    布局
    • 如图,首页tableView就可以搞定
    • 日期标题为sectionHeader
    • 下面的就是cell了,下厨房返回的数据中cell有6种模板,通过自定义cell,根据不同模板显示不同效果,很简单就不描述了
    思路:

    顶部导航部分点击事件,通过给每个控件绑定tag,然后定义对应的枚举变量,通过闭包(Block)将事件传递到控制器后,控制器判断枚举值即可。


    1. 跳转的界面控制器

    ① 菜谱

    布局
    • 整个界面是一个UIViewController,放上一个tableView,然后添加底部的收藏、丢进菜篮子自定义view
    • 用料、做法、小贴士、被加入的菜单这四个标题是sectionHeader,其内容对应为一组,每组cell自定义即可
    菜谱 - Header.png 菜谱 - 作品展示.png
    • 作品展示:
    • 这里实现的方法跟上面的用料、做法...一样,独立为一组,组内只有一个cell,cell的contentView里从上至下添加:作品个数Label作品展示CollectionView所有作品Button即可
    • collectionView手势左滑,松开会加载更多作品数据
      这里通过实现scrollView的代理方法,判断contentOffset的值是否达到预定的数值,达到即调用block,然后控制器发送网络请求加载更多数据,刷新界面即可。(这里我只是实现了一个需求,并没有进一步优化调整)
    菜谱 - 底部.png
    • 布局:
      如图即可,底部加入菜单button也可以是sectionFooter,
      虽然下厨房几乎没有边框(有分割线),但仔细分析还是很好划分的

    ② 作品

    作品.png
    布局
    • 因为关注动态买买买界面跟这个差不多,需要复用到这个界面的内容,所以这整个界面是只有一个cell的tableView
    • 上面是个图片轮播器,下面添加控件即可,描述Label以及用户评论Label的高度是有内容决定的,这个只需要在模型中添加一个labelHeight属性,然后在内部计算好高度,直接返回给控件就可以了。控件部分可以根据不同界面显示的不同效果,分割成若干部分,然后给cell添加一个type属性(枚举类型),创建的时候告诉cell属于哪一种type,然后根据不同type进行调整即可。

    2. 导航

    ① 关注动态

    关注动态.gif
    布局
    • 整个界面就一个tableView,一个动态为作品界面的cell
    遇到的问题
    • 图片轮播器会受tableViewCell的复用机制影响,导致错乱(点赞按钮的状态是由服务器返回的数据决定的,这里我就不模拟了)
      解决办法:

    • 在控制器中添加一个记录图片轮播器滚动位置的数组属性imageViewCurrentLocationArray

    • 在图片轮播器里实现scrollView代理方法,监听记录最终的位移contentOffset.x,停止滚动后通过闭包/代理位置数据传递到控制器,控制器将位置数据添加到记录数组中即可,此方法应该同样适用其他因cell复用机制导致的数据显示混乱问题,(关于数组的操作,应考虑到:下拉刷新,上拉加载更多数据以及其他情况,详细代码见工程)
      至于实现哪个代理方法最为合理,应该视实际的业务需求以及界面效果而定,下面的textField代理也是如此

    // scrollView停止滚动后记录contentOffset.x
    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
        !self.imageViewDidScrolledBlock ? : self.imageViewDidScrolledBlock(scrollView.contentOffset.x);
    }
    
    • 最后图片轮播器添加一个属性接口,接收位置数据,然后在构造方法里设置图片轮播器的contentOffset即可
    - (void)setImageViewCurrentLocation:(CGFloat)imageViewCurrentLocation {
        _imageViewCurrentLocation = imageViewCurrentLocation;
        
        // 恢复显示collectionView滚动的位置
        [self.collectionView setContentOffset:CGPointMake(imageViewCurrentLocation, 0)];
    
        // 恢复显示pageLabel的下标
        if (!self.pageLabel.hidden && self.imageArray.count) {
            NSInteger currentIndex = imageViewCurrentLocation / self.collectionView.frame.size.width + 1;
            self.pageLabel.text = [NSString stringWithFormat:@"%zd/%zd", currentIndex, self.imageArray.count];
        }
    }
    

    ② 三餐

    作品.png
    布局
    • 如图,整个控制器是ViewController,将CollectionView以及上传button添加到viewController.view即可,比较简单

    • 导航栏的标题是自定义的view,然后self.navigationItem.titleView = view;即可

    这个界面的接口号称“时时死”,如果想看效果的童鞋可以自己重新抓包


    3. 功能界面

    ① 菜谱草稿(整个项目最难的界面)

    菜谱创建 - 上半部分.png 菜谱创建 - 下半部分.png
    布局

    如图所示即可,需要注意的是:因为这个界面是操作本地数据,所以要时刻根据数据得变化判断控件是否显示、如何显示

    思路
    • 因为是创建以及草稿功能的界面,所以操作的是本地数据,我写了一个菜谱草稿数据工具类,用来增删改查,非常方便
    • 照片上传
      点击弹出ActionSheet让用户选择是相机、还是相册,然后通过UIImagePickerController的代理方法- imagePickerController:didFinishPickingMediaWithInfo:选取照片即可
    • 因为顶部做法都有上传图片的需求,如果不做判断是谁需要设置图片,会导致数据显示错乱,这个在代理方法中通过判断代理的调用者即可解决图片错乱
    - (void)imagePickerController:(UIImagePickerController *)picker
    didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
        // 如果是顶部大图
        if (picker == self.headerPicker) {
            self.createRecipe.image = info[UIImagePickerControllerEditedImage];
        }
        // 如果是步骤图
        else if (picker == self.instructPicker) {
            self.instructionArray[self.setImageIndex].image = info[UIImagePickerControllerEditedImage];
        }
        [self.tableView reloadData];
        [picker dismissViewControllerAnimated:YES completion:^{
            // 选取完成后更新本地数据
            [self updateDarft];
        }];
    }
    
    草稿界面-做法.gif
    • 做法步骤
    • 点击添加步骤,往tableView对应位置插入一行步骤cell,并且在模型数据数组对应位置插入一个数据为空做法数据,如果不添加的话,点击编辑就会因为数据越界导致崩溃
    // 增加一行点击回调
    instructionFooter.addInstructionBlock = ^{
        // 添加一个空的本地数据
        [weakSelf.instructionArray addObject:[[XCFCreateInstruction alloc] init]];
        NSInteger row = weakSelf.instructionArray.count - 1;
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:1];
        // 插入cell
        [weakSelf.tableView insertRowsAtIndexPaths:@[indexPath]
                                  withRowAnimation:UITableViewRowAnimationBottom];
    };
    
    • 点击调整步骤,执行回调后tableView进入对应状态,[self.tableView setEditing:YES animated:YES];,这里需要注意的问题有:
    • tableView中只有做法这一组cell才进入编辑模式,在代理方法-tableView:canMoveRowAtIndexPath:中判断即可
    • 需要设置cell目标移动的位置,即使其他组不能进入编辑模式,但做法步骤cell还是能移动插入其中,所以需要设置:如果超过了做法步骤所在的section,不管怎么移动,最终都回到做法步骤section
    • 调整移动了cell之后,也需要同步数据中对应做法步骤的位置
    // 如果不是步骤数组,就回到对应位置
    - (NSIndexPath *)tableView:(UITableView *)tableView
    targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath
           toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath {
        NSIndexPath *finalIndexPath;
        // 如果拖动到第0组,那么松手就插入第1组第0个
        if (proposedDestinationIndexPath.section == 0) {
            finalIndexPath = [NSIndexPath indexPathForRow:0 inSection:1];
        }
        // 如果拖动到第1组,松手即插入目标位置
        else if (proposedDestinationIndexPath.section == 1) {
            finalIndexPath = proposedDestinationIndexPath;
        }
        // 如果拖动到第2组,那么松手就插入第1组最后一个
        else if (proposedDestinationIndexPath.section == 2) {
            finalIndexPath = [NSIndexPath indexPathForRow:self.instructionArray.count-1 inSection:1];
        }
        return finalIndexPath;
    }
    
    草稿界面-用料.gif
    • 用料:
      原理大致与做法步骤相同,只是用料的编辑是在一个新的控制器XCFIngredientEditController,不管有无用料,点击都进入这个控制器,那么:

    • XCFIngredientEditController中添加一个接口,接收已存在的用料数据,如果数据为空,就默认添加两个示例cell,如果不为空,就显示已存在的数据,从而达到新增、编辑的效果

    • 编辑完成或保存后,pop回创建菜谱控制器,并执行回调将编辑好的用料数据回传,刷新界面即可

    • 因为官方的效果是:只要操作了数据,即使不点击保存,也会将数据保存起来,所以我在每个数据操作后面都更新了本地数据[self updateDarft];


    ② 搜索

    搜索界面.gif 搜索界面.png
    布局
    • 进入搜索控制器时,判断本地数据中是否有已经搜索过的关键词,有则加载,没有就不显示第0组cell
    • 输入文字时,利用通知监听UITextFieldTextDidChangedNotification,然后通过闭包回传textField的文字内容给控制器,同时时刻刷新tableView即可
    • 底部流行搜索关键词是网络数据加载的,一个九宫格搞定
    思路
    • 因为没有接口,所以我封装了一个本地单例类,保存搜索过的关键词,并提供数据操作的方法
    • 搜索:在本地数据中遍历是否已存在该关键词,存在就将旧关键词删除,然后穿插新关键词到第0位;如果不存在就直接插入到第0位即可(详细代码见工程)

    ③ 上传作品

    上传作品.gif
    布局

    一个只有tableHeaderView的tableViewController搞定,官方App中图片、标签的添加会有动画,我这里没有实现,大概就是在改变控件frame值时添加动画即可

    思路
    • 本地工具类,并不需要进行数据持久化,通过回调就可以完成数据操作
    • 需要注意的是:处理好图片、标签长度的显示以及换行

    二、市集

    1. 商品

    商品界面.png
    布局
    • 上面这部分我的实现方法是:全部作为一个tableHeaderView,然后内部细分为3个部分,控制好布局就可以了
    • 需要注意的是:商品优惠(红色边框button)、店铺优惠(橙色),服务器返回的是字符串类型数据,这里我将优惠信息以button展示(也可以Label),因为数据是动态的,所以可以通过计算字符串最大宽度,然后加上既定的长度,即可设置每个button的不同大小
    商品界面.gif 商品界面-评价.gif
    思路
    • 整个控制器是UIViewController,上面商品信息展示部分是tableView,下面图文详情界面是一个UIView(UIView上面放一个类似导航的标签view,下面为CollectionView,CollectionView的cell内部添加tableView),设置好对应frame即可
    • 继续拖动,查看图文详情
      这个通过实现scrollView代理方法,判断contentOffset.y的值是否达到预定值,官方效果是:不必等到松手,达到即商品展示tableView图文详情view同时进行位移动画,同时商品展示tableView.hidden = YES;,从而达到动画切换界面的效果。由图文详情切换回商品展示也是通过代理实现
    // 向上拖动到一定程度,切换至图文详情界面
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
        // 预定值为100
        if (scrollView.contentOffset.y > self.tableView.contentSize.height - self.tableView.frame.size.height + 100) {
            // 隐藏商品信息
            self.tableView.hidden = YES;
            // 动画
            [UIView animateWithDuration:0.5 animations:^{
                self.tableView.transform = CGAffineTransformMakeTranslation(0, -(self.view.bounds.size.height-44-64));
                self.imageTextView.transform = CGAffineTransformMakeTranslation(0, -(self.view.bounds.size.height-44));
            } completion:^(BOOL finished) {
                [UILabel showStats:@"未解决webView导致的内存泄漏问题" atView:self.view];
            }];
        }
    }
    
    • 评价CollectionView手势左滑切换控制器
      也是实现scrollView代理方法,通过回调切换控制器
    • 晒图/全部评价界面
      官方效果是:两个tableView重叠在一起,点击导航栏对应按钮设置tableView.hidden

    2. 购物车

    购物车.gif
    布局
    • 整个控制器是UIViewController,self.view添加tableView跟底部的结算view
    • 同一个店铺的商品作为一组cell,店铺名为sectionHeader
    思路(我设置了清空购物车就重新加载本地数据)
    • 官方是通过服务器接收数据显示购物车内容的,因为没接口所以我就新建了一个本地单例类,保存购物车的数据,并提供数据操作方法
    • 业务逻辑
    • 给每个商品添加一个XCFCartItemState枚举类型属性,记录是否被选中,点选商品勾选button后通过回调,在控制器更改本地数据。
    • 店铺勾选状态以及全选状态,通过遍历本地数据中对应店铺内商品所有商品是否全部选中,再通过点选商品回调刷新界面就可以达到实时更改状态的效果了
    • 商品购买数量
      在cell内部实现textField代理方法-textFieldShouldEndEditing:监听购买数量,编辑完成就通过闭包传递购买数量给控制器,控制器更改本地数据并刷新界面
        cell.itemNumberChangeBlock = ^(NSUInteger number) { // 修改商品个数回调
            // 拿到最新的数据,再修改数量
            // 如果不拿到最新数据,在编辑商品数量时点击店铺全选 会导致正在编辑的商品无法同步选中状态的bug
            NSArray *newShopArray = [XCFCartItemTool totalItems][indexPath.section];
            XCFCartItem *newItem = newShopArray[indexPath.row];
            // 修改数据中商品个数的值
            newItem.number = number;
            // 更新本地数据
            [XCFCartItemTool updateItemAtIndexPath:indexPath withItem:newItem];
            // 刷新界面
            [weakSelf.tableView reloadData];
        };
    
    • 切换编辑/删除模式:
      简单的闭包回调刷新界面即可,删除的话,根据商品状态是否点选,点选就删除,因为点选状态跟编辑模式是通用的,所以不需要另外计算

    3. 确认订单

    订单 - 上半部分.png 订单 - 下半部分.png
    布局

    如图,需要注意的是:如果计算结果店铺优惠价格为0,就会隐藏店铺优惠

    思路
    • 直接拿到购物车界面勾选好的的商品,通过计算显示对应价格数字就可以了(运费只添加一次)
    • 选择收货地址,访问本地存储的收货地址数据,有则显示,没有则提示添加
    • 因为没钱没见过订单优惠长啥样,所以就没做了,不过应该就是tableView就可以搞定的

    三、社区

    社区.gif
    思路
    • 点选一个评论cell,就获取该评论作者的昵称,赋值到底部的编辑框,然后在编辑控件内判断有无该字符串,有就删除,无则添加
    • 当编辑框内,只要最后一个字符串“@”,就显示用户tableView:利用通知监听UITextViewTextDidChangeNotification然后回调传递最后一个字符串到控制器,控制器判断显示

    四、我

    (因为我什么数据也没有,就没做那些详细界面了)

    1. 个人资料

    个人界面.gif

    2. 收货地址

    收货地址.gif
    思路

    本地数据工具类,修改内容闭包回调控制器更改数据,内部处理好逻辑关系就可以了


    五、动画

    ① 图片展示

    商品界面-图片展示动画.gif
    思路

    界面是一个UIViewController,提供接口接收数据,view中添加一个图片轮播器,present出现后执行动画(这里我只是实现了效果,详细控件分布就不做那么仔细了)
    大概步骤:

    1. 点击图片
    2. 闭包回调传递 图片在当前窗口的frame值图片所在数组的下标给控制器
    3. 控制器将数值传递给图片展示控制器,并present
    4. 图片展示控制器接收图片数据赋值给图片轮播器,然后创建一个imageView(作动画用),frame设置为从上一个界面接收到的数值,然后imageView执行形变动画
    5. 动画执行完毕移除imageView,显示图片轮播器
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        
        // 关闭按钮
        UIButton *dismissButton = [[UIButton alloc] initWithFrame:CGRectMake(15, 15, 30, 30)];
        [dismissButton setImage:[UIImage imageNamed:@"closeLandscape"] forState:UIControlStateNormal];
        [dismissButton addTarget:self action:@selector(close) forControlEvents:UIControlEventTouchUpInside];
        dismissButton.alpha = 0;
        [self.view addSubview:dismissButton];
        
        // 图片轮播器
        CGRect displayRect = CGRectMake(0, XCFScreenHeight*0.5-175, XCFScreenWidth, 350);
        XCFImageShowView *showView = [[XCFImageShowView alloc] initWithFrame:displayRect];
        // 设置属性
        showView.type = XCFShowViewTypeDetail;
        showView.imageArray = self.imageArray;
        showView.currentIndex = self.imageIndex;
        showView.imageViewDidScrolledBlock = self.imageViewDidScrolledBlock;
        // 默认先隐藏
        showView.hidden = YES;
        [self.view addSubview:showView];
        
        // 临时添加一个imageView 作动画
        CGRect rect = [self.rectValue CGRectValue];
        UIImageView *imageView = [[UIImageView alloc] initWithFrame:rect];
        XCFReviewPhoto *photo = self.imageArray[self.imageIndex];
        [imageView sd_setImageWithURL:[NSURL URLWithString:photo.url]];
        [self.view addSubview:imageView];
        
        // 动画
        [UIView animateWithDuration:0.3 animations:^{
            imageView.frame = displayRect;
        } completion:^(BOOL finished) {
            // 移除动画的imageView
            [imageView removeFromSuperview];
            // 显示图片轮播器
            showView.hidden = NO;
            [UIView animateWithDuration:0.3 animations:^{
                dismissButton.alpha = 1;
            }];
        }];
    }
    

    ② 添加商品到购物车

    商品分类选择.gif 商品分类view.png
    布局
    • 可以通过添加一个window,来实现这个需求,也可以用直接在根窗口[UIApplication sharedApplication].keyWindow上添加一个自定义的UIView,我这里用的是后者。
    • 自定义一个半透明的UIView,按照官方的动画效果,我这里用一个tableView,只设置了tableHeaderView(因为懒所以没有使用UIScrollView),tableHeaderView的内容又是一个自定义UIView,控件摆放不多说了很简单,不过需要注意的是:
      • 控件之间的逻辑关系
      • 商品种类button的标题由服务器数据决定,所以整个tableView的frame并不是固定的
      • 因为没找到可以直接在计数器中间添加一个view的接口方法,所以我自定义了一个计数器XCFStepper,很简单的一个小控件,处理好逻辑关系就可以了
    思路
    • 在购物车本地数据工具类XCFCartItemTool刷新数据方法中(不管是删除还是新增,都会刷新数据)发送自定义的通知,并传递商品数量totalNumber
        // 添加完成后发送通知,用处:购物车图标动画
        [[NSNotificationCenter defaultCenter] postNotificationName:XCFCartItemTotalNumberDidChangedNotification
                                                            object:nil
                                                          userInfo:@{@"goodsCount" : @([self totalNumber])}];
    
    • 购物车图标为自定义的UIView,[[UIBarButtonItem alloc] initWithCustomView:cartIcon];然后添加到导航栏,在view内部接收XCFCartItemTotalNumberDidChangedNotification通知后作核心动画就能实现效果了
    - (void)awakeFromNib {
        // 监听“添加商品到购物车”的通知
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(cartItemTotalNumberDidChanged:)
                                                     name:XCFCartItemTotalNumberDidChangedNotification
                                                   object:nil];
        NSUInteger count = [XCFCartItemTool totalNumber];
        if (count) { // 有商品才显示数量标签
            self.countButton.hidden = NO;
            [self.countButton setTitle:[NSString stringWithFormat:@"%zd", count]
                              forState:UIControlStateNormal];
        }
    }
    
    - (void)cartItemTotalNumberDidChanged:(NSNotification *)note {
        self.countButton.hidden = NO;
        NSDictionary *dict = note.userInfo;
        // 取得商品数量
        NSUInteger count = [dict[@"goodsCount"] integerValue];
        // 延时作动画
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [UIView animateWithDuration:0.5 animations:^{
                // 购物车图标执行放大动画
                self.countButton.transform = CGAffineTransformMakeScale(1.5, 1.5);
            } completion:^(BOOL finished) {
                // 改变显示的商品数
                [self.countButton setTitle:[NSString stringWithFormat:@"%zd", count]
                                  forState:UIControlStateNormal];
                // 还原图标大小
                [UIView animateWithDuration:0.5 animations:^{
                    self.countButton.transform = CGAffineTransformMakeScale(1, 1);
                }];
            }];
        });
    }
    
    • 这里我模拟了一下添加效果:从本地数据中随机取出一个商品
      • 如果该商品只有一个类型,直接添加到购物车
      • 如果该商品有多种类型(如:300g、450g、600g),就以碰撞方式的动画(我用的是Facebookpop框架)弹出商品类别选择tableView,同时后面的商品信息view进行缩放,设置要购买的类别和数量后,商品图片执行核心动画,然后移除类别选择view,执行添加到购物车动画
      • 这个界面也比较简单,只是要注意各种小细节,动画的顺序,以及相应的添加到购物车(或立即购买)业务逻辑
            // 随机添加一样商品
            XCFCartItem *randomItem = [XCFCartItemTool randomItem];
            XCFGoods *randomGoods = randomItem.goods;
            // 加入购物车
            if (type == BottomViewClickedAddToShoppingCart) {
                // 如果该商品有多种类型,就弹窗让用户选择具体购买哪种类型
                if (randomGoods.kinds.count > 1) {
                    UIWindow *window = [UIApplication sharedApplication].keyWindow;
                    // 缩小当前界面
                    [UIView animateWithDuration:0.3 animations:^{
                        window.rootViewController.view.transform = CGAffineTransformMakeScale(0.9, 0.9);
                    }];
                    
                    // 显示商品分类view
                    XCFKindsCategoryView *kindsView = [[XCFKindsCategoryView alloc] initWithFrame:window.bounds];
                    // 分类view的弹出类型(购物车)
                    kindsView.type = XCFKindsViewTypeCart;
                    kindsView.item = randomItem;
                    [window addSubview:kindsView];
                    // 确认购买回调
                    kindsView.confirmBlock = ^(XCFCartItem *item) {
                        // 本地购物车数据添加商品
                        [XCFCartItemTool addItem:item];
                        [UILabel showStats:[NSString stringWithFormat:@"添加:\n%@", item.kind_name] atView:weakSelf.view];
                    };
                    // 取消回调
                    kindsView.cancelBlock = ^{
                        // 恢复界面大小
                        [UIView animateWithDuration:0.3 animations:^{
                            window.rootViewController.view.transform = CGAffineTransformMakeScale(1, 1);
                        }];
                    };
                    
                } else { // 如果只有一个商品,直接加入购物车
                    [XCFCartItemTool addItemRandomly:^(NSString *goodsName) {
                        [UILabel showStats:[NSString stringWithFormat:@"随机添加:\n%@", goodsName] atView:weakSelf.view];
                    }];
                }
            }
    

    最后想说的话

    • 关于项目
    • 自动布局是按照iPhone6s的,其他机型可能会有偏差,见谅
    • 能实现的基本都实现了
      • 市集模块接口被加密,所以做不了,不过目测是简单的CollectionView+一些动画就能搞定的
      • 菜篮子界面应该是我的实现思路有问题所以没完成,后续如果有空的话会尝试实现
      • 一些很简单的、相同效果的界面我实在不想写了!也没做,有兴趣的童鞋可以自己尝试实现
    • 写代码过程中跑去骚扰了ManoBoo小神,在这里超级感谢ManoBoo!还帮我解决了两个bug!还有感谢维尼的小熊大神的开源App,两位前辈对界面实现思路的讲解让我受益匪浅。感谢一切开源。

    Github代码下载地址

    高仿下厨房App 开源咯~

    • 如果你是新手,并且我的项目对你有帮助的话,请大方的给我Star!开源的世界如此美好互帮互助感激不尽!如果你不给我Star,也不强求啦...

    • 如果你是大神...

    END

    相关文章

      网友评论

      • _Dam0n:请问pod是用了什么版本,我存在
        ld: library not found for -lAFNetworking
        clang: error: linker command failed with exit code 1 (use -v to see invocation)
        这个问题
      • 608e3b5340c2:看了下代码,我真的不感觉这算是新手。。。。这真的是新手水平么,我怎么感觉我们这新手都写不出这个。再次来膜拜。
        _Sven:@张云甫Chris 有些实现思路还行,代码现在自己看来还是很多问题的哈哈,互相学习。
      • 608e3b5340c2:膜拜大神啊,我打算自己也模仿着写一个看看!感谢你这个指导,有问题的话问你
      • 910b35f481df:大神在不?我下载下来运行不起啊?崩溃了
        608e3b5340c2:求如何get到新的接口
        _Sven:@ZDDarker 下厨房官方接口做了调整 我也没有时间去维护 现在会闪退 借鉴下思路就好~
      • PGOne爱吃饺子:作者购物车的那个动画是如何做出来的,可以给我们这些渣渣一个详细的教程么,谢谢了
        910b35f481df:我下载下来为什么运行不起?崩溃
        PGOne爱吃饺子:@callmeJoeJoe 好滴 谢谢
        _Sven:@4140d18ee6fc 动画就是用核心动画实现的呀,代码里面写得很清楚,可以去看一下
      • 李大戮:挺6:+1:
      • ____白夜:你好,我想问下,为什么会抛出 Assertion failure in -[MASLayoutConstraint _setSymbolicConstant:constant:] 这个异常的,好像前几天还没问题的。
        _Sven:@____白夜 好的 感谢理解:smile:
        ____白夜:@callmeJoeJoe 谢谢回复 应该是接口的问题,我自己试试能不能搞定。:grin:
        _Sven:@____白夜 :disappointed_relieved: 不好意思 这个布局异常我也不太清楚 好久没维护这个项目了
      • c7323fb46eb2:你好大神 现在这个项目运行起来有个bug 运行不起来
        _Sven:@指尖的舞者 很有可能是 接口是一年前的了 下厨房那边应该改了 我也没有跟进 不好意思啦 自己可以尝试抓取
        c7323fb46eb2:@callmeJoeJoe 谢谢回复 断点定位的是返回的是空数组 是不是数据没有请求到
        _Sven:@指尖的舞者 你好 下载下来最好pod update一下
      • 红色小星:挺好 学习了
      • wjc21wjc:又回来看看楼主的帖子,我自己的项目也已经提交审核通过了,一起加油哈哈 :smile:
        _Sven:@wjc21wjc :clap:厉害呀 加油
      • 简晰333:自学好久了,最近想练练手。楼主能把抓的接口,分享一下吗,万分感谢liunan_615@sina.com
      • 会飞的代码:说错了是培训?
      • 会飞的代码:你参加过黑马的IOS培育吗?
        _Sven:@李莎鑫 嗯
        会飞的代码:@callmeJoeJoe IOS找到工作了吗
        _Sven:@李莎鑫 不是 不过是通过培训视频自学的
      • 01d0ac7a6be8:楼主,首页的接口的参数怎么弄,我只能抓到某一天的数据~~~求帮助
        01d0ac7a6be8:@callmeJoeJoe 嗯嗯。能不能给您的抓的接口参考一下。因为有些参数看不明白。例如GET http://api.xiachufang.com/v2/recipes/show_v2.json?_ts=1477300417.338892&api_key=07397197043fafe11ce5c65c10febf84&api_sign=10be267ad4c1316efad6e7bcb368a8fe&...这个接口有个“_ts”的参数,后面的key没看懂 :joy:
        _Sven:@葉zi_kb 首页有很多个接口的 不知道现在改了没有
      • 述己:写的不错,值得学习,准备用swift把下厨房写一遍,,说的挺有用的
        _Sven:@述己 谢谢,只是提供了思路。:smile:
      • flowerflower:你好,方便认识一下吗,觉得你写的不错
        _Sven:@flowerflower 好的 QQ719609160
      • S型身材的猪:求源代码,在这里写得再好也没有源代码好
      • LibraCoder:您好,之前我打开项目可以正常运行,但是我现在打开项目,提示我有两处错误
        错误一:"_OBJC_CLASS_$_XCFCartItemCell", referenced from:
        objc-class-ref in XCFCartViewController.o
        objc-class-ref in XCFOrderViewController.o
        ld: symbol(s) not found for architecture x86_64
        错误二:
        clang: error: linker command failed with exit code 1 (use -v to see invocation)
        请问这个如何解决
        _Sven:@索罗门 :smile:glad to hear that
        LibraCoder:@callmeJoeJoe  解决了,compile sources里面缺少了XCFCartItemCell的文件,非常感谢楼主的帮助以及提供项目的实现思路 :blush:
        _Sven:@索罗门 错误一参照http://stackoverflow.com/questions/18408531/xcode-build-failure-undefined-symbols-for-architecture-x86-64 这个试试,我这边还是可以正常运行的
        错误二我也不太清楚具体是什么错了
      • 阿兹尔:屌爆了!!! :smile: :smile: :smile:
      • 峰子1994:楼主有源码了吗?可以发一份代码学习下吗
        峰子1994:@callmeJoeJoe 你好,下载不了他说网页不存在了可以发我一份吗,1299346106@qq.com谢谢
        _Sven:@峰子1994 文章底部有百度云下载地址
      • queuey:厉害厉害
      • 张智杰:写的很不错呀,有两点提一下:
        1.因为重用机制造成的轮播器数据混乱问题,其实可以通过重写cell里的prepareForReuse这方法解决
        2.少用闭包,多用代理
        _Sven:@张智杰 :flushed:有道理,但是代理多了也挺分散的
        张智杰:闭包不利封装,难管理,使用不慎还有内存风险
        _Sven:@张智杰 感谢建议!为啥要少用闭包呢
      • 玖零儛: :wink: star奉上,点进项目发现取消分享了,不过还是get了,回过来一看居然还有个传送门,楼主还怕我们迷路2333
        _Sven:@玖零儛 :smile:其实在GitHub还是能下载的
      • e3c8cd9f2864:能提供一下抓的接口吗????
        e3c8cd9f2864:@callmeJoeJoe 多谢多谢~~~13061650152@163.com
        _Sven:好的,请留邮箱。
      • 乂滥好人:补充下,绝不是拿去忽悠用人单位,纯属借鉴,
      • 乂滥好人:楼主来份源码借鉴下 cc_f423405@163.com,3Q
      • Rickie_Lambert:千辛万苦终于找到了 Demo源码, 运行想看看你搜索功能怎么实现的,结果Demo里你没有完全去实现搜索功能,还是说Demo里你已经把接口什么的都去除了,所以搜索功能没有搜索结果,不过还是谢谢楼主,对我还是很有帮助, 我去研究你的源码了!!给赞👍
        _Sven:@Rickie_Lambert 是没有接口 只是做了个效果而已 如果有接口应该也差不多一样的实现方式
      • super_lian:楼主厉害,源码好像没了,能否发个接口文档,superlian@163.com,最近想练练手,抓接口好烦
      • jumping鹏:想问,楼主找到工作了吗
        _Sven:@jumping鹏 还没
      • 0491e8098cad:搜索那里是不是有问题呢??
        _Sven:@SuperBong 代码忘了怎么写的了 如果有问题的地方你可以尝试改一下 那是我刚开始练习时写的代码
        0491e8098cad:@callmeJoeJoe 有没有感觉push了两次
        _Sven:@SuperBong 什么问题?
      • 只敲代码不偷桃:楼主,找到工作了吗?最近想换工作,但简历犹如泥入大海,正在犹豫是不是也要吹牛放炮的夸大简历了
        _Sven:@令狐大哥 没有 没怎么投简历 等待朋友内推的公司招人~ 广州的公司已经被培训机构的刷了几百遍了:sweat:
      • Top_熊:我靠,可以可以...
      • 大梦想家:现在取消开源了,楼主能否发一份源码给我呢?纯学习目的
        _Sven:@大梦想家 :smiley:不客气 看博客切记要仔细看
        大梦想家: @callmeJoeJoe 刚刚没注意看😂 谢谢楼主
        _Sven:@大梦想家 文章底部有下载地址
      • 142a9cf08006:首先,灰常感谢楼主的分享 :sunflower:
        之前刚好看到楼主的分享,给我这个自学的初学者一个很好学习方向 :bulb:
        因为当前还在工作的原因,断续续的一边学习楼主的代码,一边巩固相关知识点,还好项目思路很明确,我目前刚好完成主页的编写;
        后续打算再用一个月左右时间完成这个项目,也差不多在这个月结束当前的这份工作 :checkered_flag:
        By the way,下份工作我也打算去广州或深圳看看,希望楼主可以对找工作给点suggestion :kissing_heart: 如果楼主有空,烦请简信 OR E-Mail:741066288@qq.com :kissing_heart:
        最后,还是感谢楼主的分享!嘿嘿!看后面工作能不能碰上楼主 :stuck_out_tongue_winking_eye:
        _Sven:@Wingoc 之前写完下厨房去找了一下 大多数都不要实习 还有就是被人坑了 现在在继续学习吧 月底看能不能去上班 如果你要来找工作 建议夸大简历
        142a9cf08006:@callmeJoeJoe 没事的,我也是参考一下,遇到有些的知识点我自己还要补充学习呢 :stuck_out_tongue_winking_eye:
        冒昧了解一下,楼主怎么还没开始工作呢?是因为太难在找的过程中麽?还是在继续深入学习中呢?抑或是在留意别的? :scream: :scream:
        _Sven:@Wingoc 代码别学呀!特别是首页!有些错误写法,思路借鉴下就好了,找工作的话我目前也还没工作,太难找了,所以也能不能给你一些实质性的意见了:smile:
      • iYeso:你的代码Masonry约束有冲突啊! 怎么没有解决啊
        _Sven:@callmeJoeJoe 首页吗?我的第一个界面,没有优化哈哈见谅,我真机跑起来不怎么卡
        _Sven:@greedyDoor 不是没有解决。。那是xib的约束,为了方便别人查看就没去弄,你弄了的话label控件会看不见
        iYeso:没有做优化吗?? 为什么感觉有点卡顿啊
      • iYeso:感觉非常好 但是没有代码 没法学习啊
        _Sven:@greedyDoor 有百度云下载地址 自己在文章底部 不过代码很挫 看下实现思路就好
      • 黄花菜先生:hello,亲,我这边用Charles抓数据包,response和request都是乱码,知道啥原因不
        _Sven:@不会凉的黄花蔡 有些接口是这样的 加密了
      • 玉思盈蝶:群主..能给我源码学习学习么???谢谢 1299625033@qq.com
      • b7bdd01cd407:写的很好 给星了 ,
      • ae2988896ef9:谢谢LZ,我觉得写得挺不错的,也帮了我一大忙,正好有个ShoppingCart功能需要借鉴参考一下,特意注册账号来顶你。加油。 :pensive:
        _Sven:@sjjujjwffg 感谢支持~代码挺简单的,你可以多优化一下~
      • Danis:您好可以给份源码吗?学习用。
        Danis:@callmeJoeJoe github你已经删除了了,可以私聊发份吗?不胜感激啊
        Danis:@callmeJoeJoe 大神已经关注您了,可不可留个联系方式呀。
        _Sven:@MR南哥 文章底部有
      • b64b9c7b56d3:为什么打开项目报了一堆错误?:flushed:
        _Sven:@SeanJackson 什么错误?百度Google之~
      • 4b45502e46c4:你好,我们最近要开发移动项目 我也要从c++转向移动测,看你做的效果非常好,想借鉴你的代码学习学习,方便的话请发给我一份 cc11235@126.com 谢谢~
      • hello_kity:方便把接口发一份吗??、2306691335@qq.com万分感谢~~
      • linwkr:图片资源文件是怎么获取的呢。。
        我从itunes上下载了ipa,发现里面图片资源不全
        _Sven:@linwkr 我写的时候也是不全的,应该是厨房那边又做了处理,我直接从另一位前辈的项目里拷贝过来的:smiley:
      • 向钱冲啊:加油!
      • 凯文Kevin21:楼主, 你这个仿APP项目里面, 购物车,发表社区评论很多我都没做过, 可以发源码给我看一下吗.
        260595314@qq.com [真的,没别的意思.就是想参考学习一下.]
      • 凯文Kevin21:很不错,顶!!!感觉比我们公司哪些招进来2年工作经验的都要强,
      • 凯文Kevin21:楼主,可以发一份源码给我吗?是这样的,我最近周末没上班,没事干也在做一个和你这个类似的App,想借鉴一下你的代码.一些解决思路.可以吗?? 放心,我不会向别人泄露你的代码成果.仅自己参考用. 260595314@qq.com
      • ca02f96a7517:能给份源码嘛?462663315@qq.com,感激不尽。
        ca02f96a7517:@callmeJoeJoe 太邪恶了你, :smile: 说笑的啊。
        _Sven:@ca02f96a7517 文章底部有秘密入口~
      • Pr_Chen:话说下厨房的接口都是带加密规则的,你是怎么破的?
        _Sven:@Pr_Chen 。。没有破啊,有些接口没加密
      • K_Gopher:如果是新手的话,能做到这样已经很厉害了! :heart:
        _Sven:@SKHuang 没 都不要新手
        K_Gopher:@callmeJoeJoe 现在找到工作了没有?
        _Sven:@SKHuang 感谢支持~还有很多不会需要继续学习:smiley:
      • Yutaolin:你好想问一下商品详情上拉查看图文消息时,什么时候加载数据
        Yutaolin:@callmeJoeJoe ok 谢谢
        _Sven:@Yutaolin 所有内容都是在同一个界面的,所以数据也是全部一起加载的,我抓包的时候发现也是这样
      • cbae205f1527:能给一份源码看看嘛?
        _Sven:@1019yt 从后往前看有百度云下载地址
      • 巴糖:收藏中
      • 啊左:哈,好吧。最近确实听很多同行说找工作难,学历这个不重要,非科班的话就需要一些其他出彩的地方弥补了。
        加油!~ :heavy_check_mark:
        _Sven:@啊左 感谢前辈:smiley:
      • 啊左:加油,建议把博客网址放在简历上面。还有很多求职的app,你可以下载一些跟互联网boss进行交流。 记得在个人介绍上面放博客网址。
        会是亮点。
        _Sven:@啊左 没学历非科班,大公司不要,小公司只看工作经验,只能夸大简历啦!
        啊左:@callmeJoeJoe 这个不用担心,公司看重的还是实际的开发能力,而不是具体的工作年限的,哈。
        _Sven:@啊左 放上博客不就暴露了是新手吗!然后人家又不要新手哈哈哈:joy:
      • a329f2baf183:请问一下楼主,你这些生成的GIF图片用了啥工具啊? :smile:
        _Sven:@zj0517 没有。。继续学习中
        a329f2baf183:@callmeJoeJoe 好的,收到,谢啦,请问楼主现在找到工作了吗?
        _Sven:licecap~
      • 荷洛图:作为一个已经工作半年的人来说,楼主水平比我强多了,我这样的都能找到工作博主好好加油吧。多面试就行。
        _Sven:@荷洛图 :disappointed_relieved:之前的面试都是以新手身份,人家都不要,无奈接下来只能包装简历了
      • 夜的v:不错啊,技能挺全面的。好多技能我都还没用过,惭愧啊。找工作方面可以投简历的时候小包装一下,面试的时候可以尝试实话实说。很多公司看中的是学习能力,你缺少的是和面试官面对面的机会,找实习工作基本没人看你简历的。万事开头难,加油。
        _Sven:@夜v惨淡 感谢前辈,已经面试了几次,都是不要新手的,只能包装了:sweat:
      • zedxpp:能写到这个地步 确实很厉害了. 你的详解我都看了. 抽空看看你的源码. :grin:
        _Sven:@粉红色的星空 代码可能不太规范:sweat:
      • 南坞觉:请教楼主,你的app架构是怎么搭建的?
        _Sven:@南坞觉 个人感觉不会麻烦啊,因为我是新手可能不能给你实质性的建议,不好意思。
        南坞觉:@callmeJoeJoe 我觉得自己定义一个navogationbar挺麻烦的,楼主有没有什么经验?
        _Sven:@南坞觉 仿写的,按照它的架构来的:fearful:
      • wg689:不错
      • bigyuan:其实界面写这样地步,已经挺好的了
        _Sven:@中琦 :smile: 对,正有此意,感谢建议~
        bigyuan:@callmeJoeJoe 建议先学习数据库和网络线程相关。面先广再精更好找工作。
        _Sven:@中琦 感谢支持,只是练习了界面搭建,接下来是学习代码规范以及性能之类的了。
      • 吃蘑菇De大灰狼:我觉得可以把项目的结构和model的设计总结一下,画画图,效果会好一点。支持~
        _Sven:@MichaelMao 我的不是Mac系统 心都碎了
        吃蘑菇De大灰狼:keynote,画画类图
        _Sven:@MichaelMao 当时做的时候没有能画图的软件:disappointed_relieved:
      • smirkk:楼主刚学习iOS不久,想找个项目看看源码,可不可以发份源码呢?看你的这个真心不错啊!谢谢!
        _Sven:@风中的温柔 还有。。。
        smirkk:@callmeJoeJoe 给星星⭐?
        _Sven:@风中的温柔 从后往前看有秘密入口
      • 志城:不开源的理由有些牵强。真的,你不必在意这么多,凡事往好的方面想。
        _Sven:@志城 不是牵强,而是还有不能说的秘密啊,下厨房方面为了他们的服务器数据安全,让我关闭开源,这也是没办法的事。
      • SaxonRen:如果你真的是新手的话,你的自学能力真的很强 :clap:
        _Sven:@SaxonRen 然而并没人要新手~所以接下来准备捏造简历了
        SaxonRen:@callmeJoeJoe 以你这水平,工作应该好找的吧
        _Sven:@SaxonRen :joy: 兴趣是驱使学习的最好动力啊 然而也找不到工作
      • 8ae158dda3f2:纳尼,去了github,结果。。。
        03a65f7ecd8c:@CoderNevinZ 秘密入口是那个传送门吗? :joy:
        8ae158dda3f2:@callmeJoeJoe :blush:
        _Sven:@CoderNevinZ 从下往上看有个秘密入口 :flushed:
      • cf64afa2f37a:你好我运行项目报incompatible block poiner types assigning to 'void(^)(CGFloat)' from 'void(^__strong)()'这个错是什么问题
        _Sven:@Johnson_Xu 在self.showView.imageViewDidScrolledBlock = imageViewDidScrolledBlock;这里,最后加个()试试
        cf64afa2f37a:@callmeJoeJoe 试过了还是报那个错,是那个block那里出错,出错点: cell.imageViewDidScrolledBlock = ^(CGFloat finalContentOffsetX) {
        // 拿到最后的位置保存到数组中
        weakSelf.imageViewCurrentLocationArray[indexPath.row] = @(finalContentOffsetX);
        };
        还有
        - (void)setImageViewDidScrolledBlock:(void (^)())imageViewDidScrolledBlock {
        _imageViewDidScrolledBlock = imageViewDidScrolledBlock;
        self.showView.imageViewDidScrolledBlock = imageViewDidScrolledBlock;
        } 这句self.showView.imageViewDidScrolledBlock = imageViewDidScrolledBlock;
        因为我对block还不太熟,不知道是类型不匹配还是
        _Sven:@Johnson_Xu 不太清楚哈,你可以pod install试一下
      • lbjhzj:已经工作几个月了,一直犯懒,不愿动手仿写个APP。。。作者的劲头很足啊!
        _Sven:@lbjhzj 嗯,现在开始夸大简历
        lbjhzj:@callmeJoeJoe 不要着急,慢慢找
        _Sven:@lbjhzj 然而还是找不到工作
      • e2030bd1a642:很喜欢
        _Sven:@逍遥小李 这个评论我喜欢
      • vbers:写得非常棒
        _Sven:@vbers 谢谢:grin:
      • bde6cd4cb050:路过,五体投地中,膜拜Ing,同广州,能否发份完整代码给我学习学习,我目前还在学习IOS中,邮箱:501821960@qq.com,非常非常感谢!!!
        _Sven:@小年同学 你从下往上看就能看见了
        bde6cd4cb050:@callmeJoeJoe 什么意思?哪里还有隐藏的入口可以拿到代码???
        _Sven:@小年同学 有隐藏入口~
      • 路人张某某:光楼主的实践精神也值得我学习了!加油,天高任鸟飞
        _Sven:@路人张某某 :smile:感谢支持
      • 桐生一猫:做的不错,加油,加油💪
        _Sven:@桐生一猫 感谢支持!
      • LaiYoung_:lz方便来个联系方式交流么交流么?:heart_eyes:
        _Sven:@LaiYoung :hushed:交流啥呢
      • e6f6d7b458be:star献上了,哈哈,只不过取消开源没啥效果啊,代码还是完整get到了,给楼主赞一个
        _Sven:@蛋蛋cc :smiley:
      • 65f20d8e1459:给了一个最牛逼的star, :joy: :joy:
        _Sven:@newbieDev 哈哈我的表情包战斗力十足
        65f20d8e1459:@callmeJoeJoe 一个没有代码的star:joy:
        _Sven:@newbieDev :unamused: star还有分级
      • 逸小枫:楼主 ,我真的很想学习一下,也很想仿写一个APP ,您方便把源码发我一下吧 ,我现在已经在一所公办公司实习,不会用它来干嘛的,真的只想学习,谢谢您了.邮箱地址:277735871@qq.com
      • Durian_Wang:工作小一年 看到你写的感觉很惭愧啊 发一份学习一下。505580678@qq.com
      • ocarol:我不知道为什么好多人伤心你的代码看不了。。我觉得稍微懂得git的都能获取你完整项目的代码呀 :flushed:
        ocarol:@callmeJoeJoe 我刚刚就clone下来,看了源代码:flushed:
        ocarol:@callmeJoeJoe 明明可以切换到任意提交时间点的......
        _Sven:@ocarol 那是因为git以后并没有工程文件哈哈
      • 6cc787a7dc9c:lz我想问下你仿写下厨房总共花了多少时间 我也正在仿写中...
        6cc787a7dc9c:@callmeJoeJoe 厉害 学习
        _Sven:@kolaker 1个月啊,每天起床写到睡觉吧!
      • 443718761557:厚颜无耻的求一份源码,正在开发发布动态那种功能 :relaxed: ,邮箱15628418654@163.com,多谢啦!楼主。
        443718761557:@callmeJoeJoe 主要就是草稿保存到本地,有些地方不理解T_T
        _Sven:@不愿透露姓名的白先生 动态只是模拟呀 只有本地数据,没有将数据打包发送服务器的!
      • superLee:思路很清晰, 继续加油.
        不过我还是比较喜欢纯代码, 虽然比较费时间...但是方便维护和查看.
        _Sven:@Imlc 对呀 可是因为是个人练习,所以就用xib节省时间:smile:
      • just_zzs:厉害,在下已工作半年,惭愧,可以给我一份源码吗,想学习一下。
      • dcc7e0b17c02:非常优秀.你的autolayout是拖线还是VFL或者第三方框架?
        _Sven:@cescxu 拖线跟masonry
      • 981993ac2c63:求源码... 现在已经工作了 独立开发APP 感觉有点压力 希望能借鉴一下你的源码 学习一下 不胜感激 514907181@qq.com
      • HenryCheng:小伙子加油
        HenryCheng:@callmeJoeJoe :smile::smile::smile:加油
        _Sven:@HenryCheng 谢谢前辈!
      • Joy___:写的不错
      • 叶孤城___:写的挺不错的。值得鼓励。
        65f20d8e1459:彦祖这里挤一挤
        d43b0bb8a1b1:@叶孤城___ 活捉彦祖!
        _Sven:@叶孤城___ :flushed:哇感谢大前辈!继续向前辈学习~
      • 一粒吗:代码github上下载不了了 伤心啊 5555
        一粒吗:@一粒吗 嗯最近拿objectc写了个商城系统 已经学会了
        _Sven:可以学习思路啊!有了思路代码不是问题
      • doubleJJ:楼主是应届生吗?
        _Sven:@doubleJJ 是的~
      • 一一抛到银河:兄台不错, 可惜了你的star 哈哈哈哈
      • 李传格:杭州感兴趣不?
        _Sven:@jaco_li :flushed:如果广州一直找不到,会考虑杭州的,感谢前辈~
      • __西门吹雪:哈哈。可以可以。你这半年可比我那是半年要牛呀。👍
        _Sven:@__西门吹雪 过奖啦,还有很多需要学习的地方。
      • 只敲代码不偷桃:确实不错,本来想下载下来好好研究一下,才发现你昨天已经不得已取消开源了,不过还是要支持一下,开源让世界更美好
        _Sven:@梁志远 好的,不过代码不太规范,主要是借鉴思路就好!
        只敲代码不偷桃: @callmeJoeJoe 方不方便给我发一份源码学习下(本人已入职,在北京,不用吹牛放炮的去造简历😄)
        _Sven:@梁志远 :pray: 感谢支持和理解啊哈哈!
      • 83bab6ae0610:写的相当不错,代码极其规范。我之前写的XCFApp的代码 https://github.com/STShenZhaoliang/XCFAPP 如果想要Reveal 下厨房的界面,我可以发给你。好公司就缺优秀的程序员。看好你。
        _Sven:@沈天 原来是前辈呀!我的图标素材好像就是从您那拿的:smile:,非常感谢!前辈过奖了,因为是新手所以很多地方不足,需要继续学习。

      本文标题:iOS高仿下厨房(Objective-C)

      本文链接:https://www.haomeiwen.com/subject/saqkrttx.html