简体   繁体   English

自调整tableview单元内的自调整tableview

[英]Self sizing tableview inside self sizing tableview cell

Let's say I have hierarchy like this: 假设我有这样的层次结构:

*TableViewCell
**TableView
***TableViewCell

and all of them should be resizable. 所有这些都应调整大小。 Did someone face this kind of problem? 有人遇到过这种问题吗? In past I've used many workarounds like systemLayoutSizeFitting or precalculation of height in heightForRowAt , but it always breaks some constraints, because TableViewCell has height constraint equal to estimated row height and there appear some kinds of magic behavior. 过去,我使用过许多变通方法,例如systemLayoutSizeFittingheightForRowAt的height的预先计算,但它总是会打破一些约束,因为TableViewCell高度约束等于估算的行高,并且会出现某种魔术行为。 Any ways to make this live? 有什么办法可以使它成为现实?

Current workaround: 当前解决方法:

class SportCenterReviewsTableCell: UITableViewCell, MVVMView {
    var tableView: SelfSizedTableView = {
        let view = SelfSizedTableView(frame: .zero)
        view.clipsToBounds = true
        view.tableFooterView = UIView()
        view.separatorStyle = .none
        view.isScrollEnabled = false
        view.showsVerticalScrollIndicator = false
        view.estimatedRowHeight = 0
        if #available(iOS 11.0, *) {
            view.contentInsetAdjustmentBehavior = .never
        } else {
            // Fallback on earlier versions
        }

        return view
    }()

    private func markup() {
        contentView.addSubview(tableView)
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(ReviewsTableViewCell.self, forCellReuseIdentifier: "Cell")
        tableView.snp.makeConstraints() { make in
            make.top.equalTo(seeAllButton.snp.bottom).offset(12)
            make.left.equalTo(contentView.snp.left)
            make.right.equalTo(contentView.snp.right)
            make.bottom.lessThanOrEqualTo(contentView.snp.bottom)
        }
    }


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ReviewsTableViewCell

        cell.viewModel = viewModel.cellViewModels[indexPath.row]

        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! ReviewsTableViewCell

        cell.viewModel = viewModel.cellViewModels[indexPath.row]
        cell.setNeedsLayout()
        cell.layoutIfNeeded()
        let size = cell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize, withHorizontalFittingPriority: .defaultHigh, verticalFittingPriority: .defaultLow)

        return size.height
    }
}

Self sizing tableView class: 自定义tableView类:

class SelfSizedTableView: UITableView {
    override func reloadData() {
        super.reloadData()
        self.invalidateIntrinsicContentSize()
        self.layoutIfNeeded()
    }

    override var intrinsicContentSize: CGSize {
        self.setNeedsLayout()
        self.layoutIfNeeded()
        return contentSize
    }
}

This is actually not an answer to the question, but just an explanation. 这实际上不是对问题的答案,而只是一种解释。
(Wrote here because of the character count limitation for the comments). (在这里写是因为注释的字符数限制)。

The thing is that you're trying to insert a vertically scrollable view inside another vertically scrollable view. 问题是您试图在另一个垂直滚动视图内插入一个垂直滚动视图。 If you don't disable the nested tableview's scroll ability, you will have a glitch while scrolling, because the system wouldn't know to whom pass the scroll event (to the nested tableview, or to the parent tableview). 如果不禁用嵌套表视图的滚动功能,则滚动时会出现故障,因为系统将不知道向谁传递滚动事件(嵌套表视图或父表视图)。 So in our case, you'll have to disable the "scrollable" property for the nested tableviews, hence you'll have to set the height of the nested tableview to be equal to its content size. 因此,在我们的案例中,您必须为嵌套表视图禁用“ scrollable”属性,因此必须将嵌套表视图的高度设置为等于其内容大小。 But this way you will lose the advantages of tableview (ie cell reusing advantage) and it will be the same as using an actual UIScrollView. 但是这样一来,您将失去tableview的优势(即单元重用优势),并且与使用实际的UIScrollView相同。 But, on the other hand, as you'll have to set the height to be equal to its content size, then there is no reason to use UIScrollView at all, you can add your nested cells to a UIStackView, and you tableview will have this hierarchy: 但是,另一方面,由于必须将高度设置为等于其内容大小,因此根本没有理由使用UIScrollView,您可以将嵌套的单元格添加到UIStackView中,并且tableview将具有此层次结构:

*TableView
**TableViewCell
***StackView
****Items
****Items
****Items
****Items

But again, the right solution is using multi-sectional tableview. 但同样,正确的解决方案是使用多部分表格视图。 Let your cells be section headers of the tableview, and let inner cells be the rows of the tableview. 让您的单元格成为表格视图的节标题,让内部单元格成为表格视图的行。

here is an example of how to make a tableview inside a table view cell with automatic height for the cells. 这是一个如何在表格视图单元格内创建具有自动高度的表格视图的示例。

You should use the 'ContentSizedTableView' class for the inner tableViews. 您应该对内部tableView使用“ ContentSizedTableView”类。

class ViewController: UIViewController {

    @IBOutlet weak var outerTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        outerTableView.rowHeight = UITableView.automaticDimension
        outerTableView.estimatedRowHeight = UITableView.automaticDimension
        outerTableView.delegate = self
        outerTableView.dataSource = self
    }

}

final class ContentSizedTableView: UITableView {
    override var contentSize:CGSize {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        sizeToFit()
        return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
    }
}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return 10
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TableTableViewCell

        return cell!
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableView.automaticDimension
    }
}
  • Use xib files to simplify the hierarchy. 使用xib文件简化层次结构。
  • Get a tableView on your storyboard, and create a nib file for your tableViewCell(say CustomTableViewCell). 在情节提要上获取tableView,并为tableViewCell(例如CustomTableViewCell)创建一个nib文件。 Inside it create a tableView and again create one more tableViewCell xib file. 在其中创建一个tableView,然后再次创建一个tableViewCell xib文件。 Now, no need of setting labels into your xib file,(if you want only labels in cells and nothing else, if not, there is another way of adding constraints) 现在,无需在xib文件中设置标签(如果您只希望在单元格中放置标签,而无需其他标签,则不需要添加约束的另一种方法)
  • Say you have an array of text, some strings are long and some are short. 假设您有一组文本,有些字符串很长,有些则很短。
  • register nib file in CustomTableViewCell and extend it to use Delegate and DataSource. 在CustomTableViewCell中注册笔尖文件,并将其扩展为使用Delegate和DataSource。
  • register this CustomTableViewCell in ViewController. 在ViewController中注册此CustomTableViewCell。
  • While declaring a cell in CustomTableViewCell, just do= 在CustomTableViewCell中声明单元格时,只需执行=
  • cell.textLabel?.text = content cell.textLabel?.text =内容
  • cell.textLabel?.numberOfLines = 0 cell.textLabel?.numberOfLines = 0
  • Use heightForRowAt to set outer tableViewCell's height, and let the inner tableView to scroll inside. 使用heightForRowAt设置外部tableViewCell的高度,并让内部tableView向内滚动。

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

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