iOS实现多个可变cell复杂界面的制作

iOS实现多个可变cell复杂界面的制作

大家好,又见面了,我是全栈君。

在日常的开发中,有时会遇到内容块比较多,且又可变的界面:

iOS 实现多个可变 cell 复杂界面的制作

这个界面中有些内容块是固定出现的,比如最上面的商品详情图片、商品名称、价格等。而有些内容块则是不一定出现的,比如促销(显然不是每个商品都有促销)、已选规格(有的商品没有规格)、店铺信息(有的商品属于自营,就没有店铺)等。还有些内容要根据情况进行变化,比如评论,这里最多列出4条评论,如果没有评论,则显示“暂无评论”且不显示“查看所有评论”按钮。

对于这样的界面,相信很多人第一感觉会用TableView来做,因为中间要列出评论内容,这个用TableView的cell来填充比较合适。但如何处理评论内容之外的其他内容呢?我之前的做法是,评论内容之上的用HeaderView做,下面的用FooterView做,虽然最终实现了功能,但做起来十分麻烦。布局我是用Auto Layout来做的,由于Auto Layout本身的特点,代码中就涉及很多判断处理。比如“已选规格”块,最开始的是有一个和“促销”内容块的顶部间距约束,但“促销”内容块不一定会有,就得根据情况,调整“已选规格”块本身的约束(例如让其顶部间距约束指向“价格”内容块)。同样,“促销”内容块本身也需要类似的处理,如果没有“促销”时,要隐藏自己,而隐藏自己最简单的办法,就是将自己的高度约束设置为0(因为它还有底部到“已选规格”的间距约束,不能随意将自身移除,否则“已选规格”相关的约束得再进行调整)。

此外,还有一个麻烦的问题。界面刚进来的时候,是需要请求网络数据,这时界面就要显示成一个初始状态,而显然初始状态有些内容块是不应该显示的,比如促销,只有完成了数据请求,才能知道是否有促销,有的话才显示促销内容;比如评论,初始时应该显示成“暂无评论”,数据请求完成后,才显示相应的内容。这样,我们需要处理初始进入和数据请求完成两种状态下各个内容块的显示,十分复杂繁琐。

总结来说,用TableView的 HeaderView + 评论内容cell + FooterView + Auto Layout 的方式会带来如下问题:

  1. 约束本身需要依赖其他View的,而所依赖的View又是可变的内容块,会导致约束需要繁琐的判断修改增删
  2. 需要处理初始进入和数据请求完成两种状态的界面展示,使代码更加复杂繁琐
  3. 需要额外计算相应内容的高度,以更新HeaderView、FooterView的高度

可见,这种方式并不是理想的解决方案。可能有人会说,那不要用Auto Layout,直接操作frame来布局就好,这样或许能减少一些麻烦,但总体上并没有减少复杂度。也有人说,直接用ScrollView来做,这样的话,所有的内容包括评论内容的cell,都得自己手动拼接,可以想象这种做法也是比较麻烦的。所以,我们得另辟蹊径,使用其他方法来达到目的。下面就为大家介绍一种比较简便的做法,这种做法也是一个前同事分享给我的,我就借花献佛,分享给大家。

我们还是用TableView来做这个界面,和之前不同的是,我们把每一个可变内容块做成一个独立的cell,cell的粒度可以自行控制,比如可以用一个cell囊括商品图片、标题、副标题、价格,也可以拆得更细,图片、标题、副标题、价格都各自对应一个cell。这里我们选择后者,因为图片内容块,我们需要按屏幕宽度等比例拉伸;标题、副标题的文字内容可能是一行,也可能是两行,高度可变,用单独的cell来控制会更简单明了。

