Изменение размера UILabel для размещения вставок
Я строю экран для сканирования штрих-кодов, и мне нужно поставить полупрозрачный экран позади некоторых UILabels
улучшить видимость на светлом фоне.
Вот как выглядит экран сейчас:
Я устанавливаю цвет фона на UILabel
чтобы получить полупрозрачные коробки. Я также создал обычай UILabel
подкласс, чтобы позволить мне установить некоторые отступы между краем UILabel
и текст с использованием этого подхода.
Как вы можете видеть на экране выше, UILabel
неправильно изменяет размер, чтобы учитывать отступы. "Заполнение" просто смещает текст без изменения ширины метки, что приводит к усечению текста.
Обе эти метки будут содержать текст произвольной длины, и мне действительно нужно UILabel
динамически изменять размер.
Какие UILabel
метод я могу переопределить, чтобы увеличить ширину метки и фактор заполнения?
6 ответов
Вот класс меток, который правильно рассчитывает размеры. Размещенный код находится в 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
Если вы хотите настроить текстовые вставки в раскадровках, вы можете использовать следующее расширение, чтобы включить поддержку 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, а затем просто нажать ⌘=, чтобы отрегулировать размер этикетки по размеру.
Отказ от ответственности:
Весь код находится в свободном доступе. Делай как пожелаешь.
Вот Swift-версия подкласса UILabel (такая же, как ответ @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))
}
}
Свифт 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))
}}
Вот версия 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));
}
}
В дополнение к ответу Николая Рюэ, вам нужно аннулировать внутренний размер контента для автоматического размещения, чтобы правильно пересчитать изменения размера. Вы заметите эту проблему, если вы измените edgeInsets в течение жизненного цикла приложения:
class NRLabel: UILabel {
var edgeInsets = UIEdgeInsetsZero {
didSet {
self.invalidateIntrinsicContentSize()
}
}
...
}
Свифт 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 в UITableViewCell:
- Установите ограничение ширины для UILabel
- Подключите ограничение через IBOutlet к вашему коду, либо VC (пользовательский класс ячейки, если вы делаете расширяющуюся ячейку табличного представления)
- Создайте переменную для фактического размера текста, затем добавьте вставки + размер ширины к ограничению и обновите представление:
let messageTextSize: CGSize = (messageText as NSString).sizeWithAttributes([
NSFontAttributeName: UIFont.systemFontOfSize(14.0)])
cell.widthConstraint.constant = messageTextSize.width + myInsetsOrWhatever
Я еще не провёл тщательного тестирования, возможно, вам придется поиграться с точными значениями CGFloat, которые вы добавляете. Я обнаружил, что правильный размер не совсем ширина плюс вставки; это немного больше, чем это. Это гарантирует, что ширина UILabel всегда будет по крайней мере размером текста или больше.
Вот пример того, что я использовал для простого отступа в 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))
}
}
Добавление к ответу @Pataphysicien выше для форм Xamarin, вот как вы используете UIEdgeableLabel:
[assembly: ExportRenderer(typeof(PaddedLabel), typeof(PaddedLabelRenderer))]
namespace MyProj.iOS.Renderers
{
public class PaddedLabelRenderer : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
if (Control == null)
{
var label = new UIEdgeableLabel();
label.EdgeInsets = new UIEdgeInsets(2, 2, 2, 2); //padding
SetNativeControl(label);
}
base.OnElementChanged(e);
}
}
}