繁体   English   中英

调整大小 UITableView 以适应内容

[英]Resizing UITableView to fit content

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

我想为桌子找到一个sizeToFit解决方法。 表格的框架设置为其所有内容的高度。

谁能建议我如何实现这一目标?

其实我自己找到了答案。

我只是为tableView.frame创建一个新的CGRectheighttable.contentSize.height

这将UITableViewheight设置为其内容的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;
    });

没有 KVO、DispatchQueue 或自己设置约束的 Swift 5 和 4.2 解决方案。

此解决方案基于Gulz 的回答

1)创建一个UITableView的子类:

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

2) 将UITableView添加到您的布局并在所有方面设置约束。 将它的类设置为ContentSizedTableView

3) 您应该会看到一些错误,因为 Storyboard 没有考虑我们的子类的intrinsicContentSize 通过打开大小检查器并将 internalContentSize 覆盖为占位符值来解决此问题。 这是设计时的覆盖。 在运行时,它将使用我们的ContentSizedTableView类中的覆盖


更新:更改了 Swift 4.2 的代码。 如果您使用的是以前的版本,请使用UIViewNoIntrinsicMetric而不是UIView.noIntrinsicMetric

快速解决方案

按着这些次序:

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

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

     @IBOutlet var tableHeight: NSLayoutConstraint!
  3. 然后您可以使用以下代码动态更改表格的高度:

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

如果最后一行被截断,尝试在willDisplay cell函数中调用viewWillLayoutSubviews()

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

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

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

为 table view 上的 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;
    }

希望这会帮助你。

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

0)将 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:截图: 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
    }

希望这可以帮助

如果您不想自己跟踪 table view 的内容大小更改,您可能会发现这个子类很有用。

protocol ContentFittingTableViewDelegate: UITableViewDelegate {
    func tableViewDidUpdateContentSize(_ tableView: UITableView)
}

class ContentFittingTableView: UITableView {

    override var contentSize: CGSize {
        didSet {
            if !constraints.isEmpty {
                invalidateIntrinsicContentSize()
            } else {
                sizeToFit()
            }

            if contentSize != oldValue {
                if let delegate = delegate as? ContentFittingTableViewDelegate {
                    delegate.tableViewDidUpdateContentSize(self)
                }
            }
        }
    }

    override var intrinsicContentSize: CGSize {
        return contentSize
    }

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        return contentSize
    }
}

如果您的 contentSize 不正确,这是因为它基于估计的行高度(自动),请在此之前使用

tableView.estimatedRowHeight = 0;

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

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

*编辑:

在我尝试了所有解决方案之后,每个解决方案都在修复某些问题,但并不完全,是完全解释和解决此问题的答案。

我的做法有点不同,实际上我的 TableView 在滚动视图中,所以我不得不将高度约束设置为 0。

然后在运行时我做了以下更改,

       func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
            self.viewWillLayoutSubviews()
       }
    
       override func viewWillLayoutSubviews() {
            super.updateViewConstraints()
             DispatchQueue.main.async {
               self.tableViewHeightConstraint?.constant = self.myTableView.contentSize.height
               self.view.layoutIfNeeded()
          }
       }

Swift 5 解决方案

遵循以下四个步骤:

  1. 从故事板设置 tableview 的高度约束。

  2. 从情节@IBOutlet拖动高度约束并在视图控制器文件中为其创建@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 } } } }

如果您使用 AutoLayout,有一个更好的方法来做到这一点:更改确定高度的约束。 只需计算表格内容的高度,然后找到约束并更改它。 这是一个示例(假设确定表格高度的约束实际上是具有关系“Equal”的高度约束):

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

Mimo 的回答Anooj VM 的回答都很棒,但是如果您有一个大列表,则有一个小问题,框架的高度可能会切断您的某些单元格。

所以。 我稍微修改了答案:

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

这对我使用自动布局有效,表格视图只有一个部分。

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,我需要设置边界以能够遵守单元格定义的自动布局规则。 我将大小设置为一个应该足够大的任意值。 实际尺寸稍后由自动布局系统计算。

基于fl034 的回答

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

作为 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"];
}

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

你可以试试这个自定义AGTableView

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

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

注意如果设置 Height 有任何问题,则使用yourTableView.layoutSubviews()

基于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);
            }
        }
    }

我的 Swift 5 实现是将 tableView 的高度约束设置为其内容的大小 ( contentSize.height )。 此方法假定您使用的是自动布局。 这段代码应该放在cellForRowAt tableView 方法中。

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

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

 extension UIView {
func updateHeight(_ height:NSLayoutConstraint)
{
    
    let newSize = CGSize(width: self.frame.size.width, height: CGFloat(MAXFLOAT))
    let fitSize : CGSize = self.sizeThatFits(newSize)
    
    height.constant = fitSize.height
    
   
}
}

实施::

@IBOutlet weak var myTableView: UITableView!
@IBOutlet weak var myTableVieweHeight: NSLayoutConstraint!
//(call it whenever tableView is updated inside/outside delegate methods)
myTableView.updateHeight(myTableVieweHeigh)

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

就我而言,我的管理方式是。 给出表格视图的任何恒定高度。 创建表视图高度的出口,然后在重新加载 tableView 的地方调用以下 function。

private func manageHeight(){
        tableViewHeight.constant=CGFloat.greatestFiniteMagnitude
        tableView.reloadData()
        tableView.layoutIfNeeded()
        tableViewHeight.constant=tableView.contentSize.height
    }

注意:tableView 是 tableView 的出口,tableViewHeight 是 tableView 高度的出口。

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

这避免了大量的计算和对布局的调用。

swift 3 中的 Mu 解决方案:在viewDidAppear调用此方法

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM