徽章视图如何使用全自动布局方法创建
Badge view how to create with a full auto layout approach
我尝试创建右上角有一个简单徽章的自定义视图已经有一段时间了。
自定义视图由3部分组成。
- 在 red 中有 自定义视图 本身,它包含:
- 在 blue 容器视图中
- 在 green badge view 一个简单的
UILabel
容器视图和徽章视图是兄弟视图。
目前容器视图包含一个 UIImageView
该视图必须满足这些要求:
- 全自动布局方法
- 以编程方式制作
- 自定义视图必须只考虑蓝框对齐(容器视图)
为什么?想象一下,您需要通过将顶部边缘与另一个视图或按钮的顶部边缘对齐来定位该视图,如果只考虑容器中显示的内容会不会很好?
在这里你可以看到我是如何设置约束的。标签位于容器视图的右上角。
func setUpConstraint() {
var horContraints = [NSLayoutConstraint]()
horContraints.append(NSLayoutConstraint(item: containerView, attribute: .Leading, relatedBy: .Equal, toItem: self, attribute: .Leading, multiplier: 1, constant: 0))
horContraints.append(NSLayoutConstraint(item: containerView, attribute: .Trailing, relatedBy: .Equal, toItem: badge, attribute: .CenterX, multiplier: 1, constant: 0))
horContraints.append(NSLayoutConstraint(item: badge, attribute: .Trailing, relatedBy: .Equal, toItem: self, attribute: .Trailing, multiplier: 1, constant: 0))
var verContraints = [NSLayoutConstraint]()
verContraints.append(NSLayoutConstraint(item: containerView, attribute: .Bottom, relatedBy: .Equal, toItem: self, attribute: .Bottom, multiplier: 1, constant: 0))
verContraints.append(NSLayoutConstraint(item: containerView, attribute: .Top, relatedBy: .Equal, toItem: badge, attribute: .CenterY, multiplier: 1, constant: 0))
verContraints.append(NSLayoutConstraint(item: badge, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: 0))
addConstraints(verContraints + horContraints)
containerView.addConstraint(NSLayoutConstraint(item: containerView, attribute: .Height, relatedBy: .Equal, toItem: containerView, attribute: .Width, multiplier: 1, constant: 0))
}
容器视图也有纵横比约束以保持正方形大小。
正如您从图片中看到的那样,一切似乎都很好,除了当我尝试将自定义视图约束到其父视图的中心时,它 似乎没有对齐,因为我希望视图居中关于容器视图(带有图像的视图)。徽章是一种装饰,如影子,我不想考虑。
为了正确对齐它,我试图通过添加一个 "cut" 标签大小一半的插图来覆盖对齐矩形。
override func alignmentRectInsets() -> UIEdgeInsets {
let badgeSize = badge.bounds.size
return UIEdgeInsets(top: badgeSize.height / 2, left: 0, bottom: 0, right: badgeSize.width / 2)
}
我也尝试过不同的配置,但我始终无法适应想要的位置
如果我尝试使用其他 2 种方法 alignmentRectForFrame
和 frameForAlignmentRect
(删除 alignmentRectInsets
),它们将永远不会被调用。
这是我想获得的:
我创建了一个 little sample code
如果问题是您希望 other 视图("content view",显示图像)在最终超级视图中居中,那么简单地进行居中约束(center x to center x, center y to center y) 在超级视图和内容视图之间。没有法律规定约束必须存在于视图与其直接父视图之间;您可以在 任何 对视图之间进行约束(这是关于约束的美妙之处之一)。
我做了一个 mock-up 看起来很像你的 "here's what I'd like to obtain" 图片:
如您所见,Moe(中间的 Pep Boy,在图像中间)正好位于超级视图的中心(由绿线显示)。
为简单起见,整个界面是用代码创建的,这样我就可以向您展示整个过程。在这里:
// create the custom view
let customView = UIView()
customView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(customView)
customView.backgroundColor = UIColor.redColor()
NSLayoutConstraint.activateConstraints([
customView.widthAnchor.constraintEqualToConstant(100),
customView.heightAnchor.constraintEqualToConstant(100),
])
// add the content view (image) to the custom view
let contentView = UIImageView(image: UIImage(named:"pep.jpg")!)
contentView.translatesAutoresizingMaskIntoConstraints = false
customView.addSubview(contentView)
NSLayoutConstraint.activateConstraints([
contentView.leadingAnchor.constraintEqualToAnchor(customView.leadingAnchor),
contentView.bottomAnchor.constraintEqualToAnchor(customView.bottomAnchor),
contentView.heightAnchor.constraintEqualToAnchor(customView.heightAnchor, constant: -10),
contentView.widthAnchor.constraintEqualToAnchor(customView.widthAnchor, constant: -10)
])
// add the badge (label) to the custom view
let badge = UILabel()
badge.translatesAutoresizingMaskIntoConstraints = false
customView.addSubview(badge)
badge.backgroundColor = UIColor.greenColor().colorWithAlphaComponent(0.5)
badge.font = UIFont(name: "GillSans", size: 14)
badge.textAlignment = .Center
badge.text = "567"
NSLayoutConstraint.activateConstraints([
badge.centerXAnchor.constraintEqualToAnchor(contentView.trailingAnchor),
badge.centerYAnchor.constraintEqualToAnchor(contentView.topAnchor),
])
// position the whole thing with respect to the content view
NSLayoutConstraint.activateConstraints([
contentView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor),
contentView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor),
])
但这不是您问题的完整答案。您现在应该问:是的,但是当我尝试使用 alignmentRectInsets
时出了什么问题?答案是:您忘记了 alignmentRectInsets
会影响与 内部 视图以及 外部 视图的对齐。换句话说,您当然可以改用那种方法,但是您必须相应地调整内容视图的位置。
所以,这是我使用 alignmentRectInsets
的重写。首先,我将为我们的自定义视图定义一个自定义视图子类:
class AlignedView : UIView {
override func alignmentRectInsets() -> UIEdgeInsets {
return UIEdgeInsetsMake(10, 0, 0, 10)
}
}
现在重写。我在前面示例中 更改 的所有行旁边放了一个星号 (*
):
// create the custom view
let customView = AlignedView() // *
customView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(customView)
customView.backgroundColor = UIColor.redColor()
NSLayoutConstraint.activateConstraints([
customView.widthAnchor.constraintEqualToConstant(100),
customView.heightAnchor.constraintEqualToConstant(100),
])
// add the content view (image) to the custom view
let contentView = UIImageView(image: UIImage(named:"pep.jpg")!)
contentView.translatesAutoresizingMaskIntoConstraints = false
customView.addSubview(contentView)
NSLayoutConstraint.activateConstraints([
contentView.leadingAnchor.constraintEqualToAnchor(customView.leadingAnchor),
contentView.bottomAnchor.constraintEqualToAnchor(customView.bottomAnchor),
contentView.heightAnchor.constraintEqualToAnchor(customView.heightAnchor), // *
contentView.widthAnchor.constraintEqualToAnchor(customView.widthAnchor) // *
])
// add the badge (label) to the custom view
let badge = UILabel()
badge.translatesAutoresizingMaskIntoConstraints = false
customView.addSubview(badge)
badge.backgroundColor = UIColor.greenColor().colorWithAlphaComponent(0.5)
badge.font = UIFont(name: "GillSans", size: 14)
badge.textAlignment = .Center
badge.text = "567"
NSLayoutConstraint.activateConstraints([
badge.centerXAnchor.constraintEqualToAnchor(contentView.trailingAnchor),
badge.centerYAnchor.constraintEqualToAnchor(contentView.topAnchor),
])
// position the whole thing with respect to the custom view
NSLayoutConstraint.activateConstraints([
customView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor), // *
customView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor), // *
])
这给出了与以前完全相同的视觉效果。
我尝试创建右上角有一个简单徽章的自定义视图已经有一段时间了。
自定义视图由3部分组成。
- 在 red 中有 自定义视图 本身,它包含:
- 在 blue 容器视图中
- 在 green badge view 一个简单的
UILabel
容器视图和徽章视图是兄弟视图。
目前容器视图包含一个 UIImageView
该视图必须满足这些要求:
- 全自动布局方法
- 以编程方式制作
- 自定义视图必须只考虑蓝框对齐(容器视图)
为什么?想象一下,您需要通过将顶部边缘与另一个视图或按钮的顶部边缘对齐来定位该视图,如果只考虑容器中显示的内容会不会很好?
在这里你可以看到我是如何设置约束的。标签位于容器视图的右上角。
func setUpConstraint() {
var horContraints = [NSLayoutConstraint]()
horContraints.append(NSLayoutConstraint(item: containerView, attribute: .Leading, relatedBy: .Equal, toItem: self, attribute: .Leading, multiplier: 1, constant: 0))
horContraints.append(NSLayoutConstraint(item: containerView, attribute: .Trailing, relatedBy: .Equal, toItem: badge, attribute: .CenterX, multiplier: 1, constant: 0))
horContraints.append(NSLayoutConstraint(item: badge, attribute: .Trailing, relatedBy: .Equal, toItem: self, attribute: .Trailing, multiplier: 1, constant: 0))
var verContraints = [NSLayoutConstraint]()
verContraints.append(NSLayoutConstraint(item: containerView, attribute: .Bottom, relatedBy: .Equal, toItem: self, attribute: .Bottom, multiplier: 1, constant: 0))
verContraints.append(NSLayoutConstraint(item: containerView, attribute: .Top, relatedBy: .Equal, toItem: badge, attribute: .CenterY, multiplier: 1, constant: 0))
verContraints.append(NSLayoutConstraint(item: badge, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: 0))
addConstraints(verContraints + horContraints)
containerView.addConstraint(NSLayoutConstraint(item: containerView, attribute: .Height, relatedBy: .Equal, toItem: containerView, attribute: .Width, multiplier: 1, constant: 0))
}
容器视图也有纵横比约束以保持正方形大小。
正如您从图片中看到的那样,一切似乎都很好,除了当我尝试将自定义视图约束到其父视图的中心时,它 似乎没有对齐,因为我希望视图居中关于容器视图(带有图像的视图)。徽章是一种装饰,如影子,我不想考虑。
为了正确对齐它,我试图通过添加一个 "cut" 标签大小一半的插图来覆盖对齐矩形。
override func alignmentRectInsets() -> UIEdgeInsets {
let badgeSize = badge.bounds.size
return UIEdgeInsets(top: badgeSize.height / 2, left: 0, bottom: 0, right: badgeSize.width / 2)
}
我也尝试过不同的配置,但我始终无法适应想要的位置
如果我尝试使用其他 2 种方法 alignmentRectForFrame
和 frameForAlignmentRect
(删除 alignmentRectInsets
),它们将永远不会被调用。
这是我想获得的:
我创建了一个 little sample code
如果问题是您希望 other 视图("content view",显示图像)在最终超级视图中居中,那么简单地进行居中约束(center x to center x, center y to center y) 在超级视图和内容视图之间。没有法律规定约束必须存在于视图与其直接父视图之间;您可以在 任何 对视图之间进行约束(这是关于约束的美妙之处之一)。
我做了一个 mock-up 看起来很像你的 "here's what I'd like to obtain" 图片:
如您所见,Moe(中间的 Pep Boy,在图像中间)正好位于超级视图的中心(由绿线显示)。
为简单起见,整个界面是用代码创建的,这样我就可以向您展示整个过程。在这里:
// create the custom view
let customView = UIView()
customView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(customView)
customView.backgroundColor = UIColor.redColor()
NSLayoutConstraint.activateConstraints([
customView.widthAnchor.constraintEqualToConstant(100),
customView.heightAnchor.constraintEqualToConstant(100),
])
// add the content view (image) to the custom view
let contentView = UIImageView(image: UIImage(named:"pep.jpg")!)
contentView.translatesAutoresizingMaskIntoConstraints = false
customView.addSubview(contentView)
NSLayoutConstraint.activateConstraints([
contentView.leadingAnchor.constraintEqualToAnchor(customView.leadingAnchor),
contentView.bottomAnchor.constraintEqualToAnchor(customView.bottomAnchor),
contentView.heightAnchor.constraintEqualToAnchor(customView.heightAnchor, constant: -10),
contentView.widthAnchor.constraintEqualToAnchor(customView.widthAnchor, constant: -10)
])
// add the badge (label) to the custom view
let badge = UILabel()
badge.translatesAutoresizingMaskIntoConstraints = false
customView.addSubview(badge)
badge.backgroundColor = UIColor.greenColor().colorWithAlphaComponent(0.5)
badge.font = UIFont(name: "GillSans", size: 14)
badge.textAlignment = .Center
badge.text = "567"
NSLayoutConstraint.activateConstraints([
badge.centerXAnchor.constraintEqualToAnchor(contentView.trailingAnchor),
badge.centerYAnchor.constraintEqualToAnchor(contentView.topAnchor),
])
// position the whole thing with respect to the content view
NSLayoutConstraint.activateConstraints([
contentView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor),
contentView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor),
])
但这不是您问题的完整答案。您现在应该问:是的,但是当我尝试使用 alignmentRectInsets
时出了什么问题?答案是:您忘记了 alignmentRectInsets
会影响与 内部 视图以及 外部 视图的对齐。换句话说,您当然可以改用那种方法,但是您必须相应地调整内容视图的位置。
所以,这是我使用 alignmentRectInsets
的重写。首先,我将为我们的自定义视图定义一个自定义视图子类:
class AlignedView : UIView {
override func alignmentRectInsets() -> UIEdgeInsets {
return UIEdgeInsetsMake(10, 0, 0, 10)
}
}
现在重写。我在前面示例中 更改 的所有行旁边放了一个星号 (*
):
// create the custom view
let customView = AlignedView() // *
customView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(customView)
customView.backgroundColor = UIColor.redColor()
NSLayoutConstraint.activateConstraints([
customView.widthAnchor.constraintEqualToConstant(100),
customView.heightAnchor.constraintEqualToConstant(100),
])
// add the content view (image) to the custom view
let contentView = UIImageView(image: UIImage(named:"pep.jpg")!)
contentView.translatesAutoresizingMaskIntoConstraints = false
customView.addSubview(contentView)
NSLayoutConstraint.activateConstraints([
contentView.leadingAnchor.constraintEqualToAnchor(customView.leadingAnchor),
contentView.bottomAnchor.constraintEqualToAnchor(customView.bottomAnchor),
contentView.heightAnchor.constraintEqualToAnchor(customView.heightAnchor), // *
contentView.widthAnchor.constraintEqualToAnchor(customView.widthAnchor) // *
])
// add the badge (label) to the custom view
let badge = UILabel()
badge.translatesAutoresizingMaskIntoConstraints = false
customView.addSubview(badge)
badge.backgroundColor = UIColor.greenColor().colorWithAlphaComponent(0.5)
badge.font = UIFont(name: "GillSans", size: 14)
badge.textAlignment = .Center
badge.text = "567"
NSLayoutConstraint.activateConstraints([
badge.centerXAnchor.constraintEqualToAnchor(contentView.trailingAnchor),
badge.centerYAnchor.constraintEqualToAnchor(contentView.topAnchor),
])
// position the whole thing with respect to the custom view
NSLayoutConstraint.activateConstraints([
customView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor), // *
customView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor), // *
])
这给出了与以前完全相同的视觉效果。