【问题标题】:UITableView within UIScrollView using autolayoutUIScrollView 中的 UITableView 使用自动布局
【发布时间】:2013-06-24 10:37:26
【问题描述】:

目前,我正在使用UITableView 以及UIScrollView 中包含的其他视图。我希望UITableView 的高度与其内容高度相同。

为了使事情复杂化,我还插入/删除行以提供手风琴效果,这样当用户点击一行时,它将显示该行的更多详细信息。

我已经完成了插入/删除操作,但目前它没有更新作为其父视图的 UIScrollView,以便重新计算 UIScrollView 的内容大小并重新计算 UITableView 以及其他视图UIScrollView 显示正确。

当我更改UITableView 的内容时,我该如何实现这一点,以便调整UIScrollView 的大小并正确布局其内容?我目前正在使用自动布局。

【问题讨论】:

  • 你在 UIScrollView 中添加 UITableView ?????
  • 是的,UITableView 不会占用整个可见区域。我很清楚 UITableView 有一个 UIScrollView。为了禁用滚动,我将 tableView 的高度设置为 contentSize。
  • UITableView 本身有一个滚动视图。那为什么要添加到另一个scrollView?
  • UITableView 是另一个视图(UIScrollView)的子视图以及标签和图像等其他视图。
  • @MattDelves:如果是这样就可以了

标签: ios uitableview uiscrollview autolayout nslayoutconstraint


【解决方案1】:

首先,那些其他视图(表格视图的兄弟)是否严格位于表格视图的上方和下方?如果是这样,您是否考虑过让表格视图正常滚动,并将这些外部视图放在表格视图的页眉和页脚视图中?那么你就不需要滚动视图了。

其次,如果您还没有阅读Technical Note TN2154: UIScrollView And Autolayout,您可能想阅读。

第三,鉴于该技术说明中的信息,我可以想出一些方法来做你想做的事。最干净的可能是创建一个实现intrinsicContentSize 方法的UITableView 的子类。实现很简单:

@implementation MyTableView

- (CGSize)intrinsicContentSize {
    [self layoutIfNeeded]; // force my contentSize to be updated immediately
    return CGSizeMake(UIViewNoIntrinsicMetric, self.contentSize.height);
}

@end

然后让自动布局使用表格视图的固有内容大小。在滚动视图(包括表格视图)的子视图之间创建约束以进行布局,并确保滚动视图的所有四个边缘都有约束。

您可能需要在适当的时候将invalidateIntrinsicContentSize 发送到表格视图(当您添加或删除行或更改行的高度时)。您可能只需覆盖MyTableView 中的适当方法即可。例如。在-endUpdates-reloadData- insertRowsAtIndexPaths:withRowAnimation:等中做[self invalidateIntrinsicContentSize]

这是我的测试结果:

滚动视图具有浅蓝色背景。顶部的红色标签和底部的蓝色标签是滚动视图中表格视图的兄弟。

这是我测试中视图控制器的完整源代码。没有xib文件。

#import "ViewController.h"
#import "MyTableView.h"

@interface ViewController () <UITableViewDataSource, UITableViewDelegate>

@end

@implementation ViewController

- (void)loadView {
    UIView *view = [[UIView alloc] init];
    self.view = view;

    UIScrollView *scrollView = [[UIScrollView alloc] init];
    scrollView.translatesAutoresizingMaskIntoConstraints = NO;
    scrollView.backgroundColor = [UIColor cyanColor];
    [view addSubview:scrollView];

    UILabel *topLabel = [[UILabel alloc] init];
    topLabel.translatesAutoresizingMaskIntoConstraints = NO;
    topLabel.text = @"Top Label";
    topLabel.backgroundColor = [UIColor redColor];
    [scrollView addSubview:topLabel];

    UILabel *bottomLabel = [[UILabel alloc] init];
    bottomLabel.translatesAutoresizingMaskIntoConstraints = NO;
    bottomLabel.text = @"Bottom Label";
    bottomLabel.backgroundColor = [UIColor blueColor];
    [scrollView addSubview:bottomLabel];

    UITableView *tableView = [[MyTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
    tableView.translatesAutoresizingMaskIntoConstraints = NO;
    tableView.dataSource = self;
    tableView.delegate = self;
    [scrollView addSubview:tableView];

    UILabel *footer = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 30)];
    footer.backgroundColor = [UIColor greenColor];
    footer.text = @"Footer";
    tableView.tableFooterView = footer;

    NSDictionary *views = NSDictionaryOfVariableBindings(
        scrollView, topLabel, bottomLabel, tableView);
    [view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"V:|[scrollView]|"
        options:0 metrics:nil views:views]];
    [view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"H:|[scrollView]|"
        options:0 metrics:nil views:views]];
    [view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"V:|[topLabel][tableView][bottomLabel]|"
        options:0 metrics:nil views:views]];
    [view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"H:|[topLabel]|"
        options:0 metrics:nil views:views]];
    [view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"H:|-8-[tableView]-8-|"
        options:0 metrics:nil views:views]];
    [view addConstraint:[NSLayoutConstraint
        constraintWithItem:tableView attribute:NSLayoutAttributeWidth
        relatedBy:NSLayoutRelationEqual
        toItem:view attribute:NSLayoutAttributeWidth
        multiplier:1 constant:-16]];
    [view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"H:|[bottomLabel]|"
        options:0 metrics:nil views:views]];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 20;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    cell.textLabel.text = [NSString stringWithFormat:@"Row %d", indexPath.row];
    return cell;
}

@end

【讨论】:

  • 重要提示: 需要表格页脚才能使技巧发挥作用! (为此浪费了一个小时--')
  • 感谢您的精彩回答!节省了我几个小时!我已经对子类做了一个要点(它也可以在没有页眉和页脚的情况下工作):gist.github.com/booiiing/7941890
  • 这个效果很好,除了内容大小的动画变化,tableViews的大小只有在tableView内部的动画完成后才会改变
  • 我得到了一个无限循环,因为 layoutIfNeeded 调用了调用 layoutIfNeeded 的 intrinsicContentSize。 Rob 发生过这种事吗?
  • 请注意,当使用带有自动表格视图行尺寸的估计行高时,这不起作用。内容大小不正确,它不知道桌子实际有多高。
【解决方案2】:

除了 rob 的回答之外,还有 UITableView 的可自行调整大小的子类的 swift 示例:

Swift 2.x

class IntrinsicTableView: UITableView {

    override var contentSize:CGSize {
        didSet {
            self.invalidateIntrinsicContentSize()
        }
    }


    override func intrinsicContentSize() -> CGSize {
        self.layoutIfNeeded()
        return CGSizeMake(UIViewNoIntrinsicMetric, contentSize.height)
    }

}

Swift 3.xSwift 4.x

class IntrinsicTableView: UITableView {

    override var contentSize:CGSize {
        didSet {
            self.invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        self.layoutIfNeeded()
        return CGSize(width: UIViewNoIntrinsicMetric, height: contentSize.height)
    }

}

我用它把一个表格视图放到另一个可自动调整大小的表格视图的单元格中。

【讨论】:

  • 我尝试在 swift 3 override func intrinsicContentSize() 中编写此方法,但显示错误 --> 方法不覆盖超类中的任何方法
  • Swift 3 已将函数替换为: override var intrinsicContentSize: CGSize
  • Swift 4.2 UIViewNoIntrinsicMetric -> UIView.noIntrinsicMetric
  • 我试过这个解决方案,一个问题似乎是table view中的所有item都是一次性加载的,而不仅仅是那些可见的,这会导致UI挂起。跨度>
【解决方案3】:

这里是 obj-C 版本。它基于用户@MuHAOS 的解决方案

@implementation SizedTableView

- (void)setContentSize:(CGSize)contentSize {
  [super setContentSize:contentSize];
  [self invalidateIntrinsicContentSize];
}

- (CGSize)intrinsicContentSize {
  [self layoutIfNeeded]; // force my contentSize to be updated immediately
  return CGSizeMake(UIViewNoIntrinsicMetric, self.contentSize.height);
}


@end

【讨论】:

  • 我们如何使用故事板制作它
  • @AmrAngry 让你的 tableview 类成为 SizedTableView
【解决方案4】:

@MuHAOS 和@klemen-zagar 的代码对我帮助很大,但实际上当 tableview 包含在堆栈视图中时触发了无限布局循环,而堆栈视图本身包含在滚动视图中。请参阅下面的解决方案。

@interface AutoSizingTableView ()
@property (nonatomic, assign) BOOL needsIntrinsicContentSizeUpdate;
@end

@implementation AutoSizingTableView

- (void)setContentSize:(CGSize)contentSize
{
    [super setContentSize:contentSize];

    self.needsIntrinsicContentSizeUpdate = YES;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        if (!self.needsIntrinsicContentSizeUpdate) {
            return;
        }

        self.needsIntrinsicContentSizeUpdate = NO;
        [self layoutIfNeeded];
        [self invalidateIntrinsicContentSize];
    });
}

- (CGSize)intrinsicContentSize
{
    return CGSizeMake(UIViewNoIntrinsicMetric, self.contentSize.height);
}

@end

【讨论】:

    【解决方案5】:

    您可以将视图添加为 tableview 的 headerview 和 footerview。因为tableview是scrollview的子视图。 按照下面的例子。

    UILabel *topLabel = [[UILabel alloc] init];
    topLabel.translatesAutoresizingMaskIntoConstraints = NO;
    topLabel.text = @"Top Label";
    topLabel.backgroundColor = [UIColor redColor];
    tableView.tableFooterView = topLabel;
    
    UILabel *footer = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 30)];
         footer.backgroundColor = [UIColor greenColor];
         footer.text = @"Footer";
         tableView.tableFooterView = footer;
    

    您还可以使用简单的拖放视图将 tableview 的 headerview 和 footerview 添加到情节提要中的 tableview 并获取该视图的 IBOutlet。

    【讨论】:

      猜你喜欢
      • 2021-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-25
      • 2012-12-11
      • 2015-01-16
      相关资源
      最近更新 更多