[英]Resizing a UILabel to accommodate insets
我正在构建一个屏幕来扫描条形码,我需要在一些UILabels
后面放置一个半透明屏幕以提高对浅色背景的可见性。
下面是屏幕现在的样子:
我在UILabel
上设置背景颜色以获取半透明框。 我还创建了一个自定义UILabel
子类,以允许我使用这种方法在UILabel
的边缘和文本之间设置一些填充。
正如您在上面的屏幕中看到的, UILabel
没有正确调整大小以考虑填充。 “填充”只是将文本移过而不改变标签的宽度,导致文本被截断。
这两个标签都将包含任意长度的文本,我真的需要UILabel
来动态调整大小。
我可以重写什么UILabel
方法来增加标签的宽度和填充因子?
这是一个可以正确计算尺寸的标签类。 发布的代码使用 Swift 3,但您也可以下载Swift 2或Objective-C版本。
通过计算正确的 textRect,所有的sizeToFit
和自动布局都按预期工作。 诀窍是先减去插图,然后计算原始标签边界,最后再次添加插图。
class NRLabel : UILabel {
var textInsets = UIEdgeInsets.zero {
didSet { invalidateIntrinsicContentSize() }
}
override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
let insetRect = UIEdgeInsetsInsetRect(bounds, textInsets)
let textRect = super.textRect(forBounds: insetRect, limitedToNumberOfLines: numberOfLines)
let invertedInsets = UIEdgeInsets(top: -textInsets.top,
left: -textInsets.left,
bottom: -textInsets.bottom,
right: -textInsets.right)
return UIEdgeInsetsInsetRect(textRect, invertedInsets)
}
override func drawText(in rect: CGRect) {
super.drawText(in: UIEdgeInsetsInsetRect(rect, textInsets))
}
}
如果您想在故事板中设置文本插入,您可以使用以下扩展来启用 Interface Builder 支持:
@IBDesignable
extension NRLabel {
// currently UIEdgeInsets is no supported IBDesignable type,
// so we have to fan it out here:
@IBInspectable
var leftTextInset: CGFloat {
set { textInsets.left = newValue }
get { return textInsets.left }
}
// Same for the right, top and bottom edges.
}
现在您可以方便地在 IB 中设置您的插图,然后只需按 ⌘= 调整标签的大小以适应。
所有代码都在公共领域。 随心所欲。
这是 UILabel 子类的 Swift 版本(与@Nikolai 的答案相同),它在 UILabel 的文本周围创建了一个额外的填充:
class EdgeInsetLabel : UILabel {
var edgeInsets:UIEdgeInsets = UIEdgeInsetsZero
override func textRectForBounds(bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
var rect = super.textRectForBounds(UIEdgeInsetsInsetRect(bounds, edgeInsets), limitedToNumberOfLines: numberOfLines)
rect.origin.x -= edgeInsets.left
rect.origin.y -= edgeInsets.top
rect.size.width += (edgeInsets.left + edgeInsets.right);
rect.size.height += (edgeInsets.top + edgeInsets.bottom);
return rect
}
override func drawTextInRect(rect: CGRect) {
super.drawTextInRect(UIEdgeInsetsInsetRect(rect, edgeInsets))
}
}
这是基于 Nikolai 代码的 C# 版本(对 Xamarin 有用):
public class UIEdgeableLabel : UILabel
{
public UIEdgeableLabel() : base() { }
public UIEdgeableLabel(NSCoder coder) : base(coder) { }
public UIEdgeableLabel(CGRect frame) : base(frame) { }
protected UIEdgeableLabel(NSObjectFlag t) : base(t) { }
private UIEdgeInsets _edgeInset = UIEdgeInsets.Zero;
public UIEdgeInsets EdgeInsets
{
get { return _edgeInset; }
set
{
_edgeInset = value;
this.InvalidateIntrinsicContentSize();
}
}
public override CGRect TextRectForBounds(CGRect bounds, nint numberOfLines)
{
var rect = base.TextRectForBounds(EdgeInsets.InsetRect(bounds), numberOfLines);
return new CGRect(x: rect.X - EdgeInsets.Left,
y: rect.Y - EdgeInsets.Top,
width: rect.Width + EdgeInsets.Left + EdgeInsets.Right,
height: rect.Height + EdgeInsets.Top + EdgeInsets.Bottom);
}
public override void DrawText(CGRect rect)
{
base.DrawText(this.EdgeInsets.InsetRect(rect));
}
}
Swift 5 版尼古拉儒赫回答:
extension UIEdgeInsets {
func apply(_ rect: CGRect) -> CGRect {
return rect.inset(by: self)
}
}
class EdgeInsetLabel: UILabel {
var textInsets = UIEdgeInsets.zero {
didSet { invalidateIntrinsicContentSize() }
}
override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
let insetRect = bounds.inset(by: textInsets)
let textRect = super.textRect(forBounds: insetRect, limitedToNumberOfLines: numberOfLines)
let invertedInsets = UIEdgeInsets(top: -textInsets.top,
left: -textInsets.left,
bottom: -textInsets.bottom,
right: -textInsets.right)
return textRect.inset(by: invertedInsets)
}
override func drawText(in rect: CGRect) {
super.drawText(in: rect.inset(by: textInsets))
}}
除了Nikolai Ruhe的回答之外,您还需要使自动布局的内在内容大小无效,以正确重新计算大小更改。 如果您在应用程序生命周期内更改 edgeInsets,您会注意到这个问题:
class NRLabel: UILabel {
var edgeInsets = UIEdgeInsetsZero {
didSet {
self.invalidateIntrinsicContentSize()
}
}
...
}
这是我在带有圆角的标签左侧和右侧使用简单的 10 个单位填充的示例。 只需将标签文本设置为其自身居中并使其成为 IndentedLabel 类,其余部分自行处理。 要修改填充只需按比例放大或缩小 rect.size.width += (x)
class IndentedLabel: UILabel {
var edgeInsets:UIEdgeInsets = UIEdgeInsetsZero
override func textRectForBounds(bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
var rect = super.textRectForBounds(UIEdgeInsetsInsetRect(bounds, edgeInsets), limitedToNumberOfLines: numberOfLines)
rect.size.width += 20;
return rect
}
override func drawTextInRect(rect: CGRect) {
self.clipsToBounds = true
self.layer.cornerRadius = 3
super.drawTextInRect(UIEdgeInsetsInsetRect(rect, edgeInsets))
}
}
这是一种快速、hacky 的方法,您可以更快地理解它。 它不如 Nikolai 强大,但可以完成工作。 当我尝试将文本放入 UITableViewCell 中的 UILabel 中时,我这样做了:
let messageTextSize: CGSize = (messageText as NSString).sizeWithAttributes([ NSFontAttributeName: UIFont.systemFontOfSize(14.0)]) cell.widthConstraint.constant = messageTextSize.width + myInsetsOrWhatever
我还没有对它进行广泛的测试,您可能需要使用您添加的确切 CGFloat 值。 我发现正确的尺寸不完全是宽度加上插图; 它比那大一点。 这可确保 UILabel 的宽度始终至少为文本大小或更大。
斯威夫特 5 . 您可以创建自定义UILabel
类。
我在内容的左侧添加了 22 个填充。 当UILabel
通过添加您添加的填充大小要求返回intrinsicContentSize
,我添加了 22 并返回了自定义大小。 就是这样。
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
let insets = UIEdgeInsets(top: 0, left: 22, bottom: 0, right: 0)
super.drawText(in: rect.inset(by: insets))
self.layoutSubviews()
}
// This will return custom size with flexible content size. Mainly it can be used in Chat.
override var intrinsicContentSize: CGSize {
var size = super.intrinsicContentSize
size.width = 22 + size.width
return size
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.