调整UITableView的大小以适应内容

时间:2010-04-07 19:00:06

标签: ios objective-c uitableview sizetofit

我正在创建一个应用程序,其UILabel中有一个问题,UITableView中显示多项选择答案,每行显示多项选择。问题和答案会有所不同,所以我需要UITableView这个高度是动态的。

我想找一张sizeToFit来解决这个问题。表格的框架设置为其所有内容的高度。

有人可以就如何实现这一目标提出建议吗?

23 个答案:

答案 0 :(得分:143)

其实我自己找到了答案。

我只是使用CGRect tableView.frame

height创建新的table.contentSize.height

UITableView的高度设置为其内容的height。 由于代码修改了UI,因此不要忘记在主线程中运行它:

dispatch_async(dispatch_get_main_queue(), ^{
        //This code will run in the main thread:
        CGRect frame = self.tableView.frame;
        frame.size.height = self.tableView.contentSize.height;
        self.tableView.frame = frame;
    });

答案 1 :(得分:51)

没有KVO,DispatchQueue或自行设置限制的Swift 5和4.2解决方案。

此解决方案基于Gulz's answer

1)创建 list(map(lambda x: len(x.split(' ')), phrase)) 的子类:

UITableView

2)在布局中添加import UIKit final class ContentSizedTableView: UITableView { override var contentSize:CGSize { didSet { invalidateIntrinsicContentSize() } } override var intrinsicContentSize: CGSize { layoutIfNeeded() return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height) } } 并在所有方面设置约束。将其类设置为UITableView

3)你应该看到一些错误,因为故事板并没有采用我们的子类'考虑ContentSizedTableView。通过打开大小检查器并将intrinsicContentSize重写为占位符值来解决此问题。这是设计时的重写。在运行时,它将使用我们的intrinsicContentSize

中的覆盖

更新:更改了Swift 4.2的代码。如果您使用的是先前版本,请使用ContentSizedTableView代替UIViewNoIntrinsicMetric

答案 2 :(得分:49)

快速解决方案

请按照以下步骤操作:

1-从故事板中设置表格的高度约束。

2-从故事板中拖出高度约束,并在视图控制器文件中为它创建@IBOutlet。

override func viewWillLayoutSubviews() {
    super.updateViewConstraints()
    self.tableHeight?.constant = self.table.contentSize.height
}

3-然后,您可以使用以下代码动态更改表的高度:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    self.viewWillLayoutSubviews()
}

<强>更新

如果为您剪切了最后一行,请尝试在willDisplay单元格函数中调用viewWillLayoutSubviews():

{{1}}

答案 3 :(得分:20)

我在iOS 7中尝试了这个,它对我有用

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.tableView sizeToFit];
}

答案 4 :(得分:18)

在表视图上添加contentSize属性的观察者,并相应地调整帧大小

[your_tableview addObserver:self forKeyPath:@"contentSize" options:0 context:NULL];

然后在回调中:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
         CGRect frame = your_tableview.frame;
         frame.size = your_tableview.contentSize;
         your_tableview.frame = frame;
    }

希望这会对你有所帮助。

答案 5 :(得分:8)

如果您不想自己跟踪表视图的内容大小更改,您可能会发现此子类很有用。

establishEquipmentStatus

答案 6 :(得分:5)

我在滚动视图中有一个表视图,并且必须计算tableView的高度并相应地调整它的大小。这些是我采取的步骤:

0)向你的scrollView添加一个UIView(可能会在没有这一步的情况下工作,但我这样做是为了避免任何可能的冲突) - 这将是你的表视图的包含视图。如果您执行此步骤,则将视图边框设置为tableview的视图边框。

1)创建UITableView的子类:

class IntrinsicTableView: UITableView {

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

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

}

2)将Storyboard中的表视图的类设置为IntrinsicTableView:screenshot:http://joxi.ru/a2XEENpsyBWq0A

3)将heightConstraint设置为表视图

4)将表格的IBoutlet拖到ViewController

5)将表格高度约束的IBoutlet拖到ViewController

6)将此方法添加到ViewController中:

override func viewWillLayoutSubviews() {
        super.updateViewConstraints()
        self.yourTableViewsHeightConstraint?.constant = self.yourTableView.intrinsicContentSize.height
    }

希望这有帮助

答案 7 :(得分:2)

Swift 3,iOS 10.3

解决方案1: 只需将self.tableview.sizeToFit()放入cellForRowAt indexPath函数即可。确保将tableview高度设置得比您需要的更高。 如果您没有在tableview下面查看视图,这是一个很好的解决方案。但是,如果你有,底部tableview约束将不会更新(我没有尝试解决它,因为我提出了解决方案2)

示例:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as? TestCell {
        cell.configureCell(data: testArray[indexPath.row])
        self.postsTableView.sizeToFit()
        return cell
    }

    return UITableViewCell()
}

