UIScrollView中的UITableView使用autolayout

Mat*_*ves 53 uitableview uiscrollview ios autolayout nslayoutconstraint

目前,我正在使用一个UITableView包含在其中的其他视图UIScrollView.我希望UITableView它的高度与其内容高度相同.

更复杂的是,我还插入/删除行以提供手风琴效果,这样当用户点击一行时,它将显示该行的更多细节.

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

我怎样才能实现这一点,以便UIScrollView在我更改内容时调整大小并正确布置其内容UITableView?我目前正在使用自动布局.

rob*_*off 94

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

其次,您可能需要阅读技术说明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
Run Code Online (Sandbox Code Playgroud)

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

您可能需要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
Run Code Online (Sandbox Code Playgroud)

  • **重要提示:**桌面页脚是工作技巧所必需的!(用它丢了一个小时 - ') (6认同)
  • 谢谢你的回答!节省了我几个小时!我已经对子类做了一个要点(它也没有页眉和页脚):https://gist.github.com/booiiing/7941890 (5认同)
  • 请注意,当使用具有自动表视图行尺寸的估计行高时,这不起作用.内容大小不正确,它不知道表实际有多高. (4认同)
  • 我得到了一个无限循环,因为layoutIfNeeded调用了调用layoutIfNeeded的intrinsicContentSize.你和罗布一起发生过吗? (3认同)

小智 54

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

Swift 2.x

class IntrinsicTableView: UITableView {

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


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

}
Run Code Online (Sandbox Code Playgroud)

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)
    }

}
Run Code Online (Sandbox Code Playgroud)

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

  • Swift 3已经替换了函数,有利于:override var intrinsicContentSize:CGSize (6认同)

Kle*_*men 10

这是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
Run Code Online (Sandbox Code Playgroud)