61

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

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

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

我如何才能实现这一点,以便UIScrollView在更改UITableView. 我目前正在使用自动布局。

4

5 回答 5

102

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

其次,如果您还没有阅读技术说明 TN2154:UIScrollView 和 Autolayout ,则可能需要阅读。

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

@implementation MyTableView

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

@end

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

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

这是我的测试结果:

滚动视图中具有内在内容大小的表格视图

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

这是我测试中视图控制器的完整源代码。没有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
于 2013-06-27T06:17:19.517 回答
65

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

斯威夫特 2.x

class IntrinsicTableView: UITableView {

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


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

}

斯威夫特 3.x斯威夫特 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)
    }

}

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

于 2016-01-20T14:18:03.013 回答
12

这是 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
于 2016-08-06T09:58:23.810 回答
2

@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
于 2016-08-12T11:04:00.690 回答
0

您可以将视图添加为 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。

于 2016-09-30T05:07:35.460 回答