未按预期应用约束

Posted

技术标签:

【中文标题】未按预期应用约束【英文标题】:Constraints are not being applied as expected 【发布时间】:2020-03-12 21:49:42 【问题描述】:

我有一个在多个地方使用的自定义 UITextField,但我遇到了一个奇怪的问题,即它仅在我使用的少数页面上受到限制。自定义类有一些我在初始化时添加的东西:

我使用 UITextField 的顶部、左侧和右侧约束作为子视图添加的 UILabel。这在 UITextField 的 awakeFromNib 函数执行时被调用
class FormTextField: UITextField 
    override func awakeFromNib() 
        self.layer.masksToBounds = true
        self.addErrorLabel()
    

    ...

    private func addErrorLabel() 
        errorLabel = UILabel(frame: CGRect(x: self.frame.minX, y: self.frame.maxY-5, width: self.frame.width, height: self.frame.height))
        errorLabel.textColor = UIColor.red
        errorLabel.backgroundColor = UIColor.black
        errorLabel.font = UIFont.systemFont(ofSize: 12.0)
        errorLabel.text = ""
        errorLabel.alpha = 0.0
        errorLabel.numberOfLines = 0

        errorLabel.textAlignment = YTPAppManager.language == .arabic ? .right : .left

        if (self.textAlignment != .center)  self.textAlignment = YTPAppManager.language == .arabic ? .right : .left 

        errorLabel.translatesAutoresizingMaskIntoConstraints = false

        self.superview?.addSubview(errorLabel)

        let topConstraint = NSLayoutConstraint(item: errorLabel!, attribute: .top, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: 0.0)
        let leftConstraint = NSLayoutConstraint(item: errorLabel!, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1.0, constant: 0.0)
        let rightConstraint = NSLayoutConstraint(item: errorLabel!, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1.0, constant: 0.0)

        topConstraint.priority = UILayoutPriority(1.0)
        leftConstraint.priority = UILayoutPriority(1.0)
        rightConstraint.priority = UILayoutPriority(1.0)

        self.superview?.addConstraints([
            topConstraint, leftConstraint, rightConstraint
        ])
    

    ...


我还有一个 CALayer,它被添加为 UITextField 的 SubLayer,我在视图出现后从 ViewController 调用它
class FormTextField: UITextField 
    func addBottomBorder(color: CGColor? = UIColor(red: 0.78, green: 0.78, blue: 0.78, alpha: 1.0).cgColor) 
        removeBottomBorder()  _ in
            let border = CALayer()
            let width = CGFloat(2.0)
            border.backgroundColor = color
            border.borderColor = color
            border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: width)
            border.borderWidth = width
            border.accessibilityValue = "bottomBorder"

            self.layer.addSublayer(border)
        
    


class AViewController: UIViewController 

    @IBOutlet weak var exampleTextField: FormTextField!

    override func viewDidAppear(_ animated: Bool) 
        exampleTextField.addBottomBorder
        self.view.setNeedsLayout()
        self.layoutIfNeeded()

        super.viewDidAppear(animated)
    

    ...

我在 3 个不同的视图上看到了 3 个不同的结果,即使它们的设置完全相同。视图架构为:View -> ScrollView -> View -> FormTextField。在所有视图中,文本字段对其父视图的前导空格约束等于 32,对其父视图的尾随空格约束等于 32。

下面是我看到的不一致之处:

1) 一个视图看起来非常好,一切看起来都完全符合它应该的样子。底部边框跨越实际文本字段的宽度,错误标签出现在左侧。

2) 另一种观点是部分正确的。底部边框跨越实际文本字段的宽度,但错误标签出现在右侧而不是左侧。

3) 最后一个视图是完全错误的。底部边框仅跨越文本字段的一部分(它似乎纠正了故事板中使用的设备的宽度,但在具有更大屏幕的 iPhone 上使用时看起来不正确),错误标签出现在右侧而不是左边也是。

我一遍又一遍地检查,在我实现它们的方式上找不到任何区别。他们都对他们的超级视图有相同的约束,addBottomBorder 在所有三个不同的视图控制器中被viewDidAppear 调用,等等。

我的猜测是,这与 UITextField 上的布局约束有关,并且可能与初始化期间的奇怪竞争条件有关,但我迷路了。我做错了什么导致结果不一致?

【问题讨论】:

【参考方案1】:

我也认为这是关于设置约束,我建议使用以下函数重新进行:

extension UIView 

    func anchors (top:NSLayoutYAxisAnchor? , leading:NSLayoutXAxisAnchor? , bottom : NSLayoutYAxisAnchor? , trailing: NSLayoutXAxisAnchor? , padding : UIEdgeInsets = .zero)

        translatesAutoresizingMaskIntoConstraints = false

        if let top = top 
            topAnchor.constraint(equalTo: top , constant: padding.top).isActive = true
        
        if let leading = leading 
            leadingAnchor.constraint(equalTo: leading , constant: padding.left).isActive = true
        
        if let bottom = bottom 
            bottomAnchor.constraint(equalTo: bottom , constant: -padding.bottom).isActive = true
        
        if let trailing = trailing 
            trailingAnchor.constraint(equalTo: trailing , constant: -padding.right).isActive = true
        
    

希望对你有帮助:)

【讨论】:

以上是关于未按预期应用约束的主要内容,如果未能解决你的问题,请参考以下文章

程序约束未按预期工作

自动布局约束未按预期工作

iOS Storyboard 约束未按预期运行

IOS 8 高度约束动画未按预期工作

CSS 规则未按预期应用

分支测试/实时链接未按预期工作