UITableView - 平滑展开和折叠显示 UICollectionView MyAttributeTableViewController.hMyAttributeTableViewController.m

问题描述

我正在开发应用中的一项功能用户可以在其中点击有关汽车的某些属性(例如其颜色),然后会显示不同颜色的列表。

这只是页面的一部分。整个页面由不同的视图和视图控制器组成,所有视图和视图控制器都包裹在一个垂直堆栈视图中,但我认为这不是导致动画问题的原因。这是页面的整体结构:

  • UIScrollView
    • UIStackView
      • UIViewController
      • UIView
      • UITableViewController
      • ...

但是对于这个问题,我只是展示了这个表格所在的页面部分。

所以我在表格的动画方面遇到了问题,稍后可以在 gif 中看到。该部分的结构方式是它是一个 UITableViewController 子类,它接收模型(标题和汽车颜色列表),并为每个模型元素显示不同的表格部分。

每个部分都有一个部分标题,这是用户点击的部分。它显示颜色并显示预览图像。当它被点击时,一个表格行被添加到该部分的表格中,并且该更改是动画的。添加到表格中的表格视图行包含一个 UICollectionView,用于水平布置内容。这个想法来自WWDC 2010 - Mastering Table Views视频。

基本上是这样的。每个部分标题都是交互式部分,您可以点击它。然后该部分中显示的行有一个集合视图。

* SectionHeader
* SectionHeader
  * Table row containing a UICollectionView
* SectionHeader

我遇到的问题是动画的行为方式很奇怪,如下图所示。

第一张图片显示了如果展开一行下面有其他行会发生什么。在这种情况下,表格中的其他两行似乎基本上浮动在动画中的新行的内容之上。此外,在该部分的底部,这两行从底部进入,就像前一行一样两行被垂直压扁,直到消失。

Top row animation

此处的第二个动画显示底部行的展开。这个更接近我想要的,除了行内容仍然短暂显示分隔线上方的行顶部(这是上一节的节脚)。

Bottom row animation

这是我的表视图控制器类的代码

MyAttributeTableViewController.h

#import <UIKit/UIKit.h>
#import "MyAttribute.h"
#import "MyAttributeTableViewCell.h"

NS_ASSUME_NONNULL_BEGIN

@interface MyAttributeTableViewController : UITableViewController<MyAttributeDelegate>

//this represents each section of the table. It contains a title (color,for example),a selected value (red),//and a list of possible values that will be used to display the collection view when the section is expanded.
@property (strong,nonatomic) NSArray<MyAttribute *> *modelattributes;

@end

NS_ASSUME_NONNULL_END

MyAttributeTableViewController.m

#import "MyAttributeTableViewController.h"
#import "MyAttributeTableHeaderView.h"
#import "MyAttributeTableViewCell.h"

@interface MyAttributeTableViewController ()

//this keeps track of which sections are expanded. Initially,all sections start out
//not expanded. Then,upon tap,a section's expanded status is toggled here. This is
//used by the table view's data source to kNow when to display a row in a section.
@property (strong,nonatomic) NSMutableArray<NSNumber *> *itemsExpanded;

@end

@implementation MyAttributeTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView.rowHeight = UITableViewAutomaticDimension;
    self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;
    self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension;
    self.tableView.estimatedSectionHeaderHeight = UITableViewAutomaticDimension;
    
    self.tableView.estimatedSectionFooterHeight = CGFLOAT_MIN;
    
    //this xib file only contains the UICollectionView that is shown upon expanding the section.
    //There is nothing that interesting in this file,but I have some screenshots of the
    //attribute inspector for this xib file after this code.
    [self.tableView registerNib:[UINib nibWithNibName:@"MyAttributeTableViewCell" bundle:nil] forCellReuseIdentifier:@"MyAttributeTableViewCell"];
    [self.tableView registerClass:[MyAttributeTableHeaderView class] forheaderfooterViewReuseIdentifier:@"AttributeHeaderView"];
    [self.tableView invalidateIntrinsicContentSize];
}

- (void)setAttributeOptions:(NSArray<MyAttribute *> *)modelattributes {
    self->_modelattributes = modelattributes;
    
    self->_itemsExpanded = [NSMutableArray arrayWithCapacity:[self->_modelattributes count]];
    for (NSUInteger x=0; x<[self->_modelattributes count]; x++) {
        [self->_itemsExpanded addobject:@NO];
    }
    
    [self.tableView reloadData];
}


