如何强制 uitableview 调整单元格的大小(在停用高度约束后)?

Posted

技术标签:

【中文标题】如何强制 uitableview 调整单元格的大小(在停用高度约束后)?【英文标题】:How to force uitableview to resize a cell (after deactivating a height constraint)? 【发布时间】:2016-02-22 21:25:36 【问题描述】:

我正在尝试通过点击一个按钮来放置一个可扩展的UITableViewCell

我正在使用约束来强制动态UITextView 告诉UITableViewCell 它的新高度。 (使用自动布局和UITableViewAutomaticDimension

这在没有按钮的情况下按预期工作:UITableViewCell 的高度取决于UITextView 的高度。

UITextView 有一个约束以最大化单元格的大小,当点击按钮时(单元格已折叠),我想删除对UITextView 的高度约束(即限制单元格高度)和相应地更新单元格。这是扩展方法:

-(void)extendCellWasTapped:(UITableViewCell*)senderCell

    MAFollowingPostTableViewCell *cell = (MAFollowingPostTableViewCell*)senderCell;

    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];

    if (![self.arrayExtendedTitlesAtIndexPaths containsObject:indexPath]) 
        [self.arrayExtendedTitlesAtIndexPaths addObject:indexPath];
    

    if ([self.tableView.indexPathsForVisibleRows containsObject:indexPath] ) 
        dispatch_async(dispatch_get_main_queue(), ^(void)

            [NSLayoutConstraint deactivateConstraints:@[cell.constraintTextViewHeight]];

           // CGPoint offset = self.tableView.contentOffset;

            [self.tableView beginUpdates];
            [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
            [self.tableView endUpdates];

// I would like to minimize the animations as well here.. but thats another //problem
//            [self.tableView.layer removeAllAnimations];
//            [self.tableView setContentOffset:offset animated:NO];
//            [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

        );
    

如果我点击按钮然后滚动到类似的单元格,则它已经展开。所以我在这里遗漏了一些东西。我已经尝试在停用约束后执行[cell updateConstraints],但它不起作用。

更新

起初,由于某种原因,该按钮没有显示。而且我注意到(使用相同的代码)如果我向下滚动然后向上滚动,按钮就会显示出来,如果我尝试扩展它,它就会起作用。

更新

这是我的 UIViewController :

http://www.gfycat.com/FairBossyAfricanporcupine

请注意,扩展按钮不存在,但是一旦我向下/向上滚动,它就会出现,并且一旦我点击扩展按钮,它就会扩展单元格。

更新

我注意到在计算 textSize 时(查看单元格是否可扩展),首先,textView 比其最终状态更宽。我正在对 cellForRowAtIndexPath 执行此检查:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 

MAPostTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier forIndexPath:indexPath];

cell.delegate = self;
cell.selectionStyle = UITableViewCellSelectionStyleNone;

MAPostFollowing *post = (MAPostFollowing*)[self.arrayPosts objectAtIndex:indexPath.section];

cell.textViewTitle.textContainer.lineBreakMode = NSLineBreakByWordWrapping;

NSString *title = [post.post title];

[cell.textViewTitle setText:title];

UIButton * buttonExtend = [cell buttonExtend];

if ([self.arrayExtendedTitlesAtIndexPaths containsObject:indexPath]) 
    cell.constraintTextViewHeight.active = NO;

    if (![buttonExtend isHidden]) 
        buttonExtend.hidden = YES;
        buttonExtend.userInteractionEnabled = NO;
    
else

    cell.constraintTextViewHeight.active = YES;

    NSLog(@"index %li", (long)indexPath.row);

    BOOL b = ![self isTitleExtendable:title forTextView:cell.textViewTitle]; // Checks if the current text view is able to fit the title. If not, it will show the buttonExtend

    buttonExtend.hidden = b;
    buttonExtend.userInteractionEnabled = !b;



...

所以在第一次运行(滚动前)时,[self isTitleExtendable:title forTextView:cell.textViewTitle];不返回所需的值,因为在计算 textSize 时 textView 没有正确的宽度。

所以我相信有以下选择: - 更改布局后强制 textViewTitle 刷新(从而正确计算可扩展 UI 状态) - 一旦 textViewTitle 改变宽度,重新检查可扩展 UI 状态

更新

在 prepareForReuse 方法上,textViewTitle 仍然有一个更宽的框架。在 layoutSubviews 方法中,它实际上有一个小框架。这里的挑战是被更频繁地调用,它让我得到错误的情况(按钮在不应该显示的时候显示)

这是我正在尝试的:

-(void)layoutSubviews
[super layoutSubviews];

BOOL b = [self isTitleExtendable:self.textViewTitle.text forTextView:self.textViewTitle];

self.buttonExtend.hidden = !b;
self.buttonExtend.userInteractionEnabled = b;

if (!b) 
    NSLog(@"Button hidden YES UserInteraction NO");
else
    NSLog(@"Button hidden NO UserInteraction YES");


NSLog(@"textViewTitle layout %f",self.textViewTitle.frame.size.width);

【问题讨论】:

如果您尝试删除约束而不是停用它会发生什么[cell removeConstraints:cell.constraintTextViewHeight] 同样的事情。不确定一旦单元被重用它是否会起作用(这就是我停用而不是删除的原因) 我对这个问题有点困惑。通过查看您的 GIF,听起来问题在于首次呈现单元格时按钮不存在。在您似乎暗示的问题中,按钮在那里并且不起作用。请耐心等待,也许只是我误解了这个问题。 你是对的。按钮一开始就不在那里,所以我想要的行为不适用于那些。 您是否已经为视图/按钮/控件尝试了 [tableView reloadData] 和 [view setNeedsDisplay]? 【参考方案1】:

要做这样的事情,你不应该依赖约束,而是在tableView(_:heightForRowAtIndexPath:)你只需要根据上下文给出正确的高度。

按下按钮会触发更改,因此 tableView(_:heightForRowAtIndexPath:) 会为您扩展的单元格返回不同的值。

在执行此触发器之后,只需执行以下操作:

[self.tableView beginUpdates];
[self.tableView endUpdates];

它们之间没有任何东西,它会触发UITableView的“自然动画”并做你想做的事

【讨论】:

正如描述所说,我正在使用 UITableViewAutomaticDimension 我仍然会使用开始更新技巧来强制动画更新 ;) 但是在使用自动调整大小的单元格时我没有尝试【参考方案2】:

忘记从 self.arrayExtendedTitlesAtIndexPaths删除 indexpath 吗?

if (![self.arrayExtendedTitlesAtIndexPaths containsObject:indexPath]) 
    [self.arrayExtendedTitlesAtIndexPaths addObject:indexPath];

else

   [self.arrayExtendedTitlesAtIndexPaths removeObject:indexPath];

【讨论】:

没有。这是用户点击扩展按钮后执行的操作。一旦他这样做了,那个单元格就会一直被扩展。【参考方案3】:

如果按钮不存在并且在您滚动它时再次显示,那么您肯定有可重用性问题。

您可能决定在cellForRowAtIndexPath: 中显示或隐藏按钮。如果您有条件,请尝试始终显示此按钮,并且如果这将按预期工作,则无论是否存在此约束,都将按钮隐藏在单元格内。正确的方法是在UITableViewCell 子类中的prepareForReuse

【讨论】:

这可能是要走的路。不过,我仍然不能 100% 确定实施情况【参考方案4】:

更改完成后致电[cell layoutSubviews];

【讨论】:

根据 layoutSubviews 的文档:方法:“你不应该直接调用这个方法。”

以上是关于如何强制 uitableview 调整单元格的大小(在停用高度约束后)?的主要内容,如果未能解决你的问题,请参考以下文章

通过 UITextView 内容大小调整 UITableView 单元格的大小

UICollectionViewController 如何调整单元格的大小?

自动布局动态大小 uitableview 单元格的最佳方法是啥

具有可变宽度单元格的 UITableView

动态调整自定义 xib 单元格的大小

自动调整大小(动态高度)表格视图单元格的问题