如何显示在 tableview 之外并且可滚动的父视图?
Posted
技术标签:
【中文标题】如何显示在 tableview 之外并且可滚动的父视图?【英文标题】:How to show a parent view which is outside tableview and is scrollable? 【发布时间】:2018-11-14 05:10:39 【问题描述】:我有一个场景,我需要显示一个带有阴影和圆角半径的父视图,其中包含一长串可重复使用的项目。我使用 tableView 来显示项目。但我坚持让我的 tableview 扩展与其 contentSize 一样多。它有效但不准确。有什么解决办法吗?
编辑:
期望的结果:
我使用以下参考来进行自定尺寸表视图。 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
我使用了一个父 tableView,其中一个单元格包含我的 containerView 并嵌入了这个自定大小的 tableView。
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()
我的单元格类有自己大小的 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
【问题讨论】:
你有截图要分享吗? 请显示代码。你试过什么?我看不出为什么 Objective-c 和 swift 是这里的标签,因为你的问题中没有代码。 @Glenn 我在编辑中添加了详细信息。如果对理解代码或预期结果有任何疑问,请询问。 @MikeTaverne 我添加了代码并编辑了标签。如果有任何疑问,请查看并询问。 这是正确的方法还是其他解决方案? 【参考方案1】:当tableView
已经可以滚动时,为什么要将tableView
扩展到其内容大小以使其可滚动?
但是,如果您在屏幕上还有一些其他内容,除了表格,并且您希望它们一起滚动,那么您需要将所有内容嵌入到UIScrollView
。
然后,在 xib/storyboard 中为您的 tableView
设置一个高度限制,并具有任何值。
然后你可能会做这样的事情:
// 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/storyboard 中取消选中该表格视图的“启用滚动”。
【讨论】:
以上是关于如何显示在 tableview 之外并且可滚动的父视图?的主要内容,如果未能解决你的问题,请参考以下文章
SWRevealController TableView 滚动状态栏
如何滚动到 UITableView 或 UICollectionView 中的最后一个单元格之外
如何将 panGestureRecognizer 添加到 TableView,并且在识别平底锅时仍然让 tableview 滚动?