Autoshrink on a UILabel with multiple lines

前端 未结 17 1960
醉梦人生
醉梦人生 2020-11-28 21:31

Is it possible to use the autoshrink property in conjunction on multiple lines on a UILabel? for example, the large text size possible on 2 available lines.

相关标签:
17条回答
  • 2020-11-28 22:12

    itedcedor's answer has an issue that pwightman pointed out. Also, there is no need to trim whitespaces. Here it is the modified version:

    - (void)adjustFontSizeToFit {
        UIFont *font = self.font;
        CGSize size = self.frame.size;
    
        for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumScaleFactor * self.font.pointSize; maxSize -= 1.f) {
            font = [font fontWithSize:maxSize];
            CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT);
            CGSize labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping];
    
            if(labelSize.height <= size.height) {
                self.font = font;
                [self setNeedsLayout];
                break;
            }
        }
    
        // set the font to the minimum size anyway
        self.font = font;
        [self setNeedsLayout];
    }
    
    0 讨论(0)
  • 2020-11-28 22:13

    I used @wbarksdale's Swift 3 solution but found that long words were being truncated in the middle. To keep words intact, I had to modify as shown:

    extension UILabel {
        func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) {
            let maxFontSize = maximumFontSize ?? font.pointSize
            let words = self.text?.components(separatedBy: " ")
            var longestWord: String?
            if let max = words?.max(by: {$1.characters.count > $0.characters.count}) {
                longestWord = max
            }
            for size in stride(from: maxFontSize, to: minimumFontSize, by: -CGFloat(0.1)) {
                let proposedFont = font.withSize(size)
                let constraintSize = CGSize(width: bounds.size.width, height: CGFloat(MAXFLOAT))
                let labelSize = ((text ?? "") as NSString).boundingRect(with: constraintSize,
                                                                        options: .usesLineFragmentOrigin,
                                                                        attributes: [NSFontAttributeName: proposedFont],
                                                                        context: nil)
    
                let wordConstraintSize = CGSize(width: CGFloat(MAXFLOAT), height: CGFloat(MAXFLOAT))
                let longestWordSize = ((longestWord ?? "") as NSString).boundingRect(with: wordConstraintSize,
                                                                        options: .usesFontLeading,
                                                                        attributes: [NSFontAttributeName: proposedFont],
                                                                        context: nil)
    
                if labelSize.height <= bounds.size.height && longestWordSize.width < constraintSize.width {
                    font = proposedFont
                    setNeedsLayout()
                    break
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-28 22:15

    There is a method on NSString, -sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode: which has apparently existed since iOS 2.0, but unfortunately is deprecated in iOS 7 without a suggested alternative as the automatic reduction of font size is discouraged. I don't really understand Apple's stance on this as they use it in keynote etc and I think if the font sizes are within a small range it is ok. Here's an implementation in Swift using this method.

    var newFontSize: CGFloat = 30
        let font = UIFont.systemFontOfSize(newFontSize)
        (self.label.text as NSString).sizeWithFont(font, minFontSize: 20, actualFontSize: &newFontSize, forWidth: self.label.frame.size.width, lineBreakMode: NSLineBreakMode.ByWordWrapping)
        self.label.font = font.fontWithSize(newFontSize)
    

    I'm not aware of a way this can be achieved without using deprecated methods.

    0 讨论(0)
  • 2020-11-28 22:15
    extension UILabel{
    
    func adjustFont(minSize:Int, maxSize:Int){
        var newFont = self.font
        for index in stride(from: maxSize, to: minSize, by: -1) {
            newFont = UIFont.systemFont(ofSize: CGFloat(index))
            let size = CGSize(width: self.frame.width, height: CGFloat(Int.max))
            let size2 = (self.text! as NSString).boundingRect(with: size, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedStringKey.font:newFont!], context: nil).size
            if size2.height < self.frame.size.height{
                break
            }
        }
        self.font = newFont
    }
    

    }

    you need to assign value to the numberOfLines property of UILabel as well.

    0 讨论(0)
  • 2020-11-28 22:21

    I modified the above code somewhat to make it a category on UILabel:

    Header file:

    #import <UIKit/UIKit.h>
    @interface UILabel (MultiLineAutoSize)
        - (void)adjustFontSizeToFit;
    @end
    

    And the implementation file:

    @implementation UILabel (MultiLineAutoSize)
    
    - (void)adjustFontSizeToFit
    {
        UIFont *font = self.font;
        CGSize size = self.frame.size;
    
        for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumFontSize; maxSize -= 1.f)
        {
            font = [font fontWithSize:maxSize];
            CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT);
            CGSize labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
            if(labelSize.height <= size.height)
            {
                self.font = font;
                [self setNeedsLayout];
                break;
            }
        }
        // set the font to the minimum size anyway
        self.font = font;
        [self setNeedsLayout];
    }
    
    @end
    
    0 讨论(0)
提交回复
热议问题