简体   繁体   中英

Constraints are not being applied as expected

I have a custom UITextField that I use in multiple places, but am having a strange issue with it's constraints on only a few of the pages I use it. The custom class has a few things I add upon initialization:

  • A UILabel that I add as a subview using top, left, and right constraints to the UITextField. This gets called when the UITextField's awakeFromNib function executes
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
        ])
    }

    ...
}

  • I also have a CALayer that gets added as the UITextField's SubLayer that I call from the ViewController after the view has appeared
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)
    }

    ...
}

I'm seeing 3 different results on 3 different views even though all of them are set up exactly the same. The view architecture goes: View -> ScrollView -> View -> FormTextField . In all of the views, the textfield has a leading space constraint to its super view equalling 32, and a trailing space constraint to its super view equalling 32.

Now here are the inconsistencies I'm seeing:

1) One view looks perfectly fine and everything looks exactly as it should. The bottom border spans the width of the actual textfield, and the error label appears on the left side.

在此处输入图片说明

2) Another view is partially correct. The bottom border spans the width of the actual textfield, but the error label appears on the right side instead of the left side.

在此处输入图片说明

3) The last view with it is completely wrong. The bottom border only spans a part of the textfield (it appears to correct for width of the device used in the storyboard, but looks incorrect when live on an iPhone with a larger screen), and the error label appears on the right side instead of the left as well.

在此处输入图片说明

在此处输入图片说明

I've checked over and over again and can't find a single difference in the ways I've implemented them. They all have the same constraints to their superview, addBottomBorder is called in viewDidAppear in all three different view controllers, etc.

My guess is that this has something to do with the layout constraints on the UITextField and possibly a weird race condition during the initialization, but I'm lost. What am I doing incorrectly that is resulting in inconsistent results?

I also think it's about setting your constrains, I recommend re doing it using the below function:

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
        }
    }
}

Hope it helps :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM