以编程方式制作的约束不起作用
Posted
技术标签:
【中文标题】以编程方式制作的约束不起作用【英文标题】:Programmatically made constraints are not working 【发布时间】:2019-11-01 23:52:41 【问题描述】:当我想摆脱 xib 并以编程方式进行布局时,我发现使用完全相同的约束并不像我预期的那样起作用。
我想做这个 UITableViewCell 这是一个非常简单的单元格,其右侧有一个小图标以及一个活动指示器,因此我可以切换我想查看的那个。它们在视图内,左侧是标签
这些是我在大纲视图中的约束 而且效果很好。但是,当我删除 XIB 并自己编写所有代码时,就没有任何效果了
这是我的代码:
class StandardRow: UITableViewCell
private var initialWidth: CGFloat = 20
public var fetching: Bool = false
didSet
if (fetching)
activityIndicator?.startAnimating()
else
activityIndicator?.stopAnimating()
changeImageWidth()
public var rightImage: UIImage? = nil
didSet
rightImageView?.image = rightImage
changeImageWidth()
private func changeImageWidth()
if (activityIndicator?.isAnimating) ?? false || rightImage != nil
imageWidth?.constant = initialWidth
else
imageWidth?.constant = 0
override func prepareForReuse()
valueLabel?.text = ""
imageView?.image = nil
rightImage = nil
fetching = false
textLabel?.text = ""
accessoryType = .none
//Views
private var imageContainer = UIView()
private var rightImageView = UIImageView()
private var activityIndicator: UIActivityIndicatorView? = UIActivityIndicatorView()
public var valueLabel: UILabel? = UILabel()
private var imageWidth: NSLayoutConstraint? = nil
override init(style: UITableViewCell.CellStyle = .default, reuseIdentifier: String? = nil)
super.init(style: style, reuseIdentifier: reuseIdentifier)
buildView()
required init?(coder: NSCoder)
super.init(coder: coder)
buildView()
func buildView()
contentView.addSubview(valueLabel!)
imageContainer.addSubview(rightImageView)
imageContainer.addSubview(activityIndicator!)
contentView.addSubview(imageContainer)
imageContainer.backgroundColor = .red
override func layoutSubviews()
super.layoutSubviews()
//IMAGE CONTAINER CONSTRAINTS
imageWidth = NSLayoutConstraint(item: imageContainer, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: initialWidth)
imageWidth?.priority = UILayoutPriority(rawValue: 999)
imageWidth?.isActive = true
let bottomImageContainerConstraint = NSLayoutConstraint(item: imageContainer, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottom, multiplier: 1, constant: 0)
bottomImageContainerConstraint.isActive = true
bottomImageContainerConstraint.priority = UILayoutPriority(rawValue: 999)
let topImageContainerConstraint = NSLayoutConstraint(item: imageContainer, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .top, multiplier: 1, constant: 0)
topImageContainerConstraint.isActive = true
topImageContainerConstraint.priority = UILayoutPriority(rawValue: 999)
let trailingImageContainerConstraint = NSLayoutConstraint(item: imageContainer, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1, constant: 5)
trailingImageContainerConstraint.priority = UILayoutPriority(rawValue: 999)
trailingImageContainerConstraint.isActive = true
let centerYImageContainerConstraint = NSLayoutConstraint(item: imageContainer, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1, constant: 0)
centerYImageContainerConstraint.isActive = true
centerYImageContainerConstraint.priority = UILayoutPriority(rawValue: 999)
//VALUE LABEL CONSTRAINTS
let trailingValueLabelConstraint = NSLayoutConstraint(item: valueLabel!, attribute: .trailing, relatedBy: .equal, toItem: imageContainer, attribute: .leading, multiplier: 1, constant: 5)
trailingValueLabelConstraint.isActive = true
trailingValueLabelConstraint.priority = UILayoutPriority(rawValue: 999)
let centerYValueLabelConstraint = NSLayoutConstraint(item: valueLabel!, attribute: .centerY, relatedBy: .equal, toItem: contentView, attribute: .centerY, multiplier: 1, constant: 0)
centerYValueLabelConstraint.isActive = true
centerYValueLabelConstraint.priority = UILayoutPriority(rawValue: 999)
//ACTIVITY INDICATOR CONSTRAINGS
NSLayoutConstraint(item: activityIndicator!, attribute: .trailing, relatedBy: .equal, toItem: imageContainer, attribute: .trailing, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: activityIndicator!, attribute: .leading, relatedBy: .equal, toItem: imageContainer, attribute: .leading, multiplier: 1, constant: 11).isActive = false
NSLayoutConstraint(item: activityIndicator!, attribute: .bottom, relatedBy: .equal, toItem: imageContainer, attribute: .bottom, multiplier: 1, constant: 11).isActive = false
NSLayoutConstraint(item: activityIndicator!, attribute: .top, relatedBy: .equal, toItem: imageContainer, attribute: .top, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: activityIndicator!, attribute: .centerY, relatedBy: .equal, toItem: imageContainer, attribute: .centerY, multiplier: 1, constant: 0).isActive = true
//RIGHT IMAGE VIEW CONSTRAINTS
NSLayoutConstraint(item: rightImageView, attribute: .trailing, relatedBy: .equal, toItem: activityIndicator!, attribute: .trailing, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: rightImageView, attribute: .leading, relatedBy: .equal, toItem: rightImageView, attribute: .leading, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: rightImageView, attribute: .bottom, relatedBy: .equal, toItem: activityIndicator!, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: rightImageView, attribute: .top, relatedBy: .equal, toItem: activityIndicator!, attribute: .top, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: rightImageView, attribute: .centerY, relatedBy: .equal, toItem: activityIndicator!, attribute: .centerY, multiplier: 1, constant: 0).isActive = true
//changeImageWidth()
所以我对它的来源有一些想法,首先将“translatesAutoresizingMaskIntoConstraints”默认设置为 true,但是当我在超级视图中将其设置为 false 时,我的单元格不再显示,并且在contentView,Xcode 告诉我,由于未定义的行为,我不应该这样做
我也在使用 Reveal 来调试我的 UI,然后我发现了那些特殊的值:
这不是我想要的,Reveal 报告说这些约束正在将视图的自动调整大小掩码转换为自动布局,因此它将证实先前的理论。我确实将某些约束的优先级设置为 999,否则它们会被破坏。
我实际上处于死胡同,我认为我遗漏了一些东西,但我无法确定是什么,因为我对非界面构建器约束没有足够的经验
【问题讨论】:
在代码中使用锚约束要简单得多。 不要在layoutSubviews
中添加约束。每次布局视图时,您最终都会一遍又一遍地添加重复的约束。请改用buildView
。
我认为您将translatesAutoresizingMaskIntoConstraints
设置为false
是正确的,但您应该只将您创建的子视图设置为false,不是单元格本身,也不是单元格的contentView
。尝试将 imageContainer
、rightImageView
、activityIndicator
和 valueLabel
设置为 false just。如果这可以解决您的问题(我不能 100% 确定它会),那么我会将其添加为答案。
few cmets:(1)您需要在创建单元格时设置约束,而不是在 layoutSubviews 中(2)您正在为容器设置顶部、底部和 centerY 约束 - 如果您锚定顶部&底部你不需要做中心(3)使用锚,因为它们更容易阅读和调试(4)如果你将所有优先级设置为相同,那么设置它们没有意义(5)你似乎没有为标签设置任何大小或前导约束。我将在下面将它们重写为锚点并尝试解决这些问题。
@CheshireChild 仍然需要带有锚点等的示例代码,或者您现在可以了吗?
【参考方案1】:
试试 Anchors,它更容易。
示例
var redView = UIView()
redView.backgroundColor = .red
anyView.addsubView(redView)
redView.translatesAutoresizingMaskIntoConstraints = false
redView.centerXAnchor.constraint(equalTo: self.parentView.centerXAnchor).isActive = true
redView.centerYAnchor.constraint(equalTo: self.parentView.centerYAnchor).isActive = true
redView.heightAnchor.constraint(equalToConstant: 100).isActive = true
redView.widthAnchor.constraint(equalToConstant: 100).isActive = true
【讨论】:
【参考方案2】:您可以将相同的方法添加到您的 UIView 扩展中
func constrainToEdges(_ subview: UIView, top: CGFloat = 0, bottom: CGFloat = 0, leading: CGFloat = 0, trailing: CGFloat = 0)
subview.translatesAutoresizingMaskIntoConstraints = false
let topContraint = NSLayoutConstraint(
item: subview,
attribute: .top,
relatedBy: .equal,
toItem: self,
attribute: .top,
multiplier: 1.0,
constant: top)
let bottomConstraint = NSLayoutConstraint(
item: subview,
attribute: .bottom,
relatedBy: .equal,
toItem: self,
attribute: .bottom,
multiplier: 1.0,
constant: bottom)
let leadingContraint = NSLayoutConstraint(
item: subview,
attribute: .leading,
relatedBy: .equal,
toItem: self,
attribute: .leading,
multiplier: 1.0,
constant: leading)
let trailingContraint = NSLayoutConstraint(
item: subview,
attribute: .trailing,
relatedBy: .equal,
toItem: self,
attribute: .trailing,
multiplier: 1.0,
constant: trailing)
addConstraints([
topContraint,
bottomConstraint,
leadingContraint,
trailingContraint])
【讨论】:
【参考方案3】:我建议使用此framework 以编程方式构建基于约束的布局,它使过程简单快捷。以该单元格的 contentView 设置为例:
contentView.addSubview(descriptionLabel)
contentView.addSubview(amountLabel)
contentView.addSubview(dateLabel)
contentView.addSubview(bottomRightLabel)
constrain(descriptionLabel, amountLabel, dateLabel, bottomRightLabel) desc, amount, date, bottomRight in
desc.top == desc.superview!.top + 16
desc.left == desc.superview!.left + 16
desc.right <= amount.left + 12
desc.bottom == date.top - 12
amount.centerY == desc.centerY
amount.right == amount.superview!.right - 12
date.left == date.superview!.left + 16
date.right <= bottomRight.left - 12
date.bottom == date.superview!.bottom - 16
bottomRight.centerY == date.centerY
bottomRight.right == bottomRight.superview!.right - 12
【讨论】:
以上是关于以编程方式制作的约束不起作用的主要内容,如果未能解决你的问题,请参考以下文章