iOS表/集合视图中的单元间布局逻辑

时间:2014-03-13 07:33:02

标签: ios ipad uitableview layout uicollectionview

UITableViewUICollectionView中单独的单元格的部分与标题的单独行对齐的惯用方法是什么?

例如,假设我想要显示如下内容:

Name       Number
Bob        34587
Jane       32489
Barbara    23766
Montgomery 34892

"名称"和"数字"位将位于某种标题单元格中。我想我可以使用节标题(只有一个部分)。

标题下方的每个单元格都需要智能地调整其内容大小,但该大小需要影响所有其他单元格的布局。

这在iOS中通常如何实现?

2 个答案:

答案 0 :(得分:2)

如果我理解正确,那么您希望第二列尽可能靠近第一列移动而不会在第一列中制动内容。

据我所知,这在iOS上并不常用,因为对两列使用固定宽度空间要容易得多,而且通常不需要水平压缩它们。

但是,如果我真的需要完成这个:

  1. 我会循环浏览所有细胞内容并确定大小 通过调用NSString方法调用左侧内容 sizeWithFont:contrainedToSize:(如果在iOS7或之前使用) 如果在iOS7上,则boundingRectWithSize:options:attributes:context:
  2. 我将实例变量中的最大值存储起来(例如 _maxLeftContentWidth)。
  3. 在我的-tableView:cellForRowAtIndexPath:中,我为我设置了框架 根据我存储的内容左侧细胞内容和右侧细胞内容 实例变量(_maxLeftContentWidth)。
  4. 每当我的单元格的内容发生变化时,我再次运行左单元格宽度计算方法,然后调用[self.tableView reloadData]重绘单元格。

答案 1 :(得分:2)

即使它被称为表格视图'我们通常不会想到表格(即。,多列有多列)。如果你真的想要多列,那就取决于你。为了排列它们,你必须完成确定所需宽度的工作,或选择足够大的宽度以适应所有情况。

我想我会做这样的事情:

#define COLUMN_SPACE 10
- (UITableViewCell) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath {
    MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: @"cell"];
    // Layout the cell
    NSString *name = [myDataStore nameForIndexPath: indexPath];
    NSNumber *number = [myDataStore numberforIndexPath: indexPath];

    CGRect frame = cell.nameLabel.frame;
    frame.size.width = [self nameColumnWidth];
    cell.nameLabel.frame = frame;
    frame = cell.numberLabel.frame;
    frame.origin.x = cell.nameLabel.origin.x + cell.nameLabel.size.width + COLUMN_SPACE;
    frame.size.width = [self numberColumnWidth];
    cell.numberLabel.frame = frame;

    cell.nameLabel.text = name;
    // You would probably want to use an NSNumberFormatter here
    cell.numberLabel.text = [NSString stringWithFormat: @"%d", number.intValue];

    return cell;
}

- (CGFloat) nameColumnWidth {
    if( _nameColumnWidth <= 0.0 ) {
        _nameColumnWidth = 0.0;
        for( NSInteger s = 0; s < [myDataStore numberOfSections]; ++s ) {
            for( NSInteger r = 0; r < [myDataStore numberOfRowsInSection: s]; ++r ) {
                NSIndexPath *path = [NSIndexPath indexPathForRow: r inSection: s];
                NSString *name = [myDataStore nameForIndexPath: indexPath];
                CGFloat widthForName = [name widthNeeded];
                if( widthForName > _nameColumnWidth )
                    _nameColumnWidth = widthForName;
            }
        }
    }
    return _nameColumnWidth;
}

- (CGFloat) numberColumnWidth {
    if( _numberColumnWidth <= 0.0 ) {
        _numberColumnWidth = 0.0;
        for( NSInteger s = 0; s < [myDataStore numberOfSections]; ++s ) {
            for( NSInteger r = 0; r < [myDataStore numberOfRowsInSection: s]; ++r ) {
                NSIndexPath *path = [NSIndexPath indexPathForRow: r inSection: s];
                NSNumber *number = [myDataStore numberforIndexPath: indexPath];
                // You would probably want to use an NSNumberFormatter here
                // (in fact you want to do the same is you do in cellForRowAtIndexPath)
                NSString *numberAsString = [NSString stringWithFormat: @"%d", number.intValue];
                CGFloat widthForNumber = [numberAsString widthNeeded];
                if( widthForNumber > _numberColumnWidth )
                    _numberColumnWidth = widthForNumber;
            }
        }
    }
    return _numberColumnWidth;
}

在为节标题生成视图时,我会使用相同的[self nameColumnWidth][self numberColumnWidth],或者我只是让该节的第0行的单元格具有名称 Number 标题。

对于宽度,我将NSString扩展为:

@implementation NSString (WithWidthNeeded)
- (CGFloat) widthNeeded {
    // Either set the font you will use here, add it as a parameter, etc.
    UIFont *font = [UIFont systemFontOfSize: 18];
    CGSize maximumLabelSize = CGSizeMake( 9999, font.lineHeight );
    CGSize expectedLabelSize;
    if( [self respondsToSelector: @selector(boundingRectWithSize:options:attributes:context:)] ) {
        CGRect expectedLabelRect = [self boundingRectWithSize: maximumLabelSize options: NSStringDrawingUsesLineFragmentOrigin attributes: @{ NSFontAttributeName: font } context: nil];
            expectedLabelRect = CGRectIntegral( expectedLabelRect );
            expectedLabelSize = expectedLabelRect.size;
    } else {
        NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString: self attributes: @{ NSFontAttributeName: font }];
        if( [attributedText respondsToSelector: @selector(boundingRectWithSize:options:context:)] ) {
            CGRect expectedLabelRect = [attributedText boundingRectWithSize: maximumLabelSize options: NSStringDrawingUsesLineFragmentOrigin context: nil];
            expectedLabelRect = CGRectIntegral( expectedLabelRect );
            expectedLabelSize = expectedLabelRect.size;
    } else {
            expectedLabelSize = [self sizeWithFont: font
                                 constrainedToSize: maximumLabelSize
                                     lineBreakMode: NSLineBreakByTruncatingTail];

        }
    }
    return expectedLabelSize.width;
}
@end

更改数据后,_nameColumnWidth_numberColumnWidth可以更改为0.0并重新计算,以便调整列宽。

另一种方法是在cellForRowAtIndexPath中布置单元格时跟踪名称数字的最大宽度。如果它们增加,则触发重绘的可见单元格。这将具有更好的性能,因为它不必遍历整个数据集以找到最长的名称和最大的数字。但是,当您向下滚动视图时,这会导致列移动,并且遇到更长的名称或更大的数字,除非最长的名称< / em>和最大的数字恰好位于第一行。

也可以做一种混合,其中nameColumnWidthnumberColumnWidth被设置为最大可见(局部最大值的种类),但后面的线程开始通过剩下的数据并找到绝对最大值。当找到绝对最大值时,重新加载可见单元格。然后,列宽只有一个移位或变化。 (如果调用sizeWithFont:,后台线程可能不安全,因为它位于 UIKit 。对于iOS 6或7,我认为没问题。

同样,由于UITableView实际上是UIColumnView,因此您可以为您制作多个可以调整内容宽度的列。