代码之家  ›  专栏  ›  技术社区  ›  Amber K

如何显示tableview之外的可滚动父视图?

  •  0
  • Amber K  · 技术社区  · 6 年前

    我有一个场景,需要显示一个父视图,其中包含一长串可重用项的阴影和角半径。我使用表视图来显示项目。但是我坚持让我的tableview扩展到它的contentSize。它起作用,但不准确。有什么解决办法吗?

    期望结果: enter image description here

    我使用了下面的参考来自动调整tableview的大小。 Self Sizing UITableView

    我做了一些修改如下:

    final class SelfSizedTableView: UITableView {
    
        var maxHeight = CGFloat.greatestFiniteMagnitude
    
        override func reloadData() {
            super.reloadData()
            self.invalidateIntrinsicContentSize()
            self.layoutIfNeeded()
        }
    
        override var intrinsicContentSize: CGSize {
            let height = min(contentSize.height, maxHeight)
            let size = CGSize(width: contentSize.width, height: height)
            return size
        }
    
    }
    

    class MyContainerViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
        // MARK: - IBOutlets
        @IBOutlet weak var parentTableView: UITableView!
    
        // MARK: - Life Cycle
        override func viewDidLoad() {
            super.viewDidLoad()
            setupViews()
        }
    
        private func estimateDataHeight() -> CGFloat {
            let detailCellHeight: CGFloat = 32
            let headingCellHeight: CGFloat = 43
            let headings: CGFloat = headingCellHeight*2
            let detailsHeight: CGFloat = detailCellHeight*4
            let baseHeight = headings + detailsHeight
            let membersHeight =
                CGFloat(sectionsArray.count) * detailCellHeight
            return baseHeight + membersHeight
        }
    }
    
    // MARK: - UITableViewDataSource
    extension MyContainerViewController {
    
        func tableView(_ tableView: UITableView,
                       numberOfRowsInSection section: Int) -> Int {
            return 1
        }
    
        func tableView(_ tableView: UITableView,
                       cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let id = String(describing: MyContainerTVCell.self)
            guard let cell = tableView
                .dequeueReusableCell(withIdentifier: id, for: indexPath)
                as? MyContainerTVCell else {
                    return UITableViewCell()
            }
    
            cell.policyDetails = dataSource
            // my cheat/trick doesn't work on large data.
            DispatchQueue.main.asyncAfter(deadline: .now()+0.4) {
                tableView.beginUpdates()
                cell.tableView.layoutIfNeeded()
                cell.tableView.reloadData() // the overridden one
                tableView.endUpdates()
            }
            return cell
        }
    }
    
    extension MyContainerViewController {
    
        func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            return UITableViewAutomaticDimension
        }
    
        func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
            return estimateDataHeight()
        }
    }
    

    My cell类具有自大小的tableView和containerView:

    class MyContainerTVCell: UITableViewCell, UITableViewDataSource, UITableViewDelegate {
    
        // MARK: - IBOutlets
        @IBOutlet weak var containerView: UIView!
        @IBOutlet weak var shadowView: UIView!
        @IBOutlet weak var tableView: SelfSizedTableView!
    
        // MARK: - Properties
        let titles = ["Email ID:", "Mobile Number:", "Address:", "ID: "] // first section data array
        let moreData: [String] = [] // remaining reusable sections array
    
        // no of subsequent sections for moreData array type
        var numberOfSections: Int {
            return 4
        }
    
        // MARK: -
        var dataSource: MyDataSource!
    
        // MARK: - Life Cycle
        override func awakeFromNib() {
            super.awakeFromNib()
            setupView()
        }
    
        override func layoutSubviews() {
            super.layoutSubviews()
        }
    
        // MARK: - Setup
        func setupView() {
            containerView.rounded(with: 10)
            shadowView.layer.applyShadow()
    
            tableView.dataSource = self
            tableView.delegate = self
        }
    }
    
    // MARK: - UITableViewDataSource
    extension MyContainerTVCell {
        func numberOfSections(in tableView: UITableView) -> Int {
            return numberOfSections + 1
        }
    
        func tableView(_ tableView: UITableView,
                       numberOfRowsInSection section: Int) -> Int {
            if section == 0 { return titles.count + 1 }
            else if section == 1 { return moreData.count + 1 }
            else { return moreData.count }
        }
    
        func tableView(_ tableView: UITableView,
                       cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let headerID = String(describing: MyHeaderTVCell.self)
            let itemID = String(describing: MyItemTVCell.self)
    
            switch indexPath.section {
            case 0:
                if indexPath.row == 0 {
                    guard let cell = tableView
                        .dequeueReusableCell(withIdentifier: headerID, for: indexPath)
                        as? MyHeaderTVCell else {
                            return UITableViewCell()
                    }
                    cell.titleLabel.text = dataSource.title
                    return cell
                } else {
                    guard let cell = tableView
                        .dequeueReusableCell(withIdentifier: itemID, for: indexPath)
                        as? MyItemTVCell else {
                            return UITableViewCell()
                    }
                    let item = titles[indexPath.row-1]
                    cell.titleLabel.text = item
                    cell.separatorView.isHidden = true
                    let data: String
                    switch indexPath.row {
                    case 1:
                        data = dataSource.emailID
                    case 2:
                        data = dataSource.mobileNo
                    case 3:
                        data = dataSource.address
                    case 4:
                        data = dataSource.name
                    case 5:
                        data = dataSource.age
                    case 6:
                        data = dataSource.id
                    case 7:
                        data = dataSource.office
                    case 8:
                        data = dataSource.academic
                    default: data = String()
                    }
                    cell.detailLabel.text = data
                    return cell
                }
    
            case 1:
                if indexPath.row == 0 {
                    guard let cell = tableView
                        .dequeueReusableCell(withIdentifier: headerID, for: indexPath)
                        as? MyHeaderTVCell else {
                            return UITableViewCell()
                    }
                    cell.titleLabel.text = "More Data"
                    return cell
                } else {
                    guard let cell = tableView
                        .dequeueReusableCell(withIdentifier: itemID, for: indexPath)
                        as? MyItemTVCell else {
                            return UITableViewCell()
                    }
                    let sectionIndex = indexPath.section-1
                    guard sectionIndex <= numberOfSections-1,
                        let section = sectionsArray?[indexPath.section-1] else {
                            return UITableViewCell()
                    }
                    cell.titleLabel.text = moreData[indexPath.row-1]
                    cell.separatorView.isHidden = true
                    switch indexPath.row {
                    case 1:
                        cell.detailLabel.text = section.a
                    case 2:
                        cell.detailLabel.text = section.b
                    case 3:
                        cell.detailLabel.text = "\(section.c ?? 0)"
                    case 4:
                        cell.detailLabel.text = section.d
                    case 5:
                        cell.detailLabel.text = section.e
                    case 6:
                        cell.detailLabel.text = section.f
                        if indexPath.section < numberOfSections {
                            cell.separatorView.isHidden = false
                        }
                    default: break
                    }
                    return cell
                }
            default:
                guard let cell = tableView
                    .dequeueReusableCell(withIdentifier: itemID, for: indexPath)
                    as? MyItemTVCell else {
                        return UITableViewCell()
                }
                let sectionIndex = indexPath.section-1
                guard sectionIndex <= numberOfSections-1,
                    let section = sectionsArray?[indexPath.section-1] else {
                        return UITableViewCell()
                }
                cell.titleLabel.text = moreData[indexPath.row]
                cell.separatorView.isHidden = true
                switch indexPath.row {
                case 0:
                    cell.detailLabel.text = section.a
                case 1:
                    cell.detailLabel.text = section.b
                case 2:
                    cell.detailLabel.text = "\(section.c ?? 0)"
                case 3:
                    cell.detailLabel.text = section.d
                case 4:
                    cell.detailLabel.text = section.e
                case 5:
                    cell.detailLabel.text = section.f
                    if indexPath.section < numberOfSections {
                        cell.separatorView.isHidden = false
                    }
                default: break
                }
                return cell
            }
        }
    }
    
    // MARK: - UITableViewDelegate
    extension MyContainerTVCell {
        func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            return UITableViewAutomaticDimension
        }
    
        func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
            if indexPath.section == 0 && indexPath.row == 0 { return 43 }
            if indexPath.section == 1 && indexPath.row == 0 { return 43 }
            return 32
        }
    }
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   Yevgeniy Leychenko    6 年前

    你为什么要扩张 tableView 它的内容大小,使其可滚动,当 表视图 是否已可滚动?

    但是,如果屏幕上除了表之外还有其他内容,并且希望它们一起滚动,则需要将所有内容嵌入到 UIScrollView .

    然后,为您设置高度约束 在xib/情节提要中具有任何值。 然后你可以这样做:

    // in your view controller
    private var heightObservation: NSKeyValueObservation?
    
    // called once, for example, in viewDidLoad()
    private func setupTableView() {
        ...
    
        observation = tableView.constraintFrameHeightToContentSizeHeight()
    }
    
    extension UITableView {
    
        func constraintFrameHeightToContentSizeHeight() -> NSKeyValueObservation {
            return observe(\.contentSize, changeHandler: { (tableView, _) in
                tableView.heightConstraint?.constant = tableView.contentSize.height
            })
        }
    }
    
    // find height constraint
    extension UIView {
    
        var heightConstraint: NSLayoutConstraint? {
            return constraints.first(where: { $0.firstAttribute == .height })
        }
    }
    

    别忘了取消选中xib/序列图像板中该表视图的“已启用滚动”。