//Todo: is this necessary?
- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    self.preferredContentSize = self.tableView.contentSize;
}

#pragma mark - MyAttributeDelegate

- (void)twister:(MyAttributeTableViewCell *)cell didSelectItemAtIndex:(NSInteger)index {
    UITableViewheaderfooterView *headerView = [self.tableView headerViewForSection:cell.sectionIndex];
    if ([headerView isKindOfClass:[MyAttributeTableHeaderView class]]) {
        MyAttributeTableHeaderView *twisterHeaderView = (MyAttributeTableHeaderView *)headerView;
        twisterHeaderView.twister.selectedSwatchIndex = [NSNumber numberWithInteger:index];
        [twisterHeaderView updateAttributeIfNeeded];
    }
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.modelattributes.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.itemsExpanded[section].boolValue ? 1 : 0;
}

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    MyAttributeTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyAttributeTableViewCell" forIndexPath:indexPath];
    MyAttribute *twister = self.modelattributes[indexPath.section];
    cell.swatches = twister.swatches;
    NSNumber *swatchCellHeight = cell.swatchCellHeight;
    if (swatchCellHeight) {
        return swatchCellHeight.floatValue + 20.0f; //Todo: need to get this from the collection view
    }
    return 352.0f;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    if (section == self.modelattributes.count) {
        return CGFLOAT_MIN;
    }
    return 1.0f;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MyAttributeTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyAttributeTableViewCell" forIndexPath:indexPath];
    MyAttribute *twister = self.modelattributes[indexPath.section];
    cell.swatches = twister.swatches;
    cell.sectionIndex = indexPath.section;
    cell.delegate = self;
    
    if ([twister isSwatchSelected]) {
        NSInteger selectedindex = twister.selectedSwatchIndex.integerValue;
        [cell.collectionView selectItemAtIndexPath:[NSIndexPath indexPathForItem:selectedindex inSection:0] animated:YES scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
    }
    return cell;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    MyAttributeTableHeaderView *cell = (MyAttributeTableHeaderView *)[tableView dequeueReusableheaderfooterViewWithIdentifier:@"AttributeHeaderView"];
    
    if (cell.gestureRecognizers.count == 0) {
        UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleSection:)];
        [cell addGestureRecognizer:tapRecognizer];
    }
    cell.sectionIndex = section;
    cell.expanded = self.itemsExpanded[section].boolValue;
    cell.twister = self.modelattributes[section];
    return cell;
}

- (void)toggleSection:(UITapGestureRecognizer *)gesture {
    MyAttributeTableHeaderView *destinationView = ((MyAttributeTableHeaderView *)gesture.view);
    BOOL expanded = [destinationView toggleExpanded];
    self.itemsExpanded[destinationView.sectionIndex] = [NSNumber numberWithBool:expanded];

    
    UIView *viewToLayout = self.tableView;
    while ([viewToLayout superview]) {
        viewToLayout = viewToLayout.superview;
    }
    
    if (expanded) {
        [UIView beginAnimations:@"expandTableAnimationId" context:nil];
        [UIView setAnimationDuration:1.0f];
        [CATransaction begin];
        

        [self.tableView beginUpdates];
        [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:destinationView.sectionIndex]] withRowAnimation:UITableViewRowAnimationNone];
        [self.tableView endUpdates];

        [viewToLayout layoutIfNeeded];
        
        [CATransaction commit];
        [UIView commitAnimations];
    } else {
        [UIView beginAnimations:@"collapseTableAnimationId" context:nil];
        [UIView setAnimationDuration:1.0f];
        [CATransaction begin];
        
        
        [self.tableView beginUpdates];
        [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:destinationView.sectionIndex]] withRowAnimation:UITableViewRowAnimationNone];
        [self.tableView endUpdates];

        [viewToLayout layoutIfNeeded];

        [CATransaction commit];
        [UIView commitAnimations];
    }
}

@end

MyAttributeTableViewCell

MyAttributeTableViewCell attribute inspector

有谁知道我做错了什么,或者如何让这些动画看起来正确(没有加倍的行,也没有在行上方显示动画时的集合视图)?或者,如果您知道一种更好的方法来处理这个可能不那么复杂的问题,我也对此持开放态度。我只是想有一个可扩展部分的列表,其中每个部分都有一个可选项目的集合视图。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...