2013-06-27 72 views
47

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

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

我已经得到了插入/删除完成,但目前它不更新的UIScrollView这是它的父使得UIScrollView的内容大小被重新计算并与在UIScrollView其他视图是沿着UITableView正确显示。

如何才能实现这个功能,以便在更改UITableView的内容时调整UIScrollView的尺寸并正确布置其内容?我目前正在使用自动布局。

+0

您添加的UITableView在UIScrollView中????? – iPatel

+0

是的,UITableView不占用整个可见区域。我很清楚UITableView有一个UIScrollView。要禁用滚动,我将tableView的高度设置为contentSize。 –

+2

UITableView本身有一个滚动视图。那么为什么要添加到另一个scrollView? – Meera

回答

83

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

其次,如果您尚未阅读,您可能需要阅读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中的适当方法即可。例如。做[self invalidateIntrinsicContentSize]-endUpdates-reloadData- insertRowsAtIndexPaths:withRowAnimation:

这里是我的测试结果:

table view with intrinsic content size in scroll view

滚动视图有淡蓝色背景。红顶标签和蓝底标签是滚动视图内的表视图的兄弟。

这是我测试中视图控制器的完整源代码。没有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 
+5

**重要提示:**需要使用桌脚才能使用! (失去了一小时 - ') – AncAinu

+4

感谢您的这个伟大的答案!节省了我的时间!我所做的子类的要点(它的工作原理没有页眉和页脚,太):https://gist.github.com/booiiing/7941890 –

+1

这工作得很好,除了内容的大小,动画的变化在哪里tableViews的大小只会在tableView里面的动画完成后才会改变 – Ahti

44

除了抢劫的答案有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) 
    } 

} 

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

} 

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

+0

我试着写在迅速3 重写此方法FUNC intrinsicContentSize() 而是显示的错误 - >方法不覆盖任何超类 –

+6

斯威夫特3已经取代了有利于功能:覆盖VAR intrinsicContentSize:CGSize –

8

这里是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 
+0

我们如何能够用故事板 –

+1

使其@AmrAngry让你的tableview类是SizedTableView –

+0

这应该被标记为完美答案。 – Nil

1

@ 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 
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; 

,你也可以添加headerview和使用简单的拖放的tableview的footerview和故事板视图拖放到的tableview和利用这一看法IBOutlet中。

相关问题