iOS高仿下厨房(Objective-C)

467 查看

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

一个月前在简书上看到一篇ManoBoo小神发表的Swift-高仿半糖App,然后又顺藤摸瓜看到了维尼的小熊大神发表的iOS高仿爱鲜蜂,第一想法就是:太棒了!讲解了一些常见界面、效果的实现思路,作为正在自学iOS的我顿感崇拜,同时也在想:我能不能也写出一个App来?于是带着恐惧(这么难我应该写不出来吧…)我打开了Charles,抓了下厨房的数据,就这样八年的抗战 就这样一个月的仿写之旅开始了。

为什么是下厨房?

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

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

  • 开发环境:Xcode 7.2,语言:Objective-C
  • 用到的工具:Charles抓包工具
  • 仿写程度:耗时一个月,每天12h+,能实现的基本都实现了
  • 刚开始写的时候控件是用纯代码写的,后来发现太耗时间就改用Xib了
  • 因为此前没看过谁的代码(ManoBoo维尼的小熊两位前辈的都是swift…我还没学swift就没看了),所以代码如果有不合理的地方(如:命名),请见谅
  • 这个开源项目适合新手,基本的界面布局以及业务逻辑都有,看完这个基本也会写简单的App啦,但是因为我也是新手,代码很多不规范,所以学习实现思路就好,代码请不要借鉴。
  • 非科班自学,请大神勿喷。

效果预览

一、首页

首页.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. 导航

① 关注动态

 

布局
  • 整个界面就一个tableView,一个动态为作品界面的cell
遇到的问题
  • 图片轮播器会受tableViewCell的复用机制影响,导致错乱(点赞按钮的状态是由服务器返回的数据决定的,这里我就不模拟了)
    解决办法:
    • 在控制器中添加一个记录图片轮播器滚动位置的数组属性imageViewCurrentLocationArray
    • 在图片轮播器里实现scrollView代理方法,监听记录最终的位移contentOffset.x,停止滚动后通过闭包/代理位置数据传递到控制器,控制器将位置数据添加到记录数组中即可,此方法应该同样适用其他因cell复用机制导致的数据显示混乱问题,(关于数组的操作,应考虑到:下拉刷新,上拉加载更多数据以及其他情况,详细代码见工程)
      至于实现哪个代理方法最为合理,应该视实际的业务需求以及界面效果而定,下面的textField代理也是如此

 

最后图片轮播器添加一个属性接口,接收位置数据,然后在构造方法里设置图片轮播器的contentOffset即可

② 三餐

布局
  • 如图,整个控制器是ViewController,将CollectionView以及上传button添加到viewController.view即可,比较简单
  • 导航栏的标题是自定义的view,然后self.navigationItem.titleView = view;即可

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


3. 功能界面

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

 

布局

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

思路
  • 因为是创建以及草稿功能的界面,所以操作的是本地数据,我写了一个菜谱草稿数据工具类,用来增删改查,非常方便
  • 照片上传
    点击弹出ActionSheet让用户选择是相机、还是相册,然后通过UIImagePickerController的代理方法- imagePickerController:didFinishPickingMediaWithInfo:选取照片即可
    • 因为顶部做法都有上传图片的需求,如果不做判断是谁需要设置图片,会导致数据显示错乱,这个在代理方法中通过判断代理的调用者即可解决图片错乱

 

做法步骤

  • 点击添加步骤,往tableView对应位置插入一行步骤cell,并且在模型数据数组对应位置插入一个数据为空做法数据,如果不添加的话,点击编辑就会因为数据越界导致崩溃

点击调整步骤,执行回调后tableView进入对应状态,[self.tableView setEditing:YES animated:YES];,这里需要注意的问题有:

  • tableView中只有做法这一组cell才进入编辑模式,在代理方法-tableView:canMoveRowAtIndexPath:中判断即可
  • 需要设置cell目标移动的位置,即使其他组不能进入编辑模式,但做法步骤cell还是能移动插入其中,所以需要设置:如果超过了做法步骤所在的section,不管怎么移动,最终都回到做法步骤section
  • 调整移动了cell之后,也需要同步数据中对应做法步骤的位置

  • 用料:
    原理大致与做法步骤相同,只是用料的编辑是在一个新的控制器XCFIngredientEditController,不管有无用料,点击都进入这个控制器,那么:
    • XCFIngredientEditController中添加一个接口,接收已存在的用料数据,如果数据为空,就默认添加两个示例cell,如果不为空,就显示已存在的数据,从而达到新增、编辑的效果
    • 编辑完成或保存后,pop回创建菜谱控制器,并执行回调将编辑好的用料数据回传,刷新界面即可
  • 因为官方的效果是:只要操作了数据,即使不点击保存,也会将数据保存起来,所以我在每个数据操作后面都更新了本地数据[self updateDarft];

② 搜索

 

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

③ 上传作品

布局

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

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

二、市集

1. 商品

布局
  • 上面这部分我的实现方法是:全部作为一个tableHeaderView,然后内部细分为3个部分,控制好布局就可以了
  • 需要注意的是:商品优惠(红色边框button)、店铺优惠(橙色),服务器返回的是字符串类型数据,这里我将优惠信息以button展示(也可以Label),因为数据是动态的,所以可以通过计算字符串最大宽度,然后加上既定的长度,即可设置每个button的不同大小

 

思路
  • 整个控制器是UIViewController,上面商品信息展示部分是tableView,下面图文详情界面是一个UIView(UIView上面放一个类似导航的标签view,下面为CollectionView,CollectionView的cell内部添加tableView),设置好对应frame即可
  • 继续拖动,查看图文详情
    这个通过实现scrollView代理方法,判断contentOffset.y的值是否达到预定值,官方效果是:不必等到松手,达到即商品展示tableView图文详情view同时进行位移动画,同时商品展示tableView.hidden = YES;,从而达到动画切换界面的效果。由图文详情切换回商品展示也是通过代理实现

 

  • 评价CollectionView手势左滑切换控制器
    也是实现scrollView代理方法,通过回调切换控制器
  • 晒图/全部评价界面
    官方效果是:两个tableView重叠在一起,点击导航栏对应按钮设置tableView.hidden

2. 购物车

布局
  • 整个控制器是UIViewController,self.view添加tableView跟底部的结算view
  • 同一个店铺的商品作为一组cell,店铺名为sectionHeader
思路(我设置了清空购物车就重新加载本地数据)
  • 官方是通过服务器接收数据显示购物车内容的,因为没接口所以我就新建了一个本地单例类,保存购物车的数据,并提供数据操作方法