UITableView記錄

有時候 TableView 我們可能只會需要他的動態(tài)性(數(shù)據(jù)驅(qū)動)而 Cell 重用可能會導(dǎo)致問題

面對下面的界面我們該如何去做 ??

確認(rèn)訂單_1_.jpg

界面描述:

  1. 這是一個關(guān)于商品的界面, 商品依據(jù)店家來分類, 所有如果使用 tableView我們可以設(shè)置 Session 為店家的個數(shù)
  2. 商店下的商品個數(shù)不確定, 可變, 所以我選擇再次嵌套一層 TableView, 這個 TableView 就只顯示商品

所以界面拆分如下:

確認(rèn)訂單_1_.jpg

界面拆分完成, 接下來應(yīng)該梳理下數(shù)據(jù)的問題, 界面流程如下:

  1. 程序進(jìn)入到該界面, 從前一個界面中傳遞的信息中獲得所有商品的價格, 計算總價格, 推出整個頁面底部的合計金額
  2. 主界面請求網(wǎng)絡(luò)獲得用戶的地址, 用戶賬號下的禮品卡余額
  3. 每個商店根據(jù)自己的商店信息發(fā)起網(wǎng)絡(luò)請求, 請求可以使用的優(yōu)惠方式(紅包, 打折卡)
  4. 請求獲得每個商店的優(yōu)惠, 更新 UI, 將不包含的優(yōu)惠從界面中移除
  5. 當(dāng)用戶在商家下選擇任意或取消一種優(yōu)惠時, 商家底部的實(shí)付要做相應(yīng)的更新, 同時底部的總價要做相應(yīng)的更新
  6. 當(dāng)選擇好商家優(yōu)惠, 用戶再次選擇余額或禮品卡同樣樣做相應(yīng)的 UI 及數(shù)據(jù)更新, 防止總價小于優(yōu)惠的價格導(dǎo)致總價出現(xiàn)負(fù)數(shù)的情況
  7. 項目后期拆分訂單被取消, 替代的是商家的運(yùn)費(fèi), 不同的商家要請求獲得運(yùn)費(fèi), 這個請求的運(yùn)費(fèi)也會影響商家下的實(shí)付金額, 界面下的合計金額

根據(jù)上面的流程, 我做如下的設(shè)計:

  1. 整個 ViewController 的數(shù)據(jù)由一個 PrimaryDataController 控制
  2. 每個商店的數(shù)據(jù)由一個 SubDataController 控制
  3. PrimaryDataController 有個數(shù)組包含 SubDataController, 有多少商店就有多少 SubDataController 對象
  4. 每個藍(lán)色區(qū)域的 Cell 綁定一個 SubDataController(注意這是個埋坑點(diǎn))
  5. 因?yàn)楦鱾€不同的店家的優(yōu)惠要分別異步請求, 請求結(jié)束后要刷新界面, 我們還要監(jiān)聽店家優(yōu)惠金額的改變, 監(jiān)聽變化有幾種可選措施: (1). KVO, 通知 (2). 代理, (3). Block; 首先 KVO, 通知可以放棄, 因?yàn)樗麄兪且粚Χ嗟年P(guān)系, 更改優(yōu)惠方法發(fā)出一條通知, 而整個界面所有的藍(lán)色 Cell 都是監(jiān)聽者, 這樣無法確定哪一個應(yīng)該發(fā)生相應(yīng)的更改. 代理, Block 的回調(diào)觸發(fā)都是一對一的, 這兩種作為可選操作, 這里我選擇的是 Block
  6. 因?yàn)榈?步我選擇的 Block 觸發(fā)回調(diào), 看第四步有坑, 這里的坑是指藍(lán)色 Cell 指定相同的Identifier, 所以藍(lán)色 Cell 是可以重用的, 如果一重用, SubDataController 的回調(diào)綁定關(guān)系就會產(chǎn)生問題, 這個地方是我掉的一個坑, 還有如果店家的優(yōu)惠信息請求結(jié)束要相應(yīng)的對優(yōu)惠進(jìn)行顯示和隱藏, 就是要更新藍(lán)色 Cell 的高度, 這樣難免調(diào)用 TableView 的 reloadData, 如果一調(diào)用 reloadData, 界面就會重新賦值, 所以會面臨一個問題, 有多個店家, 假設(shè)店家優(yōu)惠請求結(jié)束是依次結(jié)束, 這樣當(dāng)用戶下滑時, 第一個藍(lán)色 Cell 進(jìn)入重用池, 然后再從重用池中取出顯示另一個店家商品信息, 這樣第一個店家優(yōu)惠信息請求結(jié)束實(shí)際是在這個顯示這個店家商品信息刷新, 而且會一個接一個結(jié)束, 不停地 reloadData, 導(dǎo)致問題

具體從代碼中了解如何解決:
首先創(chuàng)建 PrimaryDataController

#import <Foundation/Foundation.h>

@interface WN_PrimaryDataController : NSObject

@property (nonatomic, strong) NSDictionary *shoppingCartsInfo;

/**
 *  每個品牌 Section 的數(shù)據(jù)控制器
 */
@property (nonatomic, strong) NSArray *subDataControllers;

/**
 *  確認(rèn)訂單中總價錢
 */
@property (nonatomic, strong) NSDecimalNumber *totalPrice;

/**
 *  除去優(yōu)惠方法的商品總價
 */
@property (nonatomic, strong, readonly) NSDecimalNumber *originalTotalPrice;

/**
 *  優(yōu)惠價格
 */
@property (nonatomic, strong) NSDecimalNumber *privilegePrice;

/**
 *  更改總價后觸發(fā) View 中的回調(diào)
 */
@property (nonatomic, copy) void (^changeTotalPriceCallBack)(NSDecimalNumber *totalPrice);

/**
 *  確認(rèn)訂單中商品總數(shù)
 */
@property (nonatomic, assign, readonly) NSUInteger productCount;

/**
 *  用戶輸入的使用的禮品卡金額
 */
@property (nonatomic, copy) NSString *giftCardValue;
/**
 *  總的禮品卡余額
 */
@property (nonatomic, copy, readonly) NSString *totalGiftCardValue;
/**
 *  用戶輸入的使用的用戶余額
 */
@property (nonatomic, copy) NSString *balanceValue;

/**
 *  運(yùn)費(fèi)
 */
@property (nonatomic, strong) NSDecimalNumber *freight;
/**
 *  總的用戶余額
 */
@property (nonatomic, copy, readonly) NSString *totalUserBalanceValue;

@property (nonatomic, copy) void (^resertGiftValue)();
@property (nonatomic, copy) void (^resertBalanceValue)();
@property (nonatomic, copy) void (^resertGiftCardOrBalance)();

/**
 *  某一個品牌下添加紅包或者打折卡導(dǎo)致總優(yōu)惠發(fā)送變化
 *
 *  @param price 某個品牌選擇紅包或者打折卡產(chǎn)生的優(yōu)惠金額
 */
- (void)addPrivilegePrice:(NSDecimalNumber *)price;

/**
 *  某個品牌下移除紅包或者打折卡導(dǎo)致總優(yōu)發(fā)生變化
 *
 *  @param price 某個品牌選擇紅包或者打折卡產(chǎn)生的優(yōu)惠金額
 */
- (void)removePrivilegePrice:(NSDecimalNumber *)price;

@end

SubDataController:

#import <Foundation/Foundation.h>
#import "WN_PrivilegeModel.h"
@class WN_PrimaryDataController;
@interface WN_SubDataController : NSObject
@property (nonatomic, weak) WN_PrimaryDataController *primaryDataController;

/**
 *  該品牌下商品的總價
 */
@property (nonatomic, strong, readonly) NSDecimalNumber *totalPrice;
/**
 *  原始價格, 用于計算打折卡折扣
 */
@property (nonatomic, strong) NSDecimalNumber *originalTotalPrice;

/**
 *  優(yōu)惠價格
 */
@property (nonatomic, strong, readonly) NSDecimalNumber *privilegePrice;

/**
 *  更改總價后觸發(fā) View 中的回調(diào)
 */
@property (nonatomic, copy) void (^changeTotalPriceCallBack)(NSDecimalNumber *totalPrice);

/**
 *  優(yōu)惠更改后觸發(fā) View 中的回調(diào)
 */
@property (nonatomic, copy) void (^changePrivilegePriceCallBack)(NSDecimalNumber *privilegePrice);


@property (nonatomic, copy) void (^changeFreightPriceCallBack)(NSString *freightPrice);

/**
 *  更改優(yōu)惠后 PrimaryDataController 中的回調(diào)
 */
@property (nonatomic, copy) void (^changePrivilegePricePrimaryDataControllerCallBack)(NSDecimalNumber *privilegePrice);

/**
 *  該品牌下每件商品的禮品卡使用比例
 */
@property (nonatomic, strong) NSMutableArray *giftCardUsageRanges;

/**
 *  設(shè)置該品牌下所有的商品信息
 */
- (void)setCartItemInfo:(NSDictionary *)dic;

/**
 * 該品牌下商品的總數(shù)
 */
@property (nonatomic, assign, readonly) NSInteger productCount;

/**
 *  可用紅包數(shù)組
 */
@property (nonatomic, strong, readonly) NSArray *redPackets;
/**
 *  可用紅包個數(shù)
 */
@property (nonatomic, assign, readonly) NSUInteger validityRedPacketsCount;

/**
 *  紅包模型
 */
@property (nonatomic, strong, readonly) WN_RedpacketModel *redpacketModel;

/**
 *  可用打折卡數(shù)組
 */
@property (nonatomic, strong, readonly) NSArray *discountCards;

/**
 *  可用打折卡個數(shù)
 */
@property (nonatomic, assign, readonly) NSUInteger validityDiscountCardCount;

/**
 *  打折卡模型
 */
@property (nonatomic, strong, readonly) WN_DiscountCardModel *discountCardModel;

/**
 *  發(fā)票信息
 */
@property (nonatomic, copy) NSDictionary *invoiceInfoDictionary;

/**
 *  運(yùn)費(fèi)
 */
@property (nonatomic, copy, readonly) NSString *freight;

@property (nonatomic, copy, readonly) NSString *shoppingCardIds;

/**
 *  第幾個品牌
 */
@property (nonatomic, assign) NSUInteger index;

@property (nonatomic, copy) NSString *brandId;

@property (nonatomic, assign) BOOL loading;
@property (nonatomic, copy) void (^changeLoadPrivilegeStatusCallBack)(BOOL loading);


/**
 *  根據(jù)品牌價值完紅包打折卡后的回調(diào)
 *  @param callBack     根據(jù)品牌價值完紅包打折卡后的回調(diào)
 *  redPackets 紅包數(shù)組
 *  redPackCount 紅包個數(shù)
 *  discounts 打折卡數(shù)組
 *  discountCardCount 打折卡個數(shù)
 *  redpacketModel 與本品牌關(guān)聯(lián)的紅包模型
 *  discountCardModel 與本品牌關(guān)聯(lián)的打折卡模型
 *  freight 運(yùn)費(fèi)
 */
- (void)loadDataFinishedCallBack:(void (^)(NSArray *redPackets,
                                           NSUInteger redPackCount,
                                           NSArray *discounts,
                                           NSUInteger discountCardCount,
                                           WN_RedpacketModel *redpacketModel,
                                           WN_DiscountCardModel *discountCardModel,
                                           NSString *freight)) callBack;

@end

TableViewController 中:

  1. 根據(jù)數(shù)據(jù)創(chuàng)建 PrimaryDataController, SubDataController:
    - (WN_PrimaryDataController *)primaryDataController {
        if (!_primaryDataController) {
            // 創(chuàng)建主數(shù)據(jù)控制器
            _primaryDataController = [[WN_PrimaryDataController alloc] init];
            NSMutableArray *subDataControllers = [NSMutableArray arrayWithCapacity:_brands.count];
            __weak typeof(self)weakSelf = self;
            __weak typeof(_primaryDataController)weakPrimayDataController = _primaryDataController;
            
            // 根據(jù)每個店家的數(shù)據(jù)創(chuàng)建店家的數(shù)據(jù)控制器
            for (NSInteger index = 0; index < _brands.count; index++) {
                NSDictionary *obj = _brands[index];
                WN_SubDataController *subDataController = [[WN_SubDataController alloc] init];
                subDataController.index = index;
                subDataController.brandId = obj[@"brandId"];
                subDataController.primaryDataController = weakPrimayDataController;
                [subDataController setCartItemInfo:obj];
                [subDataControllers addObject:subDataController];
            }
            
            _primaryDataController.subDataControllers = subDataControllers;
            [_primaryDataController setChangeTotalPriceCallBack:^(NSDecimalNumber *totalPrice) {
                weakSelf.totalPriceLabel.text = [NSString stringWithFormat:@"%.2f",[totalPrice floatValue]];
            }];
            [_primaryDataController setetUserBalanceText:^(NSString *placeholder, NSString *giftCardValue, NSString *text, NSString *balanceValue) {
                weakSelf.userBalanceView.balanceTextField.placeholder = text;
                weakSelf.userBalanceView.giftCardTextField.placeholder = placeholder;
                [weakSelf.userBalanceView setGiftcards:giftCardValue userBalance:balanceValue];
            }];
            _totalPriceLabel.text = [NSString stringWithFormat:@"%.1f",[_primaryDataController.totalPrice floatValue]];
            _productCountLabel.text = [NSString stringWithFormat:@"共%zd 件商品",_primaryDataController.productCount];
            
        }
        return _primaryDataController;
    }
  1. 藍(lán)色 Cell

     #import <UIKit/UIKit.h>
     @class WN_SubDataController, WN_PrimaryDataController;
     @interface WN_ProductInfoCell : UITableViewCell
     @property (nonatomic, strong, readonly) NSDictionary *info;
     // 對應(yīng)一個數(shù)據(jù)控制器
     @property (nonatomic, strong, readonly) WN_SubDataController *subDataController;
     @property (nonatomic, copy) void (^reloadView)();
     
     /**
      *  設(shè)置品牌商品信息, 配置一個數(shù)據(jù)控制類
      *
      *  @param info           商品信息
      *  @param dataController 數(shù)據(jù)控制類
      */
     - (void)setBrandProductsInfo:(NSDictionary *)info brandProductDataController:(WN_SubDataController *)dataController;
     
     + (instancetype)cell;
     
     @end
    
  2. 藍(lán)色 Cell 綁定子數(shù)據(jù)控制器, 并綁定數(shù)據(jù)回調(diào)

     - (void)setBrandProductsInfo:(NSDictionary *)info brandProductDataController:(WN_SubDataController *)subDataController {
         _info = info;
         _subDataController = subDataController;
         self.proDSDelegate.products = _info[@"shoppingCartItemdetail"];
         self.productItemTableViewHeightConstraint.constant = self.proDSDelegate.products.count * ProductItemRowHeight;
         [UIView animateWithDuration:.3 animations:^{
             [self.contentView performSelector:@selector(layoutIfNeeded)
                                    withObject:nil
                                    afterDelay:0.f
                                       inModes:@[NSDefaultRunLoopMode]];
         }];
         
         self.totalPriceLabel.text = [NSString stringWithFormat:@"%.2f",[_subDataController.totalPrice floatValue]];
         
         __weak typeof(self)weakSelf = self;
         [_subDataController setChangeLoadPrivilegeStatusCallBack:^(BOOL loading) {
             // 運(yùn)費(fèi)獲得回調(diào)
             weakSelf.freightMaskView.hidden = loading ? NO: YES;
         }];
         
         [_subDataController setChangeTotalPriceCallBack:^(NSDecimalNumber *totalPrice) {
             // 總價發(fā)生變化回調(diào) 
             weakSelf.totalPriceLabel.text = [NSString stringWithFormat:@"%.2f",[totalPrice floatValue]];
         }];
         
         [_subDataController setChangePrivilegePriceCallBack:^(NSDecimalNumber *privliegePrice) {
             // 優(yōu)惠金額發(fā)送變化
             weakSelf.privilegeLabel.text = [NSString stringWithFormat:@"優(yōu)惠%.2f 元",[privliegePrice floatValue]];
         }];
         
         [_subDataController setChangeFreightPriceCallBack:^(NSString *freight) {
             [weakSelf.freightLabel setText:[NSString stringWithFormat:@"運(yùn)費(fèi) %@元",freight]];
         }];
         
         [_subDataController loadDataFinishedCallBack:^(NSArray *redPackets,
                                                        NSUInteger redPackCount,
                                                        NSArray *discounts,
                                                        NSUInteger discountCardCount,
                                                        WN_RedpacketModel *redpacketModel,
                                                        WN_DiscountCardModel *discountCardModel,
                                                        NSString *freight) {
             __strong typeof(weakSelf)strongSelf = weakSelf;
             // 配置優(yōu)惠 View(紅包打折卡)
             [strongSelf.privilegeView setRedPackets:redPackets
                                      redpacketCount:redPackCount
                                      redpacketModel:redpacketModel
                                       discountCards:discounts
                                   discountCardCount:discountCardCount
                                   discountCardModel:discountCardModel];
             [UIView animateWithDuration:.3 animations:^{
                 [weakSelf.contentView performSelector:@selector(layoutIfNeeded)
                                            withObject:nil
                                            afterDelay:0.f
                                               inModes:@[NSDefaultRunLoopMode]];
             }];
             if (strongSelf.reloadView) {
                 // 更新 Cell 高度
                 strongSelf.reloadView();
             }
         }];
         _productCountLabel.text = [NSString stringWithFormat:@"共%zd件商品",_subDataController.productCount];
     }
    
  3. 從 TableView中爬坑, 爬坑方法: 取消 Cell 的重用, 自己創(chuàng)建 Cell 緩存, 如果緩存中有指定的 Cell, 直接從緩存中獲取

     - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
         // 店家 Id 唯一, 根據(jù)店家 Id 來緩存 Cell
         NSDictionary *brandProInfo = _brands[indexPath.section - 1];
         // 如果緩存中有指定的 Cell 直接從緩沖中獲取
         if (self.cellCache[brandProInfo[@"brandId"]]) {
             return (UITableViewCell *)self.cellCache[brandProInfo[@"brandId"]];
         } else {
         // 緩存中沒有, 創(chuàng)建并根據(jù)店家 Id 加入緩存池
             WN_ProductInfoCell *productInfoCell = [WN_ProductInfoCell cell];
             WN_SubDataController *dataController = self.primaryDataController.subDataControllers[indexPath.section - 1];
             [productInfoCell setBrandProductsInfo:brandProInfo
                        brandProductDataController:dataController];
             __weak typeof(self)weakSelf = self;
             [productInfoCell setReloadView:^{
                 [weakSelf.tableView performSelector:@selector(reloadData)
                                withObject:nil
                                afterDelay:0.f
                                   inModes:@[NSDefaultRunLoopMode]];
             }];
             self.cellCache[brandProInfo[@"brandId"]] = productInfoCell;
             return productInfoCell;
         }
             
     }
    

