UITableView记录

有时候 TableView 我们可能只会需要他的动态性(数据驱动)而 Cell 重用可能会导致问题

面对下面的界面我们该如何去做 ??

确认订单_1_.jpg

界面描述:

  1. 这是一个关于商品的界面, 商品依据店家来分类, 所有如果使用 tableView我们可以设置 Session 为店家的个数
  2. 商店下的商品个数不确定, 可变, 所以我选择再次嵌套一层 TableView, 这个 TableView 就只显示商品

所以界面拆分如下:

确认订单_1_.jpg

界面拆分完成, 接下来应该梳理下数据的问题, 界面流程如下:

  1. 程序进入到该界面, 从前一个界面中传递的信息中获得所有商品的价格, 计算总价格, 推出整个页面底部的合计金额
  2. 主界面请求网络获得用户的地址, 用户账号下的礼品卡余额
  3. 每个商店根据自己的商店信息发起网络请求, 请求可以使用的优惠方式(红包, 打折卡)
  4. 请求获得每个商店的优惠, 更新 UI, 将不包含的优惠从界面中移除
  5. 当用户在商家下选择任意或取消一种优惠时, 商家底部的实付要做相应的更新, 同时底部的总价要做相应的更新
  6. 当选择好商家优惠, 用户再次选择余额或礼品卡同样样做相应的 UI 及数据更新, 防止总价小于优惠的价格导致总价出现负数的情况
  7. 项目后期拆分订单被取消, 替代的是商家的运费, 不同的商家要请求获得运费, 这个请求的运费也会影响商家下的实付金额, 界面下的合计金额

根据上面的流程, 我做如下的设计:

  1. 整个 ViewController 的数据由一个 PrimaryDataController 控制
  2. 每个商店的数据由一个 SubDataController 控制
  3. PrimaryDataController 有个数组包含 SubDataController, 有多少商店就有多少 SubDataController 对象
  4. 每个蓝色区域的 Cell 绑定一个 SubDataController(注意这是个埋坑点)
  5. 因为各个不同的店家的优惠要分别异步请求, 请求结束后要刷新界面, 我们还要监听店家优惠金额的改变, 监听变化有几种可选措施: (1). KVO, 通知 (2). 代理, (3). Block; 首先 KVO, 通知可以放弃, 因为他们是一对多的关系, 更改优惠方法发出一条通知, 而整个界面所有的蓝色 Cell 都是监听者, 这样无法确定哪一个应该发生相应的更改. 代理, Block 的回调触发都是一对一的, 这两种作为可选操作, 这里我选择的是 Block
  6. 因为第5步我选择的 Block 触发回调, 看第四步有坑, 这里的坑是指蓝色 Cell 指定相同的Identifier, 所以蓝色 Cell 是可以重用的, 如果一重用, SubDataController 的回调绑定关系就会产生问题, 这个地方是我掉的一个坑, 还有如果店家的优惠信息请求结束要相应的对优惠进行显示和隐藏, 就是要更新蓝色 Cell 的高度, 这样难免调用 TableView 的 reloadData, 如果一调用 reloadData, 界面就会重新赋值, 所以会面临一个问题, 有多个店家, 假设店家优惠请求结束是依次结束, 这样当用户下滑时, 第一个蓝色 Cell 进入重用池, 然后再从重用池中取出显示另一个店家商品信息, 这样第一个店家优惠信息请求结束实际是在这个显示这个店家商品信息刷新, 而且会一个接一个结束, 不停地 reloadData, 导致问题

具体从代码中了解如何解决:
首先创建 PrimaryDataController

#import <Foundation/Foundation.h>

@interface WN_PrimaryDataController : NSObject

@property (nonatomic, strong) NSDictionary *shoppingCartsInfo;

/**
 *  每个品牌 Section 的数据控制器
 */
@property (nonatomic, strong) NSArray *subDataControllers;

/**
 *  确认订单中总价钱
 */
@property (nonatomic, strong) NSDecimalNumber *totalPrice;

/**
 *  除去优惠方法的商品总价
 */
@property (nonatomic, strong, readonly) NSDecimalNumber *originalTotalPrice;

/**
 *  优惠价格
 */
@property (nonatomic, strong) NSDecimalNumber *privilegePrice;

/**
 *  更改总价后触发 View 中的回调
 */
@property (nonatomic, copy) void (^changeTotalPriceCallBack)(NSDecimalNumber *totalPrice);

/**
 *  确认订单中商品总数
 */
@property (nonatomic, assign, readonly) NSUInteger productCount;

/**
 *  用户输入的使用的礼品卡金额
 */
@property (nonatomic, copy) NSString *giftCardValue;
/**
 *  总的礼品卡余额
 */
@property (nonatomic, copy, readonly) NSString *totalGiftCardValue;
/**
 *  用户输入的使用的用户余额
 */
@property (nonatomic, copy) NSString *balanceValue;

/**
 *  运费
 */
@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)();

/**
 *  某一个品牌下添加红包或者打折卡导致总优惠发送变化
 *
 *  @param price 某个品牌选择红包或者打折卡产生的优惠金额
 */
- (void)addPrivilegePrice:(NSDecimalNumber *)price;

/**
 *  某个品牌下移除红包或者打折卡导致总优发生变化
 *
 *  @param price 某个品牌选择红包或者打折卡产生的优惠金额
 */
- (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;

/**
 *  优惠价格
 */
@property (nonatomic, strong, readonly) NSDecimalNumber *privilegePrice;

/**
 *  更改总价后触发 View 中的回调
 */
@property (nonatomic, copy) void (^changeTotalPriceCallBack)(NSDecimalNumber *totalPrice);

/**
 *  优惠更改后触发 View 中的回调
 */
@property (nonatomic, copy) void (^changePrivilegePriceCallBack)(NSDecimalNumber *privilegePrice);


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

/**
 *  更改优惠后 PrimaryDataController 中的回调
 */
@property (nonatomic, copy) void (^changePrivilegePricePrimaryDataControllerCallBack)(NSDecimalNumber *privilegePrice);

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

/**
 *  设置该品牌下所有的商品信息
 */
- (void)setCartItemInfo:(NSDictionary *)dic;

/**
 * 该品牌下商品的总数
 */
@property (nonatomic, assign, readonly) NSInteger productCount;

/**
 *  可用红包数组
 */
@property (nonatomic, strong, readonly) NSArray *redPackets;
/**
 *  可用红包个数
 */
@property (nonatomic, assign, readonly) NSUInteger validityRedPacketsCount;

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

/**
 *  可用打折卡数组
 */
@property (nonatomic, strong, readonly) NSArray *discountCards;

/**
 *  可用打折卡个数
 */
@property (nonatomic, assign, readonly) NSUInteger validityDiscountCardCount;

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

/**
 *  发票信息
 */
@property (nonatomic, copy) NSDictionary *invoiceInfoDictionary;

/**
 *  运费
 */
@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);


/**
 *  根据品牌价值完红包打折卡后的回调
 *  @param callBack     根据品牌价值完红包打折卡后的回调
 *  redPackets 红包数组
 *  redPackCount 红包个数
 *  discounts 打折卡数组
 *  discountCardCount 打折卡个数
 *  redpacketModel 与本品牌关联的红包模型
 *  discountCardModel 与本品牌关联的打折卡模型
 *  freight 运费
 */
- (void)loadDataFinishedCallBack:(void (^)(NSArray *redPackets,
                                           NSUInteger redPackCount,
                                           NSArray *discounts,
                                           NSUInteger discountCardCount,
                                           WN_RedpacketModel *redpacketModel,
                                           WN_DiscountCardModel *discountCardModel,
                                           NSString *freight)) callBack;

@end

TableViewController 中:

  1. 根据数据创建 PrimaryDataController, SubDataController:
    - (WN_PrimaryDataController *)primaryDataController {
        if (!_primaryDataController) {
            // 创建主数据控制器
            _primaryDataController = [[WN_PrimaryDataController alloc] init];
            NSMutableArray *subDataControllers = [NSMutableArray arrayWithCapacity:_brands.count];
            __weak typeof(self)weakSelf = self;
            __weak typeof(_primaryDataController)weakPrimayDataController = _primaryDataController;
            
            // 根据每个店家的数据创建店家的数据控制器
            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. 蓝色 Cell

     #import <UIKit/UIKit.h>
     @class WN_SubDataController, WN_PrimaryDataController;
     @interface WN_ProductInfoCell : UITableViewCell
     @property (nonatomic, strong, readonly) NSDictionary *info;
     // 对应一个数据控制器
     @property (nonatomic, strong, readonly) WN_SubDataController *subDataController;
     @property (nonatomic, copy) void (^reloadView)();
     
     /**
      *  设置品牌商品信息, 配置一个数据控制类
      *
      *  @param info           商品信息
      *  @param dataController 数据控制类
      */
     - (void)setBrandProductsInfo:(NSDictionary *)info brandProductDataController:(WN_SubDataController *)dataController;
     
     + (instancetype)cell;
     
     @end
    
  2. 蓝色 Cell 绑定子数据控制器, 并绑定数据回调

     - (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) {
             // 运费获得回调
             weakSelf.freightMaskView.hidden = loading ? NO: YES;
         }];
         
         [_subDataController setChangeTotalPriceCallBack:^(NSDecimalNumber *totalPrice) {
             // 总价发生变化回调 
             weakSelf.totalPriceLabel.text = [NSString stringWithFormat:@"%.2f",[totalPrice floatValue]];
         }];
         
         [_subDataController setChangePrivilegePriceCallBack:^(NSDecimalNumber *privliegePrice) {
             // 优惠金额发送变化
             weakSelf.privilegeLabel.text = [NSString stringWithFormat:@"优惠%.2f 元",[privliegePrice floatValue]];
         }];
         
         [_subDataController setChangeFreightPriceCallBack:^(NSString *freight) {
             [weakSelf.freightLabel setText:[NSString stringWithFormat:@"运费 %@元",freight]];
         }];
         
         [_subDataController loadDataFinishedCallBack:^(NSArray *redPackets,
                                                        NSUInteger redPackCount,
                                                        NSArray *discounts,
                                                        NSUInteger discountCardCount,
                                                        WN_RedpacketModel *redpacketModel,
                                                        WN_DiscountCardModel *discountCardModel,
                                                        NSString *freight) {
             __strong typeof(weakSelf)strongSelf = weakSelf;
             // 配置优惠 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 的重用, 自己创建 Cell 缓存, 如果缓存中有指定的 Cell, 直接从缓存中获取

     - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
         // 店家 Id 唯一, 根据店家 Id 来缓存 Cell
         NSDictionary *brandProInfo = _brands[indexPath.section - 1];
         // 如果缓存中有指定的 Cell 直接从缓冲中获取
         if (self.cellCache[brandProInfo[@"brandId"]]) {
             return (UITableViewCell *)self.cellCache[brandProInfo[@"brandId"]];
         } else {
         // 缓存中没有, 创建并根据店家 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;
         }
             
     }
    

这就是这个界面的设计思想; 能力有限, 如果您有更好的设计思想欢迎讨论😄

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,961评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,444评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,009评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,082评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,101评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,271评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,738评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,395评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,539评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,434评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,481评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,160评论 3 317
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,749评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,816评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,038评论 1 256
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,548评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,140评论 2 341

推荐阅读更多精彩内容