调整 UILabel 的大小以适应插图
Posted
技术标签:
【中文标题】调整 UILabel 的大小以适应插图【英文标题】:Resizing a UILabel to accommodate insets 【发布时间】:2014-02-05 16:21:00 【问题描述】:我正在构建一个屏幕来扫描条形码,我需要在一些 UILabels
后面放置一个半透明屏幕以提高在浅色背景下的可见度。
现在的屏幕是这样的:
我在UILabel
上设置背景颜色以获取半透明框。我还创建了一个自定义 UILabel
子类,以允许我在 UILabel
的边缘和使用 this approach 的文本之间设置一些填充。
正如您在上面的屏幕中看到的,UILabel
没有正确调整大小以考虑填充。 “填充”只是在不改变标签宽度的情况下移动文本,导致文本被截断。
这两个标签都将包含任意长度的文本,我真的需要UILabel
来动态调整大小。
我可以重写什么UILabel
方法来增加标签的宽度并考虑填充?
【问题讨论】:
这是如何加载的?如果您使用的是故事板,您是否使用它加载了此标签?如果你这样做了,你有没有给这个标签添加任何约束并将大小设置为一个常数? 好问题。这个视图完全是用代码构建的。我使用约束来布局屏幕,标签上唯一的约束设置屏幕上的位置,而不是标签的大小。 好的,这很可能只是文本太长的问题。我想说只是让标签完全变宽,甚至根据文本在其中的长度调整标签的宽度 根据文本长度调整标签的宽度正是我所追求的,但是在 UILabel 之外计算是愚蠢的。我希望了解我可以重写什么 UILabel 方法来重新计算标签的大小以合并填充。 看看这个:***.com/questions/13237353/… 【参考方案1】:这是一个正确计算尺寸的标签类。 posted code 在 Swift 3 中,但您也可以下载 Swift 2 或 Objective-C 版本。
它是如何工作的?
通过计算正确的 textRect,所有sizeToFit
和自动布局的东西都可以按预期工作。诀窍是先减去 insets,然后计算原始标签边界,最后再次添加 insets。
代码 (Swift 5)
class NRLabel: 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))
可选:界面生成器支持
如果您想在故事板中设置文本插入,您可以使用以下扩展来启用 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 中设置插图,然后只需按 ⌘= 即可调整标签的大小以适合。
免责声明:
所有代码都在公共领域。随心所欲。
【讨论】:
清关;而不是手动设置rect
的偏移量,您可以使用 CGRectInset
代替。像这样:return CGRectInset(rect, -insets.left, insets.top);
developer.apple.com/library/mac/documentation/graphicsimaging/…
@Petter 使用 CGRectInset
仅在插图对称时有效。
@NikolaiRuhe 我们是否也需要覆盖 intrinsicContentSize() ?我在某处看到 AL 需要它。也许你的伎俩不涉及这种方法?
我建议添加 ` if text == nil return super.textRectForBounds(bounds, limitedToNumberOfLines: numberOfLines) ` 在没有文本时“隐藏”标签。 (
我们必须在主类中调用一些函数吗?将此代码复制到 UILabel 类似乎不起作用【参考方案2】:
这是 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))
【讨论】:
您能否在回答中澄清“解决问题”的含义?无论如何,使用你的代码并投票回答。 谢谢!我更喜欢您的代码,因为它不需要扩展 - 它是一个简单的子类。嗯 - 你可能也想让子类“最终”! 这不会覆盖固有大小 - 因此如果您使用自动调整大小的流布局,它将截断文本 覆盖 textRectForBounds 修复了截断问题,谢谢!【参考方案3】:这是基于 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));
【讨论】:
【参考方案4】:Swift 5 版本 Nikolai Ruhe 回答:
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))
【讨论】:
【参考方案5】:除了 Nikolai Ruhe 的回答之外,您需要使自动布局的内在内容大小无效,以正确重新计算大小更改。如果您在应用程序生命周期内更改 edgeInsets,您会注意到此问题:
class NRLabel: UILabel
var edgeInsets = UIEdgeInsetsZero
didSet
self.invalidateIntrinsicContentSize()
...
【讨论】:
【参考方案6】:这是一个示例,我使用圆角标签左右两侧的简单 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))
【讨论】:
【参考方案7】:这是一种快速、简单的方法,您可以更快地理解。它不像 Nikolai 那样强大,但它可以完成工作。当我试图将我的文本放入 UITableViewCell 中的 UILabel 中时,我这样做了:
-
为 UILabel 设置宽度限制
通过 IBOutlet 将约束连接到您的代码,无论是 VC(如果您正在执行扩展表格视图单元格,则自定义单元格类)
为文本的实际大小创建一个变量,然后将 insets + 宽度大小添加到约束并更新视图:
let messageTextSize: CGSize = (messageText as NSString).sizeWithAttributes([
NSFontAttributeName: UIFont.systemFontOfSize(14.0)])
cell.widthConstraint.constant = messageTextSize.width + myInsetsOrWhatever
我还没有对它进行广泛的测试,您可能需要使用您添加的确切 CGFloat 值。我发现正确的尺寸并不完全是宽度加上插图;它比那个大一点。这样可以确保 UILabel 的宽度始终至少为文本大小或更大。
【讨论】:
【参考方案8】:斯威夫特 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
【讨论】:
以上是关于调整 UILabel 的大小以适应插图的主要内容,如果未能解决你的问题,请参考以下文章
为啥在 Interface Builder 中更改字体大小会阻止 UILabel 调整大小以适应内容?