向自定义 UIButton 添加约束不起作用
Posted
技术标签:
【中文标题】向自定义 UIButton 添加约束不起作用【英文标题】:Adding constraints to a custom UIButton doesn't work 【发布时间】:2017-02-13 21:39:16 【问题描述】:您好,我有以下代码:
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)
所以基本上我只是添加了两个具有宽度和高度约束的按钮,我将cancelButton
设置在左下角,将validateButton
设置在右下角。
我的按钮没有出现。当我以这种方式设置按钮的框架时,它可以工作:
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)
有人知道我的约束有什么问题吗? 谢谢!
【问题讨论】:
我使用不同的语法,但据我所见,有两件事。首先,20x20 按钮相当小。也许他们 正在 被渲染 - 给他们一个 backgroundColor 来检查。其次,两个按钮都呈现在相同的位置。但据我所知,你已经给了它们足够的自动布局来知道如何处理它们。 (除非 .notAnAttribute 不正确。我使用“锚”语法样式并且从未遇到过这个。)编辑:Doh。我没有看到一个按钮领先而另一个按钮尾随。忘记我的第二条评论! 我相信你希望你的底部和尾随约束是负面的...... 【参考方案1】: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
您应该能够将其复制/粘贴到 Playground 页面中以查看结果。请注意,我没有您的按钮图像,所以我使用“V”和“C”标题。
关键区别在于将底部和尾随值设置为负值 - 也就是说,您希望按钮底部为(包含视图的底部 MINUS 20)。同样,Button 的后沿距离包含视图的后沿为 -16。
【讨论】:
谢谢,完美,我知道我做错了什么!没有考虑负值,我想要获得正值我只需要切换item:
和toItem:
我把它扔到了操场上,它把 80x80 的图标变成了微小的 10x10 图标?【参考方案2】:
看来你把这个复杂化了。
您可以使用VisualFormat
字符串轻松实现此目的。
看看这里https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html#//apple_ref/doc/uid/TP40010853-CH27-SW1
这是使用您的代码的示例:
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()
【讨论】:
谢谢它的工作,我接受@DonMag 的回答,因为它让我明白我的错误是什么!你的允许真正进入视觉格式,直到那时我一直在避免以上是关于向自定义 UIButton 添加约束不起作用的主要内容,如果未能解决你的问题,请参考以下文章