2017.5.19編輯:因為官方接口變動,所以一些需要根據(jù)返回數(shù)據(jù)進行動態(tài)調(diào)整布局的地方會崩潰,如果有興趣可以自行抓取最新接口,然后根據(jù)返回數(shù)據(jù)進行調(diào)整。
前言
本開源項目講解了一些App常見功能界面的搭建以及實現(xiàn)思路,適合新手。
為什么是下廚房?
下廚房:一個集合了工具、社區(qū)與平臺電商屬性的家庭美食入口。很棒的一個平臺,App界面也很好看!
關(guān)于項目(Github地址在文章結(jié)尾)
- 開發(fā)環(huán)境:Xcode 7.2,語言:Objective-C
- 用到的工具:Charles抓包工具
- 仿寫程度:只是作為一個練習項目,除了沒有接口的界面,以及一些小細節(jié)沒有調(diào)整,其他大部分主要功能都實現(xiàn)了。其他部分童鞋們可以自己嘗試實現(xiàn)~
- 剛開始寫的時候控件是用純代碼寫的,后來發(fā)現(xiàn)太耗時間就改用Xib了
- 代碼如果有不合理的地方(如:命名),請見諒,學習實現(xiàn)思路就好,代碼請不要借鑒。
- 這個開源項目適合新手,基本的界面布局以及業(yè)務(wù)邏輯都有,看完這個基本也會寫簡單的App啦
- 備注:因為當時對block有特殊偏好所以通篇沒有一個protocol...代碼也是比較新手,嘛主要以實現(xiàn)思路為主!希望能對各位有幫助!
效果預(yù)覽
一、首頁
布局
- 如圖,首頁tableView就可以搞定
- 日期標題為sectionHeader
- 下面的就是cell了,下廚房返回的數(shù)據(jù)中cell有6種模板,通過自定義cell,根據(jù)不同模板顯示不同效果,很簡單就不描述了
思路:
頂部導(dǎo)航部分點擊事件,通過給每個控件綁定tag
,然后定義對應(yīng)的枚舉變量,通過閉包(Block)將事件傳遞到控制器后,控制器判斷枚舉值即可。
1. 跳轉(zhuǎn)的界面控制器
① 菜譜
布局
- 整個界面是一個UIViewController,放上一個tableView,然后添加底部的
收藏、丟進菜籃子
自定義view - 用料、做法、小貼士、被加入的菜單這四個標題是sectionHeader,其內(nèi)容對應(yīng)為一組,每組cell自定義即可
- 作品展示:
- 這里實現(xiàn)的方法跟上面的
用料、做法...
一樣,獨立為一組,組內(nèi)只有一個cell,cell的contentView里從上至下添加:作品個數(shù)Label
、作品展示CollectionView
、所有作品Button
即可 -
collectionView手勢左滑,松開會加載更多作品數(shù)據(jù)
這里通過實現(xiàn)scrollView的代理方法,判斷contentOffset
的值是否達到預(yù)定的數(shù)值,達到即調(diào)用block,然后控制器發(fā)送網(wǎng)絡(luò)請求加載更多數(shù)據(jù),刷新界面即可。(這里我只是實現(xiàn)了一個需求,并沒有進一步優(yōu)化調(diào)整)
- 布局:
如圖即可,底部加入菜單button
也可以是sectionFooter,
雖然下廚房幾乎沒有邊框(有分割線),但仔細分析還是很好劃分的
② 作品
布局
- 因為
關(guān)注動態(tài)
、買買買
界面跟這個差不多,需要復(fù)用到這個界面的內(nèi)容,所以這整個界面是只有一個cell
的tableView - 上面是個圖片輪播器,下面添加控件即可,
描述Label
以及用戶評論Label
的高度是有內(nèi)容決定的,這個只需要在模型中添加一個labelHeight
屬性,然后在內(nèi)部計算好高度,直接返回給控件就可以了。控件部分可以根據(jù)不同界面顯示的不同效果,分割成若干部分,然后給cell添加一個type
屬性(枚舉類型),創(chuàng)建的時候告訴cell屬于哪一種type
,然后根據(jù)不同type
進行調(diào)整即可。
2. 導(dǎo)航
① 關(guān)注動態(tài)
布局
- 整個界面就一個tableView,一個動態(tài)為
作品界面的cell
遇到的問題
圖片輪播器會受
tableViewCell的復(fù)用機制
影響,導(dǎo)致錯亂(點贊按鈕的狀態(tài)是由服務(wù)器返回的數(shù)據(jù)決定的,這里我就不模擬了)
解決辦法:在控制器中添加一個記錄圖片輪播器滾動位置的數(shù)組屬性
imageViewCurrentLocationArray
在圖片輪播器里實現(xiàn)scrollView代理方法,監(jiān)聽記錄最終的位移
contentOffset.x
,停止?jié)L動后通過閉包/代理將位置數(shù)據(jù)
傳遞到控制器,控制器將位置數(shù)據(jù)
添加到記錄數(shù)組
中即可,此方法應(yīng)該同樣適用其他因cell復(fù)用機制
導(dǎo)致的數(shù)據(jù)顯示混亂問題,(關(guān)于數(shù)組的操作,應(yīng)考慮到:下拉刷新,上拉加載更多數(shù)據(jù)以及其他情況,詳細代碼見工程)
至于實現(xiàn)哪個代理方法最為合理,應(yīng)該視實際的業(yè)務(wù)需求以及界面效果而定,下面的textField代理
也是如此
// scrollView停止?jié)L動后記錄contentOffset.x
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
!self.imageViewDidScrolledBlock ? : self.imageViewDidScrolledBlock(scrollView.contentOffset.x);
}
- 最后圖片輪播器添加一個屬性接口,接收
位置數(shù)據(jù)
,然后在構(gòu)造方法里設(shè)置圖片輪播器的contentOffset
即可
- (void)setImageViewCurrentLocation:(CGFloat)imageViewCurrentLocation {
_imageViewCurrentLocation = imageViewCurrentLocation;
// 恢復(fù)顯示collectionView滾動的位置
[self.collectionView setContentOffset:CGPointMake(imageViewCurrentLocation, 0)];
// 恢復(fù)顯示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];
}
}
② 三餐
布局
如圖,整個控制器是
ViewController
,將CollectionView
以及上傳button
添加到viewController.view
即可,比較簡單導(dǎo)航欄的標題是自定義的view,然后
self.navigationItem.titleView = view;
即可
這個界面的接口號稱“時時死”,如果想看效果的童鞋可以自己重新抓包
3. 功能界面
① 菜譜草稿(整個項目最難的界面)
布局
如圖所示即可,需要注意的是:因為這個界面是操作本地數(shù)據(jù),所以要時刻根據(jù)數(shù)據(jù)得變化判斷控件是否顯示、如何顯示
思路
- 因為是創(chuàng)建以及草稿功能的界面,所以操作的是本地數(shù)據(jù),我寫了一個
菜譜草稿數(shù)據(jù)工具類
,用來增刪改查,非常方便 -
照片上傳:
點擊彈出ActionSheet
讓用戶選擇是相機
、還是相冊
,然后通過UIImagePickerController
的代理方法- imagePickerController:didFinishPickingMediaWithInfo:
選取照片即可 - 因為
頂部
跟做法
都有上傳圖片的需求,如果不做判斷是誰需要設(shè)置圖片,會導(dǎo)致數(shù)據(jù)顯示錯亂,這個在代理方法中通過判斷代理的調(diào)用者即可解決圖片錯亂
- (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:^{
// 選取完成后更新本地數(shù)據(jù)
[self updateDarft];
}];
}
- 做法步驟:
- 點擊
添加步驟
,往tableView對應(yīng)位置插入一行步驟cell
,并且在模型數(shù)據(jù)數(shù)組對應(yīng)位置插入一個數(shù)據(jù)為空的做法數(shù)據(jù)
,如果不添加的話,點擊編輯就會因為數(shù)據(jù)越界導(dǎo)致崩潰
// 增加一行點擊回調(diào)
instructionFooter.addInstructionBlock = ^{
// 添加一個空的本地數(shù)據(jù)
[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];
};
- 點擊
調(diào)整步驟
,執(zhí)行回調(diào)后tableView進入對應(yīng)狀態(tài),[self.tableView setEditing:YES animated:YES];
,這里需要注意的問題有: - tableView中只有
做法
這一組cell才進入編輯模式,在代理方法-tableView:canMoveRowAtIndexPath:
中判斷即可 -
需要設(shè)置cell目標移動的位置,即使其他組不能進入編輯模式,但
做法步驟cell
還是能移動插入其中,所以需要設(shè)置:如果超過了做法步驟
所在的section,不管怎么移動,最終都回到做法步驟
section - 調(diào)整移動了cell之后,也需要同步數(shù)據(jù)中對應(yīng)
做法步驟
的位置
// 如果不是步驟數(shù)組,就回到對應(yīng)位置
- (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;
}
用料:
原理大致與做法步驟
相同,只是用料的編輯是在一個新的控制器XCFIngredientEditController
,不管有無用料,點擊都進入這個控制器,那么:在
XCFIngredientEditController
中添加一個接口,接收已存在的用料數(shù)據(jù)
,如果數(shù)據(jù)為空,就默認添加兩個示例cell
,如果不為空,就顯示已存在的數(shù)據(jù),從而達到新增、編輯的效果編輯完成或保存后,pop回
創(chuàng)建菜譜
控制器,并執(zhí)行回調(diào)將編輯好的用料數(shù)據(jù)
回傳,刷新界面即可因為官方的效果是:只要操作了數(shù)據(jù),即使不點擊
保存
,也會將數(shù)據(jù)保存起來,所以我在每個數(shù)據(jù)操作后面都更新了本地數(shù)據(jù)[self updateDarft];
② 搜索
布局
- 進入搜索控制器時,判斷本地數(shù)據(jù)中是否有已經(jīng)搜索過的關(guān)鍵詞,有則加載,沒有就不顯示第0組cell
- 輸入文字時,利用通知監(jiān)聽
UITextFieldTextDidChangedNotification
,然后通過閉包回傳textField的文字內(nèi)容
給控制器,同時時刻刷新tableView即可 - 底部流行搜索關(guān)鍵詞是網(wǎng)絡(luò)數(shù)據(jù)加載的,一個九宮格搞定
思路
- 因為沒有接口,所以我封裝了一個本地單例類,保存搜索過的關(guān)鍵詞,并提供數(shù)據(jù)操作的方法
- 搜索:在本地數(shù)據(jù)中遍歷是否已存在該關(guān)鍵詞,存在就將舊關(guān)鍵詞刪除,然后穿插新關(guān)鍵詞到第0位;如果不存在就直接插入到第0位即可(詳細代碼見工程)
③ 上傳作品
布局
一個只有tableHeaderView的tableViewController搞定,官方App中圖片、標簽的添加會有動畫,我這里沒有實現(xiàn),大概就是在改變控件frame值時添加動畫即可
思路
- 本地工具類,并不需要進行數(shù)據(jù)持久化,通過回調(diào)就可以完成數(shù)據(jù)操作
- 需要注意的是:處理好圖片、標簽長度的顯示以及換行
二、市集
1. 商品
布局
- 上面這部分我的實現(xiàn)方法是:全部作為一個tableHeaderView,然后內(nèi)部細分為3個部分,控制好布局就可以了
- 需要注意的是:商品優(yōu)惠(紅色邊框button)、店鋪優(yōu)惠(橙色),服務(wù)器返回的是字符串類型數(shù)據(jù),這里我將優(yōu)惠信息以button展示(也可以Label),因為數(shù)據(jù)是動態(tài)的,所以可以通過計算字符串最大寬度,然后加上既定的長度,即可設(shè)置每個button的不同大小
思路
- 整個控制器是UIViewController,上面
商品信息展示
部分是tableView,下面圖文詳情界面
是一個UIView(UIView上面放一個類似導(dǎo)航的標簽view
,下面為CollectionView,CollectionView的cell內(nèi)部添加tableView),設(shè)置好對應(yīng)frame即可 -
繼續(xù)拖動,查看圖文詳情:
這個通過實現(xiàn)scrollView代理方法,判斷contentOffset.y
的值是否達到預(yù)定值,官方效果是:不必等到松手,達到即商品展示tableView
和圖文詳情view
同時進行位移動畫,同時商品展示tableView.hidden = YES;
,從而達到動畫切換界面的效果。由圖文詳情
切換回商品展示
也是通過代理實現(xiàn)
// 向上拖動到一定程度,切換至圖文詳情界面
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// 預(yù)定值為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導(dǎo)致的內(nèi)存泄漏問題" atView:self.view];
}];
}
}
-
評價CollectionView手勢左滑切換控制器:
也是實現(xiàn)scrollView代理方法,通過回調(diào)切換控制器 -
曬圖/全部評價界面:
官方效果是:兩個tableView重疊在一起,點擊導(dǎo)航欄對應(yīng)按鈕設(shè)置tableView.hidden
2. 購物車
布局
- 整個控制器是UIViewController,
self.view
添加tableView跟底部的結(jié)算view
- 同一個店鋪的商品作為一組cell,店鋪名為sectionHeader
思路(我設(shè)置了清空購物車就重新加載本地數(shù)據(jù))
- 官方是通過服務(wù)器接收數(shù)據(jù)顯示購物車內(nèi)容的,因為沒接口所以我就新建了一個本地單例類,保存購物車的數(shù)據(jù),并提供數(shù)據(jù)操作方法
- 業(yè)務(wù)邏輯:
- 給每個商品添加一個
XCFCartItemState
枚舉類型屬性,記錄是否被選中,點選商品勾選button
后通過回調(diào),在控制器更改本地數(shù)據(jù)。 -
店鋪勾選
狀態(tài)以及全選
狀態(tài),通過遍歷本地數(shù)據(jù)中對應(yīng)店鋪內(nèi)商品
或所有商品
是否全部選中,再通過點選商品回調(diào)
刷新界面就可以達到實時更改狀態(tài)的效果了 -
商品購買數(shù)量:
在cell內(nèi)部實現(xiàn)textField代理方法-textFieldShouldEndEditing:
監(jiān)聽購買數(shù)量,編輯完成就通過閉包傳遞購買數(shù)量給控制器,控制器更改本地數(shù)據(jù)并刷新界面
cell.itemNumberChangeBlock = ^(NSUInteger number) { // 修改商品個數(shù)回調(diào)
// 拿到最新的數(shù)據(jù),再修改數(shù)量
// 如果不拿到最新數(shù)據(jù),在編輯商品數(shù)量時點擊店鋪全選 會導(dǎo)致正在編輯的商品無法同步選中狀態(tài)的bug
NSArray *newShopArray = [XCFCartItemTool totalItems][indexPath.section];
XCFCartItem *newItem = newShopArray[indexPath.row];
// 修改數(shù)據(jù)中商品個數(shù)的值
newItem.number = number;
// 更新本地數(shù)據(jù)
[XCFCartItemTool updateItemAtIndexPath:indexPath withItem:newItem];
// 刷新界面
[weakSelf.tableView reloadData];
};
-
切換編輯/刪除模式:
簡單的閉包回調(diào)刷新界面即可,刪除的話,根據(jù)商品狀態(tài)是否點選,點選就刪除,因為點選狀態(tài)跟編輯模式是通用的,所以不需要另外計算
3. 確認訂單
布局
如圖,需要注意的是:如果計算結(jié)果店鋪優(yōu)惠價格為0,就會隱藏店鋪優(yōu)惠
思路
- 直接拿到購物車界面勾選好的的商品,通過計算顯示對應(yīng)價格數(shù)字就可以了(運費只添加一次)
- 選擇收貨地址,訪問本地存儲的收貨地址數(shù)據(jù),有則顯示,沒有則提示添加
- 因為
沒錢沒見過訂單
、優(yōu)惠
長啥樣,所以就沒做了,不過應(yīng)該就是tableView就可以搞定的
三、社區(qū)
思路
- 點選一個評論cell,就獲取該評論作者的昵稱,賦值到底部的編輯框,然后在編輯控件內(nèi)判斷有無該字符串,有就刪除,無則添加
- 當編輯框內(nèi),只要最后一個字符串為“@”,就顯示
用戶tableView
:利用通知監(jiān)聽UITextViewTextDidChangeNotification
然后回調(diào)傳遞最后一個字符串到控制器,控制器判斷顯示
四、我
(因為我什么數(shù)據(jù)也沒有,就沒做那些詳細界面了)
1. 個人資料
2. 收貨地址
思路
本地數(shù)據(jù)工具類,修改內(nèi)容閉包回調(diào)控制器更改數(shù)據(jù),內(nèi)部處理好邏輯關(guān)系就可以了
五、動畫
① 圖片展示
思路
界面是一個UIViewController,提供接口接收數(shù)據(jù),view中添加一個圖片輪播器,present出現(xiàn)后執(zhí)行動畫(這里我只是實現(xiàn)了效果,詳細控件分布就不做那么仔細了)
大概步驟:
- 點擊圖片
- 閉包回調(diào)傳遞
圖片在當前窗口的frame值
、圖片所在數(shù)組的下標
給控制器 - 控制器將數(shù)值傳遞給
圖片展示控制器
,并present -
圖片展示控制器
接收圖片數(shù)據(jù)
賦值給圖片輪播器,然后創(chuàng)建一個imageView(作動畫用),frame
設(shè)置為從上一個界面接收到的數(shù)值,然后imageView執(zhí)行形變動畫 - 動畫執(zhí)行完畢移除imageView,顯示圖片輪播器
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// 關(guān)閉按鈕
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];
// 設(shè)置屬性
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;
}];
}];
}
② 添加商品到購物車
布局
- 可以通過添加一個
window
,來實現(xiàn)這個需求,也可以用直接在根窗口[UIApplication sharedApplication].keyWindow
上添加一個自定義的UIView,我這里用的是后者。 - 自定義一個半透明的UIView,按照官方的動畫效果,我這里用一個tableView,只設(shè)置了tableHeaderView(因為懶所以沒有使用UIScrollView),tableHeaderView的內(nèi)容又是一個自定義UIView,控件擺放不多說了很簡單,不過需要注意的是:
- 控件之間的邏輯關(guān)系
-
商品種類button
的標題由服務(wù)器數(shù)據(jù)決定,所以整個tableView的frame并不是固定的 - 因為沒找到可以直接在計數(shù)器中間添加一個view的接口方法,所以我自定義了一個計數(shù)器
XCFStepper
,很簡單的一個小控件,處理好邏輯關(guān)系就可以了
思路
- 在購物車本地數(shù)據(jù)工具類
XCFCartItemTool
的刷新數(shù)據(jù)
方法中(不管是刪除還是新增,都會刷新數(shù)據(jù))發(fā)送自定義的通知,并傳遞商品數(shù)量totalNumber
// 添加完成后發(fā)送通知,用處:購物車圖標動畫
[[NSNotificationCenter defaultCenter] postNotificationName:XCFCartItemTotalNumberDidChangedNotification
object:nil
userInfo:@{@"goodsCount" : @([self totalNumber])}];
- 購物車圖標為自定義的UIView,
[[UIBarButtonItem alloc] initWithCustomView:cartIcon];
然后添加到導(dǎo)航欄,在view內(nèi)部接收XCFCartItemTotalNumberDidChangedNotification
通知后作核心動畫就能實現(xiàn)效果了
- (void)awakeFromNib {
// 監(jiān)聽“添加商品到購物車”的通知
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(cartItemTotalNumberDidChanged:)
name:XCFCartItemTotalNumberDidChangedNotification
object:nil];
NSUInteger count = [XCFCartItemTool totalNumber];
if (count) { // 有商品才顯示數(shù)量標簽
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;
// 取得商品數(shù)量
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:^{
// 購物車圖標執(zhí)行放大動畫
self.countButton.transform = CGAffineTransformMakeScale(1.5, 1.5);
} completion:^(BOOL finished) {
// 改變顯示的商品數(shù)
[self.countButton setTitle:[NSString stringWithFormat:@"%zd", count]
forState:UIControlStateNormal];
// 還原圖標大小
[UIView animateWithDuration:0.5 animations:^{
self.countButton.transform = CGAffineTransformMakeScale(1, 1);
}];
}];
});
}
- 這里我模擬了一下添加效果:從本地數(shù)據(jù)中隨機取出一個商品
- 如果該商品只有一個類型,直接添加到購物車
- 如果該商品有多種類型(如:300g、450g、600g),就以
碰撞方式
的動畫(我用的是Facebook
的pop
框架)彈出商品類別選擇tableView
,同時后面的商品信息view
進行縮放,設(shè)置要購買的類別和數(shù)量后,商品圖片執(zhí)行核心動畫,然后移除類別選擇view
,執(zhí)行添加到購物車
動畫 - 這個界面也比較簡單,只是要注意各種小細節(jié),動畫的順序,以及相應(yīng)的
添加到購物車(或立即購買)業(yè)務(wù)邏輯
// 隨機添加一樣商品
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];
// 確認購買回調(diào)
kindsView.confirmBlock = ^(XCFCartItem *item) {
// 本地購物車數(shù)據(jù)添加商品
[XCFCartItemTool addItem:item];
[UILabel showStats:[NSString stringWithFormat:@"添加:\n%@", item.kind_name] atView:weakSelf.view];
};
// 取消回調(diào)
kindsView.cancelBlock = ^{
// 恢復(fù)界面大小
[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];
}];
}
}
最后想說的話
- 關(guān)于項目
- 自動布局是按照iPhone6s的,其他機型可能會有偏差,見諒
- 能實現(xiàn)的基本都實現(xiàn)了
-
市集
模塊接口被加密,所以做不了,不過目測是簡單的CollectionView+一些動畫就能搞定的 -
菜籃子
界面應(yīng)該是我的實現(xiàn)思路有問題所以沒完成,后續(xù)如果有空的話會嘗試實現(xiàn) - 一些很簡單的、相同效果的界面我
實在不想寫了!也沒做,有興趣的童鞋可以自己嘗試實現(xiàn)
-
- 寫代碼過程中跑去騷擾了ManoBoo小神,在這里超級感謝ManoBoo!還幫我解決了兩個bug!還有感謝維尼的小熊大神的開源App,兩位前輩對界面實現(xiàn)思路的講解讓我受益匪淺。感謝一切開源。
Github代碼下載地址
高仿下廚房App 開源咯~
-
如果你是新手,并且我的項目對你有幫助的話,請大方的給我Star!開源的世界如此美好互幫互助感激不盡!如果你不給我Star,也不強求啦...
-
如果你是大神...