下面先定义好各种类型的cell:


  1. //基础cell,这里为了演示简便,定义这个cell,其他cell继承自这个cell 
  2.  
  3. @interface MultipleVariantBasicTableViewCell : UITableViewCell 
  4.  
  5. @property (nonatomic, weak) UILabel *titleTextLabel; 
  6.  
  7. @end 
  8.  
  9.   
  10.  
  11. //滚动图片 
  12.  
  13. @interface CycleImagesTableViewCell : MultipleVariantBasicTableViewCell 
  14.  
  15. @end 
  16.  
  17.   
  18.  
  19. //正标题 
  20.  
  21. @interface MainTitleTableViewCell : MultipleVariantBasicTableViewCell 
  22.  
  23. @end 
  24.  
  25.   
  26.  
  27. //副标题 
  28.  
  29. @interface SubTitleTableViewCell : MultipleVariantBasicTableViewCell 
  30.  
  31. @end 
  32.  
  33.   
  34.  
  35. //价格 
  36.  
  37. @interface PriceTableViewCell : MultipleVariantBasicTableViewCell 
  38.  
  39. @end 
  40.  
  41.   
  42.  
  43. // …其他内容块的cell声明 
  44.  
  45.   
  46.  
  47.   
  48.  
  49. // 各种内容块cell的实现,这里为了演示简便,cell中就只放了一个Label 
  50.  
  51. @implementation MultipleVariantBasicTableViewCell 
  52.  
  53.   
  54.  
  55. – (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { 
  56.  
  57.     self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 
  58.  
  59.     if (self) { 
  60.  
  61.         UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 44)]; 
  62.  
  63.         label.numberOfLines = 0; 
  64.  
  65.         [self.contentView addSubview:label]; 
  66.  
  67.         self.titleTextLabel = label; 
  68.  
  69.     } 
  70.  
  71.     return self; 
  72.  
  73.  
  74.   
  75.  
  76. @end 
  77.  
  78.   
  79.  
  80. @implementation CycleImagesTableViewCell 
  81.  
  82. @end 
  83.  
  84.   
  85.  
  86. @implementation MainTitleTableViewCell 
  87.  
  88. @end 
  89.  
  90.   
  91.  
  92. // …其他内容块的cell实现 
  93.  
  94.   
  95.  
  96. // 评论内容cell使用Auto Layout,配合iOS 8 TableView的自动算高,实现内容自适应 
  97.  
  98. @implementation CommentContentTableViewCell 
  99.  
  100.   
  101.  
  102. – (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { 
  103.  
  104.     self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 
  105.  
  106.     if (self) { 
  107.  
  108.         self.titleTextLabel.translatesAutoresizingMaskIntoConstraints = NO
  109.  
  110.         self.titleTextLabel.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width – 8; 
  111.  
  112.         NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:self.titleTextLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeLeading multiplier:1.0f constant:4.0f]; 
  113.  
  114.         NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:self.titleTextLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeTrailing multiplier:1.0f constant:-4.0f]; 
  115.  
  116.         NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self.titleTextLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeTop multiplier:1.0f constant:4.0f]; 
  117.  
  118.         NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:self.titleTextLabel attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeBottom multiplier:1.0f constant:-4.0f]; 
  119.  
  120.         [self.contentView addConstraints:@[leftConstraint, rightConstraint, topConstraint, bottomConstraint]]; 
  121.  
  122.     } 
  123.  
  124.     return self; 
  125.  
  126.  
  127.   
  128.  
  129. @end 