解决方案2: 在storyboard中设置tableview高度约束并将其拖到ViewController。如果您知道单元格的平均高度,并且知道数组包含多少元素,则可以执行以下操作:

tableViewHeightConstraint.constant = CGFloat(testArray.count) * 90.0     // Let's say 90 is the average cell height

<强> *编辑:

在我尝试过的所有解决方案之后,他们每个人都在修复某些问题,但并非完全无法解决,this就是完全解释和解决这个问题的答案。

答案 8 :(得分:2)

如果你的contentSize不对,这是因为它基于estimatedRowHeight(自动),请在此之前使用

  

tableView.estimatedRowHeight = 0;

来源:https://forums.developer.apple.com/thread/81895

答案 9 :(得分:1)

如果使用AutoLayout,有一种更好的方法:更改确定高度的约束。只需计算表格内容的高度,然后找到约束并进行更改。这是一个例子(假设确定你的桌子高度的约束实际上是一个高度约束,关系为&#34; Equal&#34;):

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    for constraint in tableView.constraints {
        if constraint.firstItem as? UITableView == tableView {
            if constraint.firstAttribute == .height {
                constraint.constant = tableView.contentSize.height
            }
        }
    }
}

答案 10 :(得分:1)

Mimo's answerAnooj VM 's answer两者都很棒但是如果你有一个很大的列表会有一个小问题,框架的高度可能会切断你的一些细胞。

因此。我已经修改了一点答案:

dispatch_async(dispatch_get_main_queue()) {
    //This code will run in the main thread:
    CGFloat newHeight=self.tableView.contentSize.height;
    CGFloat screenHeightPermissible=(self.view.bounds.size.height-self.tableView.frame.origin.y);
    if (newHeight>screenHeightPermissible)
    {
        //so that table view remains scrollable when 'newHeight'  exceeds the screen bounds
        newHeight=screenHeightPermissible;
    }

    CGRect frame = self.tableView.frame;
    frame.size.height = newHeight;
    self.tableView.frame = frame;
}

答案 11 :(得分:1)

这适用于我使用自动版式的情况,并且只有一个部分的表格视图。

func getTableViewContentHeight(tableView: UITableView) -> CGFloat {
        tableView.bounds = CGRect(x: 0, y: 0, width: 300, height: 40)
        let rows = tableView.numberOfRows(inSection: 0)
        var height = CGFloat(0)
        for n in 0...rows - 1 {
            height = height + tableView.rectForRow(at: IndexPath(row: n, section: 0)).height
        }
        return height
    }

我在设置自动版式时调用此函数(此处的示例使用SnapKit,但您知道了)

    let height = getTableViewContentHeight(tableView: myTableView)
    myTableView.snp.makeConstraints {
        ...
        ...
        $0.height.equalTo(height)
    }

我希望UITableView只与单元格的组合高度一样高;我遍历单元格并累积单元格的总高度。由于此时表视图的大小为CGRect.zero,因此我需要设置范围以遵守单元格定义的自动布局规则。我将大小设置为应该足够大的任意值。实际尺寸将在以后由自动版面系统计算。

答案 12 :(得分:1)

基于 fl034's answer

Swit 5

var tableViewHeight: NSLayoutConstraint?

    tableViewHeight = NSLayoutConstraint(item: servicesTableView, 
    attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute,
    multiplier: 0.0, constant: 10)
    tableViewHeight?.isActive = true


  func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    tableViewHeight?.constant = tableView.contentSize.height
    tableView.layoutIfNeeded()
}

答案 13 :(得分:0)

Musa almatri的objc版本

(void)viewWillLayoutSubviews
{
    [super updateViewConstraints];
    CGFloat desiredHeight = self.tableView.contentSize.height;
    // clamp desired height, if needed, and, in that case, leave scroll Enabled
    self.tableHeight.constant = desiredHeight;
    self.tableView.scrollEnabled = NO;
}

答案 14 :(得分:0)

您可以尝试使用此自定义AGTableView

使用故事板或以编程方式设置TableView高度约束。 (此类自动获取高度约束并将内容视图高度设置为yourtableview height)。

class AGTableView: UITableView {

    fileprivate var heightConstraint: NSLayoutConstraint!

    override init(frame: CGRect, style: UITableViewStyle) {
        super.init(frame: frame, style: style)
        self.associateConstraints()
    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.associateConstraints()
    }

    override open func layoutSubviews() {
        super.layoutSubviews()

        if self.heightConstraint != nil {
            self.heightConstraint.constant = self.contentSize.height
        }
        else{
            self.sizeToFit()
            print("Set a heightConstraint to Resizing UITableView to fit content")
        }
    }