這就是這個界面的設(shè)計思想; 能力有限, 如果您有更好的設(shè)計思想歡迎討論??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末甜孤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谈况,更是在濱河造成了極大的恐慌,老刑警劉巖撤防,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件番枚,死亡現(xiàn)場離奇詭異涌庭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)肥哎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門辽俗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人篡诽,你說我怎么就攤上這事崖飘。” “怎么了杈女?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵朱浴,是天一觀的道長。 經(jīng)常有香客問我碧信,道長赊琳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任砰碴,我火速辦了婚禮躏筏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘呈枉。我一直安慰自己趁尼,他們只是感情好埃碱,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著酥泞,像睡著了一般砚殿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上芝囤,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天似炎,我揣著相機(jī)與錄音,去河邊找鬼悯姊。 笑死羡藐,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的悯许。 我是一名探鬼主播仆嗦,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼先壕!你這毒婦竟也來了瘩扼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤垃僚,失蹤者是張志新(化名)和其女友劉穎集绰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冈在,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡倒慧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年按摘,在試婚紗的時候發(fā)現(xiàn)自己被綠了包券。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡炫贤,死狀恐怖溅固,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情兰珍,我是刑警寧澤侍郭,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站掠河,受9級特大地震影響亮元,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜唠摹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一爆捞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧勾拉,春花似錦煮甥、人聲如沸盗温。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卖局。三九已至,卻和暖如春双霍,著一層夾襖步出監(jiān)牢的瞬間砚偶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工洒闸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蟹演,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓顷蟀,卻偏偏與公主長得像酒请,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鸣个,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

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