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自定义即可
- 作品展示:
- 这里实现的方法跟上面的
用料、做法...
一样,独立为一组,组内只有一个cell,cell的contentView里从上至下添加:作品个数Label
、作品展示CollectionView
、所有作品Button
即可 -
collectionView手势左滑,松开会加载更多作品数据
这里通过实现scrollView的代理方法,判断contentOffset
的值是否达到预定的数值,达到即调用block,然后控制器发送网络请求加载更多数据,刷新界面即可。(这里我只是实现了一个需求,并没有进一步优化调整)
- 布局:
如图即可,底部加入菜单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的不同大小
思路
- 整个控制器是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. 个人资料
个人界面.gif2. 收货地址
收货地址.gif思路
本地数据工具类,修改内容闭包回调控制器更改数据,内部处理好逻辑关系就可以了
五、动画
① 图片展示
商品界面-图片展示动画.gif思路
界面是一个UIViewController,提供接口接收数据,view中添加一个图片轮播器,present出现后执行动画(这里我只是实现了效果,详细控件分布就不做那么仔细了)
大概步骤:
- 点击图片
- 闭包回调传递
图片在当前窗口的frame值
、图片所在数组的下标
给控制器 - 控制器将数值传递给
图片展示控制器
,并present -
图片展示控制器
接收图片数据
赋值给图片轮播器,然后创建一个imageView(作动画用),frame
设置为从上一个界面接收到的数值,然后imageView执行形变动画 - 动画执行完毕移除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),就以
碰撞方式
的动画(我用的是Facebook
的pop
框架)弹出商品类别选择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,也不强求啦...
-
如果你是大神...
网友评论
ld: library not found for -lAFNetworking
clang: error: linker command failed with exit code 1 (use -v to see invocation)
这个问题
错误一:"_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)
请问这个如何解决
错误二我也不太清楚具体是什么错了
1.因为重用机制造成的轮播器数据混乱问题,其实可以通过重写cell里的prepareForReuse这方法解决
2.少用闭包,多用代理
之前刚好看到楼主的分享,给我这个自学的初学者一个很好学习方向
因为当前还在工作的原因,断续续的一边学习楼主的代码,一边巩固相关知识点,还好项目思路很明确,我目前刚好完成主页的编写;
后续打算再用一个月左右时间完成这个项目,也差不多在这个月结束当前的这份工作
By the way,下份工作我也打算去广州或深圳看看,希望楼主可以对找工作给点suggestion 如果楼主有空,烦请简信 OR E-Mail:741066288@qq.com
最后,还是感谢楼主的分享!嘿嘿!看后面工作能不能碰上楼主
冒昧了解一下,楼主怎么还没开始工作呢?是因为太难在找的过程中麽?还是在继续深入学习中呢?抑或是在留意别的?
我从itunes上下载了ipa,发现里面图片资源不全
260595314@qq.com [真的,没别的意思.就是想参考学习一下.]
加油!~
会是亮点。
// 拿到最后的位置保存到数组中
weakSelf.imageViewCurrentLocationArray[indexPath.row] = @(finalContentOffsetX);
};
还有
- (void)setImageViewDidScrolledBlock:(void (^)())imageViewDidScrolledBlock {
_imageViewDidScrolledBlock = imageViewDidScrolledBlock;
self.showView.imageViewDidScrolledBlock = imageViewDidScrolledBlock;
} 这句self.showView.imageViewDidScrolledBlock = imageViewDidScrolledBlock;
因为我对block还不太熟,不知道是类型不匹配还是
不过我还是比较喜欢纯代码, 虽然比较费时间...但是方便维护和查看.