我有一个带有TableView
的iOS应用,该应用的所有UI均使用autolayout
和Swift
以编程方式完成。
直到最近,所有UI都运行良好,我必须在UIView
内添加一个新组件(自定义UITableViewCell
),当显示或隐藏新组件时,其单元格高度将发生变化。单元格的高度不正确,因此UITableViewCell
内部的视图变得一团糟。
检查“调试视图层次结构”后,我发现UITableViewCell
的高度不同于UITableViewCellContentView
。
组件何时显示:
UITableViewCell
的内容视图比预期的要短(如果隐藏了组件,则高度正确)隐藏组件的时间:
UITableViewCell
的内容视图长于预期(如果显示组件,则高度正确)我不太确定真正的问题是什么。当我切换要显示或不显示的组件时,我将执行以下操作:
// Update the constraints status
var componentIsShown: Bool = .....
xxxConstraints?.isActive = componentIsShown
yyyConstraints?.isActive = !componentIsShown
// Update UI
layoutIfNeeded()
view.setNeedsUpdateConstraints()
tableView.beginUpdates()
tableView.endUpdates()
在我看来,当我切换要显示或不显示的组件时,UITableViewCell
会立即更新,但是内容视图使用以前的数据来更新高度。如果是这个问题,我该如何更新内容视图的高度?
如果这不是问题,是否有任何解决建议或进一步调查?
谢谢
===================
于2018-08-29更新:
已随附此问题的代码。 在MyBaseView(带有红色alpha bg的视图)中单击topMainContainerView,将切换是否显示hiddenView。
在ViewController.swift中:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell: MyViewCell
if let theCell = tableView.dequeueReusableCell(withIdentifier: "cell") as? MyViewCell
{
cell = theCell
}
else
{
cell = MyViewCell(reuseIdentifier: "cell")
}
cell.setViewCellDelegate(delegate: self)
return cell
}
MyViewCell.swift
class MyViewCell: MyViewParentCell
{
var customView: MyView
required init?(coder aDecoder: NSCoder)
{
return nil
}
override init(reuseIdentifier: String?)
{
customView = MyView()
super.init(reuseIdentifier: reuseIdentifier)
selectionStyle = .none
}
override func initViews()
{
contentView.addSubview(customView)
}
override func initLayout()
{
customView.translatesAutoresizingMaskIntoConstraints = false
customView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
customView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
customView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
customView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
}
override func setViewCellDelegate(delegate: MyViewCellDelegate)
{
super.setViewCellDelegate(delegate: delegate)
customView.delegate = delegate
customView.innerDelegate = self
}
}
MyViewParentCell.swift:
protocol MyViewCellDelegate
{
func reloadTableView()
}
protocol MyViewCellInnerDelegate
{
func viewCellLayoutIfNeeded()
}
class MyViewParentCell: UITableViewCell
{
private var delegate: MyViewCellDelegate?
var innerDelegate: MyViewCellInnerDelegate?
required init?(coder aDecoder: NSCoder)
{
return nil
}
init(reuseIdentifier: String?)
{
super.init(style: UITableViewCellStyle.default, reuseIdentifier: reuseIdentifier)
selectionStyle = .none
initViews()
initLayout()
}
func initViews()
{
}
func initLayout()
{
}
override func layoutSubviews()
{
}
func setViewCellDelegate(delegate: MyViewCellDelegate)
{
self.delegate = delegate
}
}
extension MyViewParentCell: MyViewCellInnerDelegate
{
func viewCellLayoutIfNeeded()
{
print("MyViewParentCell viewCellLayoutIfNeeded")
setNeedsUpdateConstraints()
layoutIfNeeded()
}
}
MyView.swift
class MyView: MyParentView
{
private var mainView: UIView
// Variables
var isViewShow = true
// Constraint
private var mainViewHeightConstraint: NSLayoutConstraint?
private var hiddenViewHeightConstraint: NSLayoutConstraint?
private var hiddenViewPosYHideViewConstraint: NSLayoutConstraint?
private var hiddenViewPosYShowViewConstraint: NSLayoutConstraint?
// Constant:
let viewSize = UIScreen.main.bounds.width
// Init
override init()
{
mainView = UIView(frame: CGRect(x: 0, y: 0, width: viewSize, height: viewSize))
super.init()
}
required init?(coder aDecoder: NSCoder)
{
return nil
}
override func initViews()
{
super.initViews()
topMainContainerView.addSubview(mainView)
}
override func initLayout()
{
super.initLayout()
//
mainView.translatesAutoresizingMaskIntoConstraints = false
mainView.topAnchor.constraint(equalTo: topMainContainerView.topAnchor).isActive = true
mainView.bottomAnchor.constraint(equalTo: topMainContainerView.bottomAnchor).isActive = true
mainView.leadingAnchor.constraint(equalTo: topMainContainerView.leadingAnchor).isActive = true
mainView.widthAnchor.constraint(equalToConstant: viewSize).isActive = true
mainView.heightAnchor.constraint(equalToConstant: viewSize).isActive = true
mainViewHeightConstraint = mainView.heightAnchor.constraint(equalToConstant: viewSize)
mainViewHeightConstraint?.isActive = true
}
override func toggle()
{
isViewShow = !isViewShow
print("toggle: isViewShow is now (\(isViewShow))")
setViewHidden()
}
private func setViewHidden()
{
UIView.animate(withDuration: 0.5) {
if self.isViewShow
{
self.hiddenViewBottomConstraint?.isActive = false
self.hiddenViewTopConstraint?.isActive = true
}
else
{
self.hiddenViewTopConstraint?.isActive = false
self.hiddenViewBottomConstraint?.isActive = true
}
self.layoutIfNeeded()
self.needsUpdateConstraints()
self.innerDelegate?.viewCellLayoutIfNeeded()
self.delegate?.reloadTableView()
}
}
}
MyParentView.swift
class MyParentView: MyBaseView
{
var delegate: MyViewCellDelegate?
var innerDelegate: MyViewCellInnerDelegate?
override init()
{
super.init()
}
required init?(coder aDecoder: NSCoder)
{
return nil
}
override func initViews()
{
super.initViews()
}
override func initLayout()
{
super.initLayout()
translatesAutoresizingMaskIntoConstraints = false
}
}
MyBaseView.swift
class MyBaseView: UIView
{
var topMainContainerView: UIView
var hiddenView: UIView
var bottomActionContainerView: UIView
var bottomSeparator: UIView
var hiddenViewTopConstraint: NSLayoutConstraint?
var hiddenViewBottomConstraint: NSLayoutConstraint?
// Layout constratint
var descriptionWidthConstraint: NSLayoutConstraint?
var moreMainTopAnchorConstraint: NSLayoutConstraint?
var moreMainBottomAnchorConstraint: NSLayoutConstraint?
var separatorTopAnchorToActionBarConstraint: NSLayoutConstraint?
var separatorTopAnchorToPartialCommentConstraint: NSLayoutConstraint?
// Constant
let paddingX: CGFloat = 10
let InnerPaddingY: CGFloat = 9
init()
{
topMainContainerView = UIView()
hiddenView = UIView()
bottomActionContainerView = UIView()
bottomSeparator = UIView()
super.init(frame: .zero)
initViews()
initLayout()
}
required init?(coder aDecoder: NSCoder)
{
return nil
}
func initViews()
{
let borderColor = UIColor.gray
backgroundColor = UIColor(red: 211/255.0, green: 211/255.0, blue: 1, alpha: 1)
topMainContainerView.backgroundColor = UIColor.red.withAlphaComponent(0.7)
let gesture = UITapGestureRecognizer(target: self, action: #selector(toggle))
topMainContainerView.addGestureRecognizer(gesture)
// Hidden View
hiddenView.backgroundColor = UIColor.yellow
hiddenView.layer.cornerRadius = 50
// Action
bottomActionContainerView.backgroundColor = UIColor.blue
bottomSeparator.backgroundColor = borderColor
// Add hiddenView first, so it will hide behind main view
addSubview(hiddenView)
addSubview(topMainContainerView)
addSubview(bottomActionContainerView)
addSubview(bottomSeparator)
}
func initLayout()
{
// MARK: Main
topMainContainerView.translatesAutoresizingMaskIntoConstraints = false
topMainContainerView.topAnchor.constraint(equalTo: topAnchor, constant: 30).isActive = true
topMainContainerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
topMainContainerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20).isActive = true
topMainContainerView.heightAnchor.constraint(equalToConstant: 150).isActive = true
// Hidden View
hiddenView.translatesAutoresizingMaskIntoConstraints = false
hiddenViewTopConstraint = hiddenView.topAnchor.constraint(equalTo: topMainContainerView.bottomAnchor)
hiddenViewTopConstraint?.isActive = true
hiddenView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
hiddenView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20).isActive = true
hiddenViewBottomConstraint = hiddenView.bottomAnchor.constraint(equalTo: topMainContainerView.bottomAnchor)
hiddenViewBottomConstraint?.isActive = false
hiddenView.heightAnchor.constraint(equalToConstant: 100).isActive = true
// MARK: Bottom
bottomActionContainerView.translatesAutoresizingMaskIntoConstraints = false
bottomActionContainerView.topAnchor.constraint(equalTo: hiddenView.bottomAnchor, constant: InnerPaddingY).isActive = true
bottomActionContainerView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
bottomActionContainerView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
bottomActionContainerView.heightAnchor.constraint(equalToConstant: 32).isActive = true
// MARK: Separator
bottomSeparator.translatesAutoresizingMaskIntoConstraints = false
separatorTopAnchorToPartialCommentConstraint = bottomSeparator.topAnchor.constraint(equalTo: bottomActionContainerView.bottomAnchor, constant: InnerPaddingY)
separatorTopAnchorToActionBarConstraint = bottomSeparator.topAnchor.constraint(equalTo: bottomActionContainerView.bottomAnchor, constant: InnerPaddingY)
separatorTopAnchorToPartialCommentConstraint?.isActive = false
separatorTopAnchorToActionBarConstraint?.isActive = true
bottomSeparator.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
bottomSeparator.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
bottomSeparator.heightAnchor.constraint(equalToConstant: 10).isActive = true
bottomSeparator.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
self.hiddenViewBottomConstraint?.isActive = false
self.hiddenViewTopConstraint?.isActive = true
}
@objc func toggle()
{
}
}
答案 0 :(得分:0)
contentView的布局未更新。您应该尝试
cell.contentView.layoutIfNeeded()
尝试并分享结果。
答案 1 :(得分:0)
我终于发现我应该在MyViewParentCell.swift中调用super.layoutSubviews()
或直接删除该函数来解决该问题。
override func layoutSubviews()
{
super.layoutSubviews()
}