接下来就是重点,就是如何来控制显示哪些cell及cell显示的数量。这一步如果处理不好,也会使开发变得复杂。如下面的方式:


  1. // 加载完数据 
  2.  
  3. self.cellCount = 0; 
  4.  
  5. if (存在促销) { 
  6.  
  7.     self.cellCount++; 
  8.  
  9.  
  10. if (存在规格) { 
  11.  
  12.     self.cellCount++; 
  13.  
  14.  
  15. …… 

如果以这种方式来记录cell的数量,那么后续cell的展示、点击判断等都会很麻烦。这里我们采用的方式是,使用单独的类(作为一种数据结构)来保存所要展示的cell信息。


  1. // SKRow.h 
  2.  
  3. @interface SKRow : NSObject 
  4.  
  5.   
  6.  
  7. @property (nonatomic, copy) NSString *cellIdentifier; 
  8.  
  9. @property (nonatomic, strong) id data; 
  10.  
  11. @property (nonatomic, assign) float rowHeight; 
  12.  
  13.   
  14.  
  15. – (instancetype)initWithCellIdentifier:(NSString *)cellIdentifier 
  16.  
  17.                                   data:(id)data 
  18.  
  19.                              rowHeight:(float)rowHeight; 
  20.  
  21.   
  22.  
  23. @end 
  24.  
  25.   
  26.  
  27. // SKRow.m 
  28.  
  29. #import “SKRow.h” 
  30.  
  31.   
  32.  
  33. @implementation SKRow 
  34.  
  35.   
  36.  
  37. – (instancetype)initWithCellIdentifier:(NSString *)cellIdentifier data:(id)data rowHeight:(float)rowHeight { 
  38.  
  39.     if (self = [super init]) { 
  40.  
  41.         self.cellIdentifier = cellIdentifier; 
  42.  
  43.         self.data = data; 
  44.  
  45.         self.rowHeight = rowHeight; 
  46.  
  47.     } 
  48.  
  49.     return self; 
  50.  
  51.  
  52.   
  53.  
  54. @end  

SKRow用来存储每个cell所需的信息,包括重用标识、数据项、高度。接下来,我们就开始拼接cell信息。


  1. @interface ViewController () 
  2.  
  3. @property (nonatomic, strong) NSMutableArray *> *tableSections; 
  4.  
  5. @end 
  6.  
  7.  
  8. self.tableSections = [NSMutableArray array]; 
  9.  
  10.   
  11.  
  12. /* 初始加载数据 
  13.  
  14. * 初始化时,只显示滚动图片、价格、评论头、无评论 
  15.  
  16. */ 
  17.  
  18. // 滚动图片(宽高保持比例) 
  19.  
  20. SKRow *cycleImagesRow = [[SKRow alloc] initWithCellIdentifier:@“CycleImagesCellIdentifier” data:@[@“滚动图片地址”] rowHeight:120*[UIScreen mainScreen].bounds.size.width / 320.f]; 
  21.  
  22. // 价格 
  23.  
  24. SKRow *priceRow = [[SKRow alloc] initWithCellIdentifier:@“PriceCellIdentifier” data:@“0” rowHeight:44]; 
  25.  
  26. [self.tableSections addObject:@[cycleImagesRow, priceRow]]; 
  27.  
  28. // 评论头 
  29.  
  30. SKRow *commentSummaryRow = [[SKRow alloc] initWithCellIdentifier:@“CommentSummaryCellIdentifier” data:@{@“title”:@“商品评价”, @“count”:@“0”} rowHeight:44]; 
  31.  
  32. // 无评论 
  33.  
  34. SKRow *noCommentRow = [[SKRow alloc] initWithCellIdentifier:@“NoCommentCellIdentifier” data:@“暂无评论” rowHeight:44]; 
  35.  
  36. [self.tableSections addObject:@[commentSummaryRow, noCommentRow]]; 

以上是初始状态时要显示的cell,我们在ViewController中声明一个数组,用来存储TableView各个section要显示的cell信息。这里我们将cell分成不同的section,实际中,要不要分,分成几个section都可以自行决定。初始状态我们有两个section,第一个section用于显示基本信息,第二个section用于显示评论信息,这样就完成了cell信息的拼接,接下来就是显示:


  1. – (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
  2.  
  3.     // 这里可以通过判断cellIdentifier来区分处理各种不同的cell,cell所需的数据从row.data上获取 
  4.  
  5.   
  6.  
  7.     SKRow *row = self.tableSections[indexPath.section][indexPath.row]; 
  8.  
  9.     if ([row.cellIdentifier isEqualToString:@“CycleImagesCellIdentifier”]) { 
  10.  
  11.         CycleImagesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifier forIndexPath:indexPath]; 
  12.  
  13.         NSArray *urlStringArray = row.data; 
  14.  
  15.         cell.titleTextLabel.text = [urlStringArray componentsJoinedByString:@“\n”]; 
  16.  
  17.         return cell; 
  18.  
  19.     } else if ([row.cellIdentifier isEqualToString:@“MainTitleCellIdentifier”]) { 
  20.  
  21.         MainTitleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifier forIndexPath:indexPath]; 
  22.  
  23.         cell.titleTextLabel.text = row.data; 
  24.  
  25.         return cell; 
  26.  
  27.     } else if ([row.cellIdentifier isEqualToString:@“PriceCellIdentifier”]) { 
  28.  
  29.         PriceTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifier forIndexPath:indexPath]; 
  30.  
  31.         cell.titleTextLabel.text = [NSString stringWithFormat:@“¥%@”, row.data]; 
  32.  
  33.         return cell; 
  34.  
  35.     } else if ([row.cellIdentifier isEqualToString:@“SalePromotionCellIdentifier”]) { 
  36.  
  37.         SalePromotionTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifier forIndexPath:indexPath]; 
  38.  
  39.         NSArray *salePromotionStringArray = row.data; 
  40.  
  41.         cell.titleTextLabel.text = [salePromotionStringArray componentsJoinedByString:@“\n”]; 
  42.  
  43.         return cell; 
  44.  
  45.     } else if ([row.cellIdentifier isEqualToString:@“SpecificationCellIdentifier”]) { 
  46.  
  47.         SpecificationTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifier forIndexPath:indexPath]; 
  48.  
  49.         cell.titleTextLabel.text = [NSString stringWithFormat:@“已选:%@”, row.data]; 
  50.  
  51.         return cell; 
  52.  
  53.     } else if ([row.cellIdentifier isEqualToString:@“CommentSummaryCellIdentifier”]) { 
  54.  
  55.         CommentSummaryTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifier forIndexPath:indexPath]; 
  56.  
  57.         NSDictionary *commentSummary = row.data; 
  58.  
  59.         cell.titleTextLabel.text = [NSString stringWithFormat:@“%@(%@)”, commentSummary[@“title”], commentSummary[@“count”]]; 
  60.  
  61.         return cell; 
  62.  
  63.     } else if ([row.cellIdentifier isEqualToString:@“CommentContentCellIdentifier”]) { 
  64.  
  65.         CommentContentTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifier forIndexPath:indexPath]; 
  66.  
  67.         cell.titleTextLabel.text = row.data; 
  68.  
  69.         return cell; 
  70.  
  71.     } else if ([row.cellIdentifier isEqualToString:@“AllCommentCellIdentifier”]) { 
  72.  
  73.         AllCommentTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifier forIndexPath:indexPath]; 
  74.  
  75.         cell.titleTextLabel.text = row.data; 
  76.  
  77.         return cell; 
  78.  
  79.     } else if ([row.cellIdentifier isEqualToString:@“NoCommentCellIdentifier”]) { 
  80.  
  81.         NoCommentTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:row.cellIdentifier forIndexPath:indexPath]; 
  82.  
  83.         cell.titleTextLabel.text = row.data; 
  84.  
  85.         return cell; 
  86.  
  87.     } 
  88.  
  89.     return nil; 
  90.  

上面的代码进行了删减,没有处理所有类型。虽然稍嫌冗长,但是逻辑非常简单,就是获取cell信息,根据重用标识来区分不同类型的内容块,将数据处理后放到cell中展示。

例如,对于商品图片,因为是滚动图片,滚动图片可以有多张,前面我们传入的数据就是数组data:@[@”滚动图片地址”]。后面获取到数据后,cell.titleTextLabel.text = [urlStringArray componentsJoinedByString:@”\n”];,出于演示,商品图片cell我们只放了一个Label,所以只是简单的将地址信息分行显示出来。在实际的开发中,可以放入一个图片滚动显示控件,并将图片地址的数组数据传给控件展示。

其他类型的cell处理也是大同小异,出于演示的原因,都只是简单的数据处理展示。当然,别忘了,设置一下TableView相关的dataSource和delegate:


  1. – (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 
  2.  
  3.     SKRow *row = self.tableSections[indexPath.section][indexPath.row]; 
  4.  
  5.     return row.rowHeight; 
  6.  
  7.  
  8.   
  9.  
  10. – (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 
  11.  
  12.     return self.tableSections.count
  13.  
  14.  
  15.   
  16.  
  17. – (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
  18.  
  19.     return self.tableSections[section].count
  20.  

这样我们就完成了初始状态时界面的展示

iOS实现多个可变cell复杂界面的制作

完成了cell的显示处理,接下来我们来模拟一下网络请求数据后,界面如何显示所需的cell


  1. self.tableSections = [NSMutableArray array]; 
  2.  
  3.   
  4.  
  5. NSMutableArray *section1 = [NSMutableArray array]; 
  6.  
  7. // 滚动图片(宽高保持比例) 
  8.  
  9. SKRow *cycleImagesRow = [[SKRow alloc] initWithCellIdentifier:@“CycleImagesCellIdentifier” data:@[@“滚动图片地址1”, @“滚动图片地址2”, @“滚动图片地址3”] rowHeight:120*[UIScreen mainScreen].bounds.size.width / 320.f]; 
  10.  
  11. // 主标题 
  12.  
  13. SKRow *mainTitleRow = [[SKRow alloc] initWithCellIdentifier:@“MainTitleCellIdentifier” data:@“商品名称” rowHeight:44]; 
  14.  
  15. // 副标题 
  16.  
  17. SKRow *subTitleRow = [[SKRow alloc] initWithCellIdentifier:@“SubTitleCellIdentifier” data:@“节日促销,快来买啊” rowHeight:44]; 
  18.  
  19. // 价格 
  20.  
  21. SKRow *priceRow = [[SKRow alloc] initWithCellIdentifier:@“PriceCellIdentifier” data:@(arc4random()) rowHeight:44]; 
  22.  
  23. [section1 addObjectsFromArray:@[cycleImagesRow, mainTitleRow, subTitleRow, priceRow]]; 
  24.  
  25. // 促销(随机出现) 
  26.  
  27. if (arc4random() % 2 == 0) { 
  28.  
  29.     SKRow *salePromotionRow = [[SKRow alloc] initWithCellIdentifier:@“SalePromotionCellIdentifier” data:@[@“促销信息1”, @“促销信息2”, @“促销信息3”] rowHeight:44]; 
  30.  
  31.     [section1 addObject:salePromotionRow]; 
  32.  
  33.  
  34. [self.tableSections addObject:section1]; 
  35.  
  36.   
  37.  
  38. NSMutableArray *section2 = [NSMutableArray array]; 
  39.  
  40. // 规格(随机出现) 
  41.  
  42. if (arc4random() % 2 == 0) { 
  43.  
  44.     SKRow *specificationRow = [[SKRow alloc] initWithCellIdentifier:@“SpecificationCellIdentifier” data:@“银色,13.3英寸” rowHeight:44]; 
  45.  
  46.     [section2 addObject:specificationRow]; 
  47.  
  48.  
  49. if (section2.count > 0) { 
  50.  
  51.     [self.tableSections addObject:section2]; 
  52.  
  53.  
  54.   
  55.  
  56. NSMutableArray *section3 = [NSMutableArray array]; 
  57.  
  58. NSArray *commentArray = [NSMutableArray array]; 
  59.  
  60. // 评论内容数据(随机出现) 
  61.  
  62. if (arc4random() % 2 == 0) { 
  63.  
  64.     commentArray = @[@“评论内容1”, @“评论内容2”, @“2016年6月,苹果系统iOS 10正式亮相,苹果为iOS 10带来了十大项更新。2016年6月13日,苹果开发者大会WWDC在旧金山召开,会议宣布iOS 10的测试版在2016年夏天推出,正式版将在秋季发布。2016年9月7日,苹果发布iOS 10。iOS10正式版于9月13日(北京时间9月14日凌晨一点)全面推送。”, @“评论内容4”]; 
  65.  
  66.  
  67. // 评论头 
  68.  
  69. SKRow *commentSummaryRow = [[SKRow alloc] initWithCellIdentifier:@“CommentSummaryCellIdentifier” data:@{@“title”:@“商品评价”, @“count”:@(commentArray.count)} rowHeight:44]; 
  70.  
  71. [section3 addObject:commentSummaryRow]; 
  72.  
  73. if (commentArray.count > 0) { 
  74.  
  75.     for (NSString *commentString in commentArray) { 
  76.  
  77.         // 评论内容需要自适应高度,高度值指定为UITableViewAutomaticDimension 
  78.  
  79.         SKRow *commentContentRow = [[SKRow alloc] initWithCellIdentifier:@“CommentContentCellIdentifier” data:commentString rowHeight:UITableViewAutomaticDimension]; 
  80.  
  81.         [section3 addObject:commentContentRow]; 
  82.  
  83.     } 
  84.  
  85.     // 查看所有评论 
  86.  
  87.     SKRow *allCommentRow = [[SKRow alloc] initWithCellIdentifier:@“AllCommentCellIdentifier” data:@“查看所有评论” rowHeight:44]; 
  88.  
  89.     [section3 addObject:allCommentRow]; 
  90.  
  91. else { 
  92.  
  93.     // 无评论 
  94.  
  95.     SKRow *noCommentRow = [[SKRow alloc] initWithCellIdentifier:@“NoCommentCellIdentifier” data:@“暂无评论” rowHeight:44]; 
  96.  
  97.     [section3 addObject:noCommentRow]; 
  98.  
  99.  
  100. [self.tableSections addObject:section3]; 
  101.  
  102.   
  103.  
  104. [self.tableView reloadData]; 

上面的代码同样比较冗长,但逻辑也同样十分简单。按显示顺序拼凑cell数据,有些不一定显示的内容块,如促销,则随机判断,如果显示,将数据加入到section数组中[section1 addObject:salePromotionRow];。其他类型的cell也是类似的,不再赘述。要注意的是,评论内容的文本可能有多行,我们将它的cell高设置为UITableViewAutomaticDimension:

[[SKRow alloc] initWithCellIdentifier:@”CommentContentCellIdentifier” data:commentString rowHeight:UITableViewAutomaticDimension];

由于评论内容cell我们使用了Auto Layout,这样就可以利用iOS 8 TableView的新特性,自动计算cell的高度。拼接完数据后,只要调用[self.tableView reloadData];让TableView重新加载即可。

好了,这样就大功告成

iOS实现多个可变cell复杂界面的制作

最终效果

使用上述方式制作这种内容块可变的界面虽然写起来较为啰嗦,但有如下优点:

  1. 逻辑清晰简单,易于理解,视图间不存在像先前HeaderView + Auto Layout + FooterView那种复杂的依赖,内容块的显示与否处理非常简便
  2. 易于调整。例如调换内容块的顺序,只要移动下拼凑cell数据的代码顺序即可
  3. 易于扩展增加新的内容块。要增加新的内容块,只需创建新的cell,在数据拼接时,增加拼接新cell类型的数据代码,同样在显示的地方增加显示新cell类型的代码即可,几乎不需要修改原有的逻辑

最后,附上Demo工程代码(https://github.com/kelystor/MultipleVariantCell)。注意,这个工程是用XCode 8创建的,低版本的XCode可能运行会有问题(XCode 8的storyboard默认好像不兼容老版本),示例是基于iOS 8,如果要兼容老版本,请自行修改(主要是涉及cell自动算高的部分)。





本文作者:佚名
来源:51CTO
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/108272.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • HONOR荣耀50/荣耀50Pro怎么解锁huawei 荣耀50pro屏幕锁开机锁激活设备锁了应该如何强制解除鸿蒙系统刷机解锁方法流程步骤不开机跳过锁屏移除锁定进系统方法经验

    HONOR荣耀50/荣耀50Pro怎么解锁huawei 荣耀50pro屏幕锁开机锁激活设备锁了应该如何强制解除鸿蒙系统刷机解锁方法流程步骤不开机跳过锁屏移除锁定进系统方法经验今天带来一台用户华为荣耀50手机强制清除华为账号锁案例分享,这个台手机是用户公司手机,由于前使用者离职后未能退出手机的华为账号和锁屏密码,导致手机无法使用。自己通过简单的恢复出厂设置后,发现手机有华为账号锁无法激活手机,这才联系到刷机爱好者技术人员,给予远程强制刷机移除华为荣耀60的账号锁。在此提醒广大用户,登录的华为账号建议绑定经常使用的手机号码,防止无法找回密码从而到时手机无法使用。在刷机解锁过程中需要准备以下工具:链接:百度网盘请输入提取码提取码:8888备用下载连接:yun.p

  • python截图识别文字_python截图并转换文字「建议收藏」

    python截图识别文字_python截图并转换文字「建议收藏」截图识别文字作者万开国[acewan]【摘要】本文主要介绍了使用pyHook、pythoncom、pytesseract、PIL、win32api等module实现python的截图识别文字功能。【正文】一准备及介绍1.pyhookpyHook通过pip直接安装比较困难,可以先下载whl文件再使用pip安装选择与python环境一致的文件下载,使用cmd导向到下载文件夹,执行安装即可其他modul…

  • 安卓ExpandableListView的详细使用教程(附代码解析过程)

    安卓ExpandableListView的详细使用教程(附代码解析过程)ExpandableListView又称可扩展的ListView,它可以实现点击父项展开子项的效果,本文实现了一个比较精美的ExpandableListView。

  • android错误之android.content.res.Resources$NotFoundException:

    错误:android.content.res.Resources$NotFoundException: String resource ID #0x1原因:一般发生在参数 int resId 错误,你把String赋值给int的resId,所以编译器找不到正确的resource于是报错。最简单的例子,检查一下你的Toast.makeText()啊textView.setText啊之类的函数

  • 婴儿照片大全可爱萌萌_好萌图片

    婴儿照片大全可爱萌萌_好萌图片萌翻了!这是一组可爱的婴儿照片,你可以从中学习到优秀的婴儿照片拍摄技术。就像我以前说的,婴儿总是持续在运动,因此很难找到一个好的角度。拍摄的时候,试着和婴儿保持同一水平,使用长de焦距。特别在最初几

  • 虚拟GPU_vmware gpu

    虚拟GPU_vmware gpu本系列文章推送门:阿里云郑晓:浅谈GPU虚拟化技术(第一章)GPU虚拟化发展史阿里云郑晓:浅谈GPU虚拟化技术(第二章)GPU虚拟化方案之——GPU直通模式今天一个小伙伴@我说:“你浅谈一下,没点技术背景的,估计都看不懂…”,醍醐灌顶啊,面向公众的文章不是学术论文,应以普及基本概念为主。所以我决定在接下来的文章力求写的让吃瓜群众能看懂,专业人士能读完也会有很大感触和启迪。至于技术细节,大致…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号