Resizing a UILabel to accommodate insets

后端 未结 8 1662
梦谈多话
梦谈多话 2020-11-27 11:03

I\'m building a screen to scan barcodes, and I need to put a translucent screen behind some UILabels to improve visibility against light backgrounds.

He

相关标签:
8条回答
  • 2020-11-27 11:06

    Here is the C# version (usefull for Xamarin) based on Nikolai's code :

    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));
        }
    }
    
    0 讨论(0)
  • 2020-11-27 11:07

    Here's a quick, hacky way to do it that you can understand more quickly. It's not as robust as Nikolai's, but it gets the job done. I did this when I was trying to fit my text in my UILabel within a UITableViewCell:

    1. Set a width constraint for the UILabel
    2. Connect the constraint via IBOutlet onto your code, either VC (custom cell class if you're doing an expanding table view cell)
    3. Create a variable for the actual size of the text, then add the insets + the width size to the constraint and update the view:

    let messageTextSize: CGSize = (messageText as NSString).sizeWithAttributes([ NSFontAttributeName: UIFont.systemFontOfSize(14.0)]) cell.widthConstraint.constant = messageTextSize.width + myInsetsOrWhatever

    I haven't extensively tested it yet, you might have to play around with the exact CGFloat values that you add. I found that the right size isn't exactly width plus insets; it's a little larger than that. This makes sure that the width of the UILabel will always be at least the text size or larger.

    0 讨论(0)
  • 2020-11-27 11:08

    In additions to Nikolai Ruhe's answer, you need to invalidate intrinsic content size for autolayout to properly recalculate the size changes. You would notice this issue if you change edgeInsets over the application lifecycle:

    class NRLabel: UILabel {
    
        var edgeInsets = UIEdgeInsetsZero {
            didSet {
                self.invalidateIntrinsicContentSize()
            }
        }
    
        ...
    }
    
    0 讨论(0)
  • 2020-11-27 11:25

    Here is an example of what I used for a simple 10 unit padding on the left and right of the label with rounded corners. Just set the label text to center it's self and make it's class IndentedLabel and the rest takes care of itself. To modify the padding just scale up or down 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))
        }
    }
    
    0 讨论(0)
  • 2020-11-27 11:25

    Swift 5 . You can create a custom UILabel class.
    I've added 22 paddings to the left side of the content. When UILabel asks for intrinsicContentSize return by adding padding size you have added, I've added 22 and returned customized size. That's it.

    // 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
    }
    
    0 讨论(0)
  • 2020-11-27 11:26

    Here's a label class that calculates sizes correctly. The posted code is in Swift 3, but you can also download Swift 2 or Objective-C versions.

    How does it work?

    By calculating the proper textRect all of the sizeToFit and auto layout stuff works as expected. The trick is to first subtract the insets, then calculate the original label bounds, and finally to add the insets again.

    Code

    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))
        }
    }
    

    Optional: Interface Builder support

    If you want to setup text insets in storyboards you can use the following extension to enable Interface Builder support:

    @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.
    }
    

    Now you can conveniently setup your insets in IB and then just press ⌘= to adjust the label's size to fit.

    Disclaimer:

    All code is in the public domain. Do as you please.

    0 讨论(0)
提交回复
热议问题