简体   繁体   English

向自定义UIButton添加约束不起作用

[英]Adding constraints to a custom UIButton doesn't work

Hi I have the following code: 嗨,我有以下代码:

class MyController {    
    override func viewDidLoad() {
        self.addButtons()

        super.viewDidLoad()
    }

    func addButtons() {
        let cancelButton = UIButton(type: .custom)
        let validateButton = UIButton(type: .custom)

        cancelButton.setImage(UIImage(named: "cancel_icon"), for: .normal)
        validateButton.setImage(UIImage(named: "validate_icon"), for: .normal)

        cancelButton.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)
        validateButton.addTarget(self, action: #selector(validateAction), for: .touchUpInside)

        cancelButton.translatesAutoresizingMaskIntoConstraints = false
        validateButton.translatesAutoresizingMaskIntoConstraints = false

        self.view.addSubview(cancelButton)
        self.view.addSubview(validateButton)

        let leftCancel = NSLayoutConstraint(item: cancelButton, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leading, multiplier: 1, constant: 16)
        let bottomCancel = NSLayoutConstraint(item: cancelButton, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: 20)
        let widthCancel = NSLayoutConstraint(item: cancelButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
        let heightCancel = NSLayoutConstraint(item: cancelButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
        let rightValidate = NSLayoutConstraint(item: validateButton, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailing, multiplier: 1, constant: 16)
        let bottomValidate = NSLayoutConstraint(item: validateButton, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: 20)
        let widthValidate = NSLayoutConstraint(item: validateButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
        let heightValidate = NSLayoutConstraint(item: validateButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
        NSLayoutConstraint.activate([leftCancel, bottomCancel, rightValidate, bottomValidate, widthCancel, heightCancel, widthValidate, heightValidate])

        self.view.layoutIfNeeded()
    }

    func cancelAction() {
        self.dismiss(animated: true, completion: nil)
    }

    func validateAction() {
        self.dismiss(animated: true, completion: nil)
    }
}

So basically I'm just adding two buttons with a width and height constraint, and I set the cancelButton to the bottom left and the validateButton to the bottom right. 所以基本上我只是添加了两个带宽度和高度约束的按钮,我将cancelButton设置为左下角,将validateButton设置为右下角。

My buttons don't appear. 我的按钮没有出现。 When I set the frame of the buttons this way, it works though: 当我以这种方式设置按钮的框架时,它可以工作:

cancelButton.frame = CGRect(x: 16, y: self.view.frame.height - 40, width: 20, height: 20) 
validateButton.frame = CGRect(x: self.view.frame.width - 36, y: self.view.frame.height - 40, width: 20, height: 20)

Does someone know what's wrong with my constraints? 有人知道我的约束有什么问题吗? Thanks! 谢谢!

VisualFormat has its uses, but it also has its own quirks - such as flexible spacing between elements. VisualFormat有其用途,但它也有自己的怪癖 - 例如元素之间的灵活间距。 Here's a solution without VisualFormat: 这是没有VisualFormat的解决方案:

//: Playground - noun: a place where people can play

import UIKit

import PlaygroundSupport

class MyController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        self.addButtons()
    }

    func addButtons() {
        let cancelButton = UIButton(type: .custom)
        let validateButton = UIButton(type: .custom)

        cancelButton.setImage(UIImage(named: "cancel_icon"), for: .normal)
        validateButton.setImage(UIImage(named: "validate_icon"), for: .normal)

        cancelButton.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)
        validateButton.addTarget(self, action: #selector(validateAction), for: .touchUpInside)

        cancelButton.translatesAutoresizingMaskIntoConstraints = false
        validateButton.translatesAutoresizingMaskIntoConstraints = false

        self.view.addSubview(cancelButton)
        self.view.addSubview(validateButton)

        validateButton.backgroundColor = UIColor.red
        cancelButton.backgroundColor = UIColor.blue

        validateButton.setTitle("V", for: UIControlState())
        cancelButton.setTitle("C", for: UIControlState())

        let leftCancel = NSLayoutConstraint(item: cancelButton, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leading, multiplier: 1, constant: 16)
        let bottomCancel = NSLayoutConstraint(item: cancelButton, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: -20)
        let widthCancel = NSLayoutConstraint(item: cancelButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
        let heightCancel = NSLayoutConstraint(item: cancelButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
        let rightValidate = NSLayoutConstraint(item: validateButton, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailing, multiplier: 1, constant: -16)
        let bottomValidate = NSLayoutConstraint(item: validateButton, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: -20)
        let widthValidate = NSLayoutConstraint(item: validateButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
        let heightValidate = NSLayoutConstraint(item: validateButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)

        NSLayoutConstraint.activate([leftCancel, bottomCancel, rightValidate, bottomValidate, widthCancel, heightCancel, widthValidate, heightValidate])

        self.view.layoutIfNeeded()
    }

    func cancelAction() {
        //      self.dismiss(animated: true, completion: nil)
        print("cancel")
    }

    func validateAction() {
        //      self.dismiss(animated: true, completion: nil)
        print("validate")
    }
}

var vc = MyController()
vc.view.backgroundColor = UIColor.green
vc.view.frame = CGRect(x: 0, y: 0, width: 400, height: 400)

PlaygroundPage.current.liveView = vc.view

PlaygroundPage.current.needsIndefiniteExecution = true

You should be able to copy/paste this into a Playground page to see the results. 您应该能够将其复制/粘贴到Playground页面中以查看结果。 Note that I don't have your button images, so I "V" and "C" titles. 请注意,我没有你的按钮图像,所以我有“V”和“C”标题。

The key difference is setting the Bottom and Trailing values to negative - that is, you want the Bottom of the Button to be (Bottom of containing view MINUS 20). 关键区别在于将Bottom和Trailing值设置为负值 - 也就是说,您希望Button的底部为(包含视图MINUS 20的底部)。 Likewise with the Trailing edge of the Button to be -16 from the trailing edge of the containing view. 同样,Button的后缘距离包含视图的后缘为-16。

It seems you are over complicating this. 看来你已经过度复杂了。 You can achieve this easily with VisualFormat strings. 您可以使用VisualFormat字符串轻松实现此目的。

Have a look here https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html#//apple_ref/doc/uid/TP40010853-CH27-SW1 看看这里https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html#//apple_ref/doc/uid/TP40010853-CH27-SW1

Here's an example using your code: 以下是使用您的代码的示例:

func addButtons() {
    let cancelButton = UIButton(type: .custom)
    let validateButton = UIButton(type: .custom)

    cancelButton.setImage(UIImage(named: "cancel_icon"), for: .normal)
    validateButton.setImage(UIImage(named: "validate_icon"), for: .normal)

    cancelButton.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)
    validateButton.addTarget(self, action: #selector(validateAction), for: .touchUpInside)

    cancelButton.translatesAutoresizingMaskIntoConstraints = false
    validateButton.translatesAutoresizingMaskIntoConstraints = false

    self.view.addSubview(cancelButton)
    self.view.addSubview(validateButton)

    // With visual format strings
    let views = ["cancelButton" : cancelButton, "validateButton" : validateButton]

    let horizontalFormat = "|[cancelButton]-[validateButton(==cancelButton)]|"
    let verticalFormat = "V:[cancelButton]-10-|"

    let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: horizontalFormat, options:.alignAllBottom , metrics: nil, views: views)
    let verticalConstraints = NSLayoutConstraint.constraints(withVisualFormat: verticalFormat, options:.alignAllBottom, metrics: nil, views: views)

    NSLayoutConstraint.activate(horizontalConstraints)
    NSLayoutConstraint.activate(verticalConstraints)

    self.view.layoutIfNeeded()
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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