【问题标题】:Dynamic height collection/tag view inside a UITableViewCellUITableViewCell 内的动态高度集合/标签视图
【发布时间】:2017-01-30 10:12:15
【问题描述】:

我的应用程序中有一个已经存在的表格视图,我正在尝试向其中添加一个新单元格。新单元格将显示可变数量的按钮,每个按钮都有一个标题(因此是动态宽度)。新单元格应该如下所示:

┃┃                                   ┃┃  <---- existing cells above
┃┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛┃
┃┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓┃
┃┃┏━━━━━┓  ┏━━━━━━━━━━━━━━━━━━━┓     ┃┃
┃┃┃ 632 ┃  ┃ 12345678901728933 ┃     ┃┃
┃┃┗━━━━━┛  ┗━━━━━━━━━━━━━━━━━━━┛     ┃┃
┃┃┏━━━━━━━━━━━┓  ┏━━━━━━┓  ┏━━━━━━┓  ┃┃
┃┃┃ 123345573 ┃  ┃ jhkl ┃  ┃ dasd ┃  ┃┃  <---- new cell
┃┃┗━━━━━━━━━━━┛  ┗━━━━━━┛  ┗━━━━━━┛  ┃┃
┃┃┏━━━┓  ┏━━━━━━━━━━━━━━┓  ┏━━━┓     ┃┃
┃┃┃ 1 ┃  ┃ 128732434554 ┃  ┃ 1 ┃     ┃┃
┃┃┗━━━┛  ┗━━━━━━━━━━━━━━┛  ┗━━━┛     ┃┃
┃┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛┃
┃┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓┃
┃┃                                   ┃┃  <---- existing cells below

按钮应该放在一行上,直到下一个溢出单元格,此时它应该换行到下一行(类似于带有自动换行的 UILabel)。

重要注意事项

  1. 此单元格需要具有动态高度。我从tableView:heightForRowAtIndexPath 方法返回UITableViewAutomaticDimension,单元格需要计算自己的高度。
  2. 按钮标题数据将在cellForRowAtIndexPath 返回单元格之前填充。
  3. 我正在尝试找到一种无需在表格视图上调用beginUpdates/endUpdates 即可调整其单元格大小的解决方案。
  4. 我更喜欢使用 Autolayout 来执行此操作,但如果我真的需要,我愿意手动计算帧(尽管我不完全确定如何执行此操作,因此可能需要一些指导)。
  5. 此视图也需要在表格视图单元格之外可用,所以我不能只看[UIScreen mainScreen] 来计算单元格的理想宽度。

我已经尝试过的

  1. 我尝试使用 UICollectionView 实现视图。在这种情况下,集合视图的数据源返回包装在 UICollectionViewCells 中的按钮。这样做的问题是 UICollectionView 是一个 UIScrollView,因此不会自动调整自身大小以适应其内容。
  2. 我尝试使用自定义 UICollectionView 子类和自定义实现 intrinsicContentSize。这个实现将返回集合视图的contentSize,或者集合视图的 UICollectionViewFlowLayout 的collectionViewContentSize()(我都试过了)。这里的问题是,这些值在布局 collectionView 之前不会返回实际的内容大小,此时我的 UITableView 已经将单元格高度缓存为 0。
  3. 我尝试使用 UICollectionView 并为其高度保留 NSLayoutConstraint,每次 contentSize 更改时都会更改。不幸的是,这会遇到与上述相同的问题,因为 UITableView 在 contentSize 更改之前布置了单元格。
  4. 我尝试使用嵌套的 UIStackViews 实现一个完全自定义的单元格:在这种情况下,单元格内会有 N 个水平堆栈视图。我会将按钮添加到第一个堆栈视图,直到它们的总宽度超过单元格的边界,此时我会将它们添加到下一个堆栈视图,等等。问题是,为了做到这一点,我需要知道单元格的bounds,直到调用它的layoutSubviews 才设置,此时 UITableView 已经缓存了单元格的高度。
  5. 我尝试使用 https://github.com/xhacker/TagListView 之类的东西,其中自定义视图在调用 layoutSubviews 时手动计算所有内容的框架,并将(行数)*(每行的高度)报告为其内在内容高度,但是问题是,当计算发生时,视图的框架为 0(因为它被布置在 UITableViewCell 内),因此内在内容大小计算错误,导致如下所示:

    ┃┃                                   ┃┃  <---- existing cells above
    ┃┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛┃
    ┃┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓┃
    ┃┃┏━━━━━┓   ┏━━━━━━━━━━━━┓           ┃┃
    ┃┃┃ 632 ┃   ┃ 6327237842 ┃           ┃┃
    ┃┃┗━━━━━┛   ┗━━━━━━━━━━━━┛           ┃┃
    ┃┃┏━━━━━━━━━━━━━━━━━━━┓              ┃┃
    ┃┃┃ 12345678901728933 ┃              ┃┃
    ┃┃┗━━━━━━━━━━━━━━━━━━━┛              ┃┃    
    ┃┃                                   ┃┃
    ┃┃                                   ┃┃  <---- new cell
    ┃┃                                   ┃┃
    ┃┃                                   ┃┃
    ┃┃                                   ┃┃
    ┃┃                                   ┃┃
    ┃┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛┃
    ┃┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓┃
    ┃┃                                   ┃┃  <---- existing cells below
    

一般来说,UITableView 缓存单元格的高度会导致有一个称为 UIView-Encapsulated-Layout-Height 的约束,它的常量为 0,并且在许多 SO 帖子中都有很好的记录。据我所知,没有办法从单元级别重新计算这个高度:setNeedsUpdateConstraintssetNeedsLayoutlayoutIfNeededinvalidateIntrinsicContentSize,以及我尝试过的任何组合都没有帮助。

这两天真的让我很困惑。我想知道是否有人以前实现过类似的东西并找到了一种可靠的好方法,或者可以就如何解决这个问题提供任何建议。

【问题讨论】:

  • 你最终实现了什么?我也想做同样的事情。
  • 我最终实施了一个非常 hacky 的解决方案,因为我找不到这样做的好方法。 UICollectionView 具有自定义流布局几乎 可以工作,但不能自动布局单元格的大小。我编写了一个名为 StaticCollectionView 的类,它具有preferredMaxLayoutWidth 属性和一个通过手动计算重新排列其子视图的方法,称为rearrangeSubviewsIfNeeded。我还手动计算了intrinsicContentSize。然后,在包含 StaticCollectionView 的视图的 layoutSubviews 中,我设置了 preferredMaxLayoutWidth = self.bounds.width 并调用了重新排列SubviewsIfNeeded
  • 在这种情况下,您还必须在调用 super.layoutSubviews 之前调用 invalidateIntrinsicContentSize。当我将新数据设置到集合视图中时,我在 StaticCollectionView 上调用 setNeedsLayout,最后在 superview 上调用 invalidateIntrinsicContentSize 并在 superview 上调用 setNeedsLayout。恐怕你的里程数可能会有所不同。

标签: ios objective-c iphone uitableview uicollectionview


【解决方案1】:

在表格单元格中写入RowAtIndexPath 方法。

        y=20;
        x=20;

        for (int i=0; i<[arrAssignUsers count]; i++) {
            CGRect screenRect=[[UIScreen mainScreen]bounds];
            CGFloat screenWidth=screenRect.size.width;


            [arrBtnStatus addObject:[NSNumber  numberWithBool:NO]];

            NSString *strNames=[arrAssignUsers objectAtIndex:i];
            stringsize=[strNames sizeWithAttributes:
                        @{NSFontAttributeName: [UIFont systemFontOfSize:20.0f]}];
            btn=[UIButton buttonWithType:UIButtonTypeRoundedRect];
            CGFloat m=x+stringsize.width+10;
            CGFloat n=screenWidth-20;

            if (m<=n) {
                btn.frame=CGRectMake(x, y,stringsize.width,stringsize.height);
                x=x+stringsize.width +10;
            }
            else
            {
                y=y+stringsize.height+10;
                x=20;
                btn.frame=CGRectMake(x, y,stringsize.width,stringsize.height);
                x=x+stringsize.width+10;
            }

            [btn setTitle:self.arrAssignUsers[i] forState:UIControlStateNormal];
            [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
            btn.tag=i;
            [btn addTarget:self
                    action:@selector(notifyButtonPressedInEditTask:) forControlEvents:UIControlEventTouchUpInside];

            btn.backgroundColor=[UIColor whiteColor];
            btn.layer.cornerRadius=10;
            [btn.layer setMasksToBounds:YES];
            btn.layer.borderWidth=2.0f;
            btn.layer.borderColor=[UIColor orangeColor].CGColor;

            [self checkNotifybtnStatus:btn];
            [cell addSubview:btn];
            return cell;

那就写这个方法:

-(void)checkNotifybtnStatus:(UIButton *)clicked{
state = [[arrBtnStatus objectAtIndex:clicked.tag]boolValue];

if ((clicked.tag>=0)&&(state==YES)) {
    clicked.backgroundColor=OS_ORANGE_COLOR;
}
else
    clicked.backgroundColor=[UIColor clearColor];
}

那就写这个吧。

-(void)notifyButtonPressedInEditTask:(id)sender{
UIButton *clicked = (UIButton *) sender;
NSLog(@"%ld",(long)clicked.tag);    //Here you know which button has pressed


for (UserListData *taskData in arrAssignUserInfoList) {
    [arrNotifyUsers addObject:taskData.strUniqID];
}

 state = [[arrBtnStatus objectAtIndex:clicked.tag]boolValue];

if ((clicked.tag>=0)&&(state==NO)) {
    [arrBtnStatus replaceObjectAtIndex:clicked.tag withObject:[NSNumber numberWithBool:!state]];
    clicked.backgroundColor=[UIColor colorWithRed:254.0/255.0 green:139.0/255.0 blue:26.0/255.0 alpha:1];

    [arrNotifySelectedUsers addObject:[arrNotifyUsers objectAtIndex:clicked.tag]];
    NSLog(@" arr new--%@",arrNotifySelectedUsers.description);
}
else
{
    clicked.backgroundColor=[UIColor clearColor];
    [arrBtnStatus replaceObjectAtIndex:clicked.tag withObject:[NSNumber numberWithBool:!state]];
    [arrNotifySelectedUsers removeObject:[arrNotifyUsers objectAtIndex:clicked.tag]];
    NSLog(@" arr new--%@",arrNotifySelectedUsers.description);
}
}

根据您的项目更改变量。 这可能会对您有所帮助。

【讨论】:

  • 不幸的是,这无济于事,因为它根据[UIScreen mainScreen] 计算宽度,这意味着我需要编写的这个视图在表格视图单元(或任何不占据整个屏幕宽度的地方)。
猜你喜欢
  • 1970-01-01
  • 2019-10-12
  • 2018-04-09
  • 1970-01-01
  • 2014-07-30
  • 1970-01-01
  • 2019-03-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多