    func associateConstraints() {
        // iterate through height constraints and identify

        for constraint: NSLayoutConstraint in constraints {
            if constraint.firstAttribute == .height {
                if constraint.relation == .equal {
                    heightConstraint = constraint
                }
            }
        }
    }
}

注意如果设置高度有任何问题,请yourTableView.layoutSubviews()

答案 15 :(得分:0)

基于 fl034 的答案。但对于 Xamarin.iOS 用户:

    [Register("ContentSizedTableView")]
    public class ContentSizedTableView : UITableView
    {
        public ContentSizedTableView(IntPtr handle) : base(handle)
        {
        }

        public override CGSize ContentSize { get => base.ContentSize; set { base.ContentSize = value; InvalidateIntrinsicContentSize(); } }
        public override CGSize IntrinsicContentSize
        {
            get
            {
                this.LayoutIfNeeded();
                return new CGSize(width: NoIntrinsicMetric, height: ContentSize.Height);
            }
        }
    }

答案 16 :(得分:0)

如果希望表是动态的,则需要使用基于表内容的解决方案,如上所述。如果只想显示一个较小的表,则可以使用容器视图并在其中嵌入UITableViewController-UITableView将根据容器大小进行调整。

这避免了很多计算和布局调用。

答案 17 :(得分:0)

我在Swift 5中的解决方案是将tableView的高度约束设置为其contentSize.height的大小,假设每次cellForRowAt中生成一个单元格时都使用“自动布局”

tableView.heightAnchor.constraint(equalToConstant: tableView.contentSize.height).isActive = true

答案 18 :(得分:0)

我做了一些不同的事情,实际上我的TableView在scrollview内部,所以我不得不将高度限制设为0。

然后在运行时进行以下更改,

       func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
            self.viewWillLayoutSubviews()
       }

       override func viewWillLayoutSubviews() {
            super.updateViewConstraints()
            self.tableViewHeightConstraint?.constant = self.myTableView.contentSize.height
       }

答案 19 :(得分:0)

Swift 5解决方案

请遵循以下四个步骤:

  1. 从情节提要中设置表格视图的高度限制。

  2. 从情节提要中拖动高度约束,然后在视图控制器文件中为其创建@IBOutlet

    @IBOutlet var tableViewHeightConstraint: NSLayoutConstraint!
    
  3. override func viewDidLoad()

    上为contentSize属性添加观察者
override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
 
    }

  1. 然后您可以使用以下代码动态更改桌子的高度:

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
         if(keyPath == "contentSize"){
             if let newvalue = change?[.newKey]
             {
                 DispatchQueue.main.async {
                 let newsize  = newvalue as! CGSize
                 self.tableViewHeightConstraint.constant = newsize.height
                 }
    
             }
         }
     }
    

答案 20 :(得分:0)

我正在使用 UIView扩展,方法与上面的@ChrisB方法接近

admin.database().ref('/113882052')

实现:

admin.database().ref()

奖金:可以在任何其他UIView上使用,例如:您自己的动态标签

答案 21 :(得分:-1)

在swift 3中解决此问题:在viewDidAppear中调用此方法

func UITableView_Auto_Height(_ t : UITableView)
{
        var frame: CGRect = t.frame;
        frame.size.height = t.contentSize.height;
        t.frame = frame;        
}

答案 22 :(得分:-1)

作为Anooj VM的答案的扩展,我建议以下内容仅在更改时刷新内容大小。

此方法还正确禁用滚动支持更大列表轮换。不需要dispatch_async,因为在主线程上调度了contentSize更改。

- (void)viewDidLoad {
        [super viewDidLoad];
        [self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:NULL]; 
}


- (void)resizeTableAccordingToContentSize:(CGSize)newContentSize {
        CGRect superviewTableFrame  = self.tableView.superview.bounds;
        CGRect tableFrame = self.tableView.frame;
        BOOL shouldScroll = newContentSize.height > superviewTableFrame.size.height;
        tableFrame.size = shouldScroll ? superviewTableFrame.size : newContentSize;
        [UIView animateWithDuration:0.3
                                    delay:0
                                    options:UIViewAnimationOptionCurveLinear
                                    animations:^{
                            self.tableView.frame = tableFrame;
        } completion: nil];
        self.tableView.scrollEnabled = shouldScroll;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([change[NSKeyValueChangeKindKey] unsignedIntValue] == NSKeyValueChangeSetting &&
        [keyPath isEqualToString:@"contentSize"] &&
        !CGSizeEqualToSize([change[NSKeyValueChangeOldKey] CGSizeValue], [change[NSKeyValueChangeNewKey] CGSizeValue])) {
        [self resizeTableAccordingToContentSize:[change[NSKeyValueChangeNewKey] CGSizeValue]];
    } 
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
    [self resizeTableAccordingToContentSize:self.tableView.contentSize]; }

- (void)dealloc {
    [self.tableView removeObserver:self forKeyPath:@"contentSize"];
}