iOS高仿下廚房(Objective-C)

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ù)覽

首頁.gif
關(guān)注動態(tài).gif
菜譜.gif
帖子與作品.gif
商品界面.gif
商品分類選擇.gif
商品界面-圖片展示動畫.gif
購物車.gif
上傳作品.gif
收貨地址.gif
搜索界面.gif
菜譜-創(chuàng)建與刪除.gif

一、首頁

首頁.png
布局
  • 如圖,首頁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自定義即可
菜譜 - Header.png
菜譜 - 作品展示.png
  • 作品展示:
  • 這里實現(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)整)
菜譜 - 底部.png
  • 布局:
    如圖即可,底部加入菜單button也可以是sectionFooter,
    雖然下廚房幾乎沒有邊框(有分割線),但仔細分析還是很好劃分的

② 作品

作品.png
布局
  • 因為關(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)

關(guān)注動態(tài).gif
布局
  • 整個界面就一個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];
    }
}

② 三餐

作品.png
布局
  • 如圖,整個控制器是ViewController,將CollectionView以及上傳button添加到viewController.view即可,比較簡單

  • 導(dǎo)航欄的標題是自定義的view,然后self.navigationItem.titleView = view;即可

這個界面的接口號稱“時時死”,如果想看效果的童鞋可以自己重新抓包


3. 功能界面

① 菜譜草稿(整個項目最難的界面)

菜譜創(chuàng)建 - 上半部分.png
菜譜創(chuàng)建 - 下半部分.png
布局

如圖所示即可,需要注意的是:因為這個界面是操作本地數(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];
    }];
}
草稿界面-做法.gif
  • 做法步驟
  • 點擊添加步驟,往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;
}
草稿界面-用料.gif
  • 用料:
    原理大致與做法步驟相同,只是用料的編輯是在一個新的控制器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];


② 搜索

搜索界面.gif
搜索界面.png
布局
  • 進入搜索控制器時,判斷本地數(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位即可(詳細代碼見工程)

③ 上傳作品

上傳作品.gif
布局

一個只有tableHeaderView的tableViewController搞定,官方App中圖片、標簽的添加會有動畫,我這里沒有實現(xiàn),大概就是在改變控件frame值時添加動畫即可

思路
  • 本地工具類,并不需要進行數(shù)據(jù)持久化,通過回調(diào)就可以完成數(shù)據(jù)操作
  • 需要注意的是:處理好圖片、標簽長度的顯示以及換行

二、市集

1. 商品

商品界面.png
布局
  • 上面這部分我的實現(xiàn)方法是:全部作為一個tableHeaderView,然后內(nèi)部細分為3個部分,控制好布局就可以了
  • 需要注意的是:商品優(yōu)惠(紅色邊框button)、店鋪優(yōu)惠(橙色),服務(wù)器返回的是字符串類型數(shù)據(jù),這里我將優(yōu)惠信息以button展示(也可以Label),因為數(shù)據(jù)是動態(tài)的,所以可以通過計算字符串最大寬度,然后加上既定的長度,即可設(shè)置每個button的不同大小
商品界面.gif
商品界面-評價.gif
思路
  • 整個控制器是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. 購物車

購物車.gif
布局
  • 整個控制器是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. 確認訂單

訂單 - 上半部分.png
訂單 - 下半部分.png
布局

如圖,需要注意的是:如果計算結(jié)果店鋪優(yōu)惠價格為0,就會隱藏店鋪優(yōu)惠

思路
  • 直接拿到購物車界面勾選好的的商品,通過計算顯示對應(yīng)價格數(shù)字就可以了(運費只添加一次)
  • 選擇收貨地址,訪問本地存儲的收貨地址數(shù)據(jù),有則顯示,沒有則提示添加
  • 因為沒錢沒見過訂單優(yōu)惠長啥樣,所以就沒做了,不過應(yīng)該就是tableView就可以搞定的

三、社區(qū)

社區(qū).gif
思路
  • 點選一個評論cell,就獲取該評論作者的昵稱,賦值到底部的編輯框,然后在編輯控件內(nèi)判斷有無該字符串,有就刪除,無則添加
  • 當編輯框內(nèi),只要最后一個字符串“@”,就顯示用戶tableView:利用通知監(jiān)聽UITextViewTextDidChangeNotification然后回調(diào)傳遞最后一個字符串到控制器,控制器判斷顯示

四、我

(因為我什么數(shù)據(jù)也沒有,就沒做那些詳細界面了)

1. 個人資料

個人界面.gif

2. 收貨地址

收貨地址.gif
思路

本地數(shù)據(jù)工具類,修改內(nèi)容閉包回調(diào)控制器更改數(shù)據(jù),內(nèi)部處理好邏輯關(guān)系就可以了


五、動畫

① 圖片展示

商品界面-圖片展示動畫.gif
思路

界面是一個UIViewController,提供接口接收數(shù)據(jù),view中添加一個圖片輪播器,present出現(xiàn)后執(zhí)行動畫(這里我只是實現(xiàn)了效果,詳細控件分布就不做那么仔細了)
大概步驟:

  1. 點擊圖片
  2. 閉包回調(diào)傳遞 圖片在當前窗口的frame值圖片所在數(shù)組的下標給控制器
  3. 控制器將數(shù)值傳遞給圖片展示控制器,并present
  4. 圖片展示控制器接收圖片數(shù)據(jù)賦值給圖片輪播器,然后創(chuàng)建一個imageView(作動畫用),frame設(shè)置為從上一個界面接收到的數(shù)值,然后imageView執(zhí)行形變動畫
  5. 動畫執(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;
        }];
    }];
}

② 添加商品到購物車

商品分類選擇.gif
商品分類view.png
布局
  • 可以通過添加一個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),就以碰撞方式的動畫(我用的是Facebookpop框架)彈出商品類別選擇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,也不強求啦...

  • 如果你是大神...

END

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,619評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,155評論 3 425
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,635評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,539評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,255評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,646評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,655評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,838評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,399評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,146評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,338評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,893評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,565評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,983評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,257評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,059評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,296評論 2 376

推薦閱讀更多精彩內(nèi)容