Nice programing

여러 줄이있는 UILabel에서 자동 축소

nicepro 2020. 12. 27. 20:46
반응형

여러 줄이있는 UILabel에서 자동 축소


의 여러 줄에서 autoshrink 속성을 함께 사용할 수 UILabel있습니까? 예를 들어, 사용 가능한 두 줄에서 가능한 큰 텍스트 크기입니다.


이 사람들은 해결책을 찾았습니다.

http://www.11pixel.com/blog/28/resize-multi-line-text-to-fit-uilabel-on-iphone/

그들의 해결책은 다음과 같습니다.

int maxDesiredFontSize = 28;
int minFontSize = 10;
CGFloat labelWidth = 260.0f;
CGFloat labelRequiredHeight = 180.0f;
//Create a string with the text we want to display.
self.ourText = @"This is your variable-length string. Assign it any way you want!";

/* This is where we define the ideal font that the Label wants to use.
   Use the font you want to use and the largest font size you want to use. */
UIFont *font = [UIFont fontWithName:@"Marker Felt" size:maxDesiredFontSize];

int i;
/* Time to calculate the needed font size.
   This for loop starts at the largest font size, and decreases by two point sizes (i=i-2)
   Until it either hits a size that will fit or hits the minimum size we want to allow (i > 10) */
for(i = maxDesiredFontSize; i > minFontSize; i=i-2)
{
    // Set the new font size.
    font = [font fontWithSize:i];
    // You can log the size you're trying: NSLog(@"Trying size: %u", i);

    /* This step is important: We make a constraint box 
       using only the fixed WIDTH of the UILabel. The height will
       be checked later. */ 
    CGSize constraintSize = CGSizeMake(labelWidth, MAXFLOAT);

    // This step checks how tall the label would be with the desired font.
    CGSize labelSize = [self.ourText sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];

    /* Here is where you use the height requirement!
       Set the value in the if statement to the height of your UILabel
       If the label fits into your required height, it will break the loop
       and use that font size. */
    if(labelSize.height <= labelRequiredHeight)
        break;
}
// You can see what size the function is using by outputting: NSLog(@"Best size is: %u", i);

// Set the UILabel's font to the newly adjusted font.
msg.font = font;

// Put the text into the UILabel outlet variable.
msg.text = self.ourText;

이 작업을 수행하려면 인터페이스 빌더에서 IBOutlet을 UILabel에 할당해야합니다.

"IBOutlet UILabel * msg;"

모든 장점은 11 픽셀의 사람들입니다.


위의 코드를 약간 수정하여 카테고리로 만들었습니다 UILabel.

헤더 파일 :

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

그리고 구현 파일 :

@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

이 링크를 찾았습니다 http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/

이 문제는 Interface Builder를 사용하여 간단한 3 단계로 해결할 수 있습니다.

  1. "Autoshrink"를 "최소 글꼴 크기"로 설정합니다.
  2. 글꼴을 원하는 가장 큰 글꼴 크기 (20)로 설정하고 Lines를 예를 들어 10으로 설정합니다. 제 경우에는 해당 글꼴 크기의 레이블에 들어갈 수있는 줄 수입니다.
  3. 그런 다음 "줄 바꿈"을 "단어 줄 바꿈"에서 "꼬리 자르기"로 변경합니다.

도움이 되었기를 바랍니다.


다음은 itecedor의 iOS 6 업데이트를 기반으로 iOS 7로 업데이트 된 카테고리 솔루션입니다.

헤더 파일 :

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

그리고 구현 파일 :

@implementation UILabel (MultiLineAutoSize)


- (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);

        CGRect textRect = [self.text boundingRectWithSize:constraintSize
                                             options:NSStringDrawingUsesLineFragmentOrigin
                                          attributes:@{NSFontAttributeName:font}
                                             context:nil];

        CGSize labelSize = textRect.size;


        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

해결책으로 표시된 대답은 엉망이고 부정확합니다. UILabel은 다음 속성을 올바르게 설정하면 자동으로 처리합니다.

numberOfLines 0이 아니어야합니다

adjustsFontSizeToFitWidth 반드시 YES

lineBreakMode있어야 하지NSLineBreakByCharWrapping또는NSLineBreakByWordWrapping


@DaGaMs에서 채택한 신속한 버전.

SWIFT 2 :

extension UILabel {
    func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) {
        let maxFontSize = maximumFontSize ?? font.pointSize
        for size in stride(from: maxFontSize, to: minimumFontSize, by: -CGFloat(0.1)) {
            let proposedFont = font.fontWithSize(size)
            let constraintSize = CGSizeMake(bounds.size.width, CGFloat(MAXFLOAT))
            let labelSize = ((text ?? "") as NSString).boundingRectWithSize(constraintSize,
                options: .UsesLineFragmentOrigin,
                attributes: [NSFontAttributeName: proposedFont],
                context: nil)
            if labelSize.height <= bounds.size.height {
                font = proposedFont
                setNeedsLayout()
                break;
            }
        }
    }
}

SWIFT 3 :

extension UILabel {
    func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) {
        let maxFontSize = maximumFontSize ?? font.pointSize
        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)
            if labelSize.height <= bounds.size.height {
                font = proposedFont
                setNeedsLayout()
                break;
            }
        }
    }
}

평판이 부족해서 몬티 래빗의 게시물에 대해서는 댓글을 달 수 없으니 새로운 답변을 드리겠습니다. 그가 제안한 솔루션은 Xcode 7.3 이상에서 작동하지 않으며 정확하지 않습니다. 제대로 작동하려면 스토리 보드에서 다음을 수행해야했습니다.

  1. 너비 제한 설정 (순수 너비 또는 꼬리 및 리드)
  2. HEIGHT CONSTRAINT 설정 (매우 중요합니다. 일반적으로 자동 크기 조정은 레이블 높이를 설정하지 않습니다.)
  3. "Autoshrink"속성을 "최소 글꼴 크기"또는 "최소 글꼴 크기"로 설정합니다 (두 경우 모두 작동).
  4. "줄 바꿈"속성을 "꼬리 자르기"로 설정합니다.
  5. "Lines"속성을 0이 아닌 값으로 설정합니다.

도움이 되었기를 바랍니다. ;)


나는 DaGaMs의 대답을 좋아했지만 dequeueReusableCell : 반환 될 수있는 UITableViewCells와 같은 레이블을 사용하면 원래 글꼴 크기가 텍스트가 적고 활용할 수있는 일부 tableView 셀에 대해 여전히 원하는 경우에도 일반 글꼴 크기가 계속 축소됩니다. 원래 라벨의 원래 글꼴 크기.

그래서 저는 DaGaMs의 카테고리로 시작하여 별도의 카테고리가 아닌 별도의 클래스를 만들고 스토리 보드의 UILabels가이 새로운 클래스를 사용하는지 확인합니다.

#import "MultiLineAutoShrinkLabel.h"

@interface MultiLineAutoShrinkLabel ()
@property (readonly, nonatomic) UIFont* originalFont;
@end

@implementation MultiLineAutoShrinkLabel

@synthesize originalFont = _originalFont;

- (UIFont*)originalFont { return _originalFont ? _originalFont : (_originalFont = self.font); }

- (void)quoteAutoshrinkUnquote
{
    UIFont* font = self.originalFont;
    CGSize frameSize = self.frame.size;

    CGFloat testFontSize = _originalFont.pointSize;
    for (; testFontSize >= self.minimumFontSize; testFontSize -= 0.5)
    {
        CGSize constraintSize = CGSizeMake(frameSize.width, MAXFLOAT);
        CGSize testFrameSize = [self.text sizeWithFont:(font = [font fontWithSize:testFontSize])
                                     constrainedToSize:constraintSize
                                         lineBreakMode:self.lineBreakMode];
        // the ratio of testFontSize to original font-size sort of accounts for number of lines
        if (testFrameSize.height <= frameSize.height * (testFontSize/_originalFont.pointSize))
            break;
    }

    self.font = font;
    [self setNeedsLayout];
}

@end

itedcedor의 답변에는 pwightman이 지적한 문제가 있습니다. 또한 공백을 다듬을 필요가 없습니다. 다음은 수정 된 버전입니다.

- (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];
}

이 솔루션에 대해 DaGaMs에 감사드립니다.

다음과 같이 업데이트했습니다.

1-iOS 6에서 작업하려면 (minimumFontSize 및 UILineBreakModeWordWrap이 모두 사용되지 않기 때문에) 2-크기 조정이 실패 할 수 있으므로 레이블 텍스트에서 공백을 제거합니다 (찾는 데 얼마나 걸 렸는지 알고 싶지 않습니다 곤충)

-(void)adjustFontSizeToFit 
{
    self.text = [self.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

    UIFont *font = self.font;
    CGSize size = self.frame.size;

    for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumScaleFactor; 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];
}

UIButton의 경우 다음 줄만 작동합니다.

self.centerBtn.titleLabel.numberOfLines = 2;
self.centerBtn.titleLabel.textAlignment = NSTextAlignmentCenter;
self.centerBtn.titleLabel.adjustsFontSizeToFitWidth = YES;

@wbarksdale의 Swift 3 솔루션을 사용했지만 긴 단어가 중간에서 잘리는 것을 발견했습니다. 단어를 그대로 유지하려면 다음과 같이 수정해야했습니다.

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

이 기능을 달성하기 위해 위의 "The Dude 's"답변을 기반으로 UILabel에 작은 범주를 작성했습니다.

https://gist.github.com/ayushn21/d87b835b2efc756e859f


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.


Try this:

Either subclass UILabel or call adjustFontSize method after setting the text property on a label

override var text : String? { didSet { self.adjustFontSize() } }

func adjustFontSize()
{
    var lineCount = self.string.components(separatedBy: "\n").count - 1
    var textArray = self.string.components(separatedBy: " ")
    var wordsToCompare = 1
    while(textArray.count > 0)
    {
        let words = textArray.first(n: wordsToCompare).joined(separator: " ")
        let wordsWidth = words.widthForHeight(0, font: self.font)
        if(wordsWidth > self.frame.width)
        {
            textArray.removeFirst(wordsToCompare)
            lineCount += 1
            wordsToCompare = 1
        }
        else if(wordsToCompare > textArray.count)
        {
            break
        }
        else
        {
            wordsToCompare += 1
        }
    }
    self.numberOfLines = lineCount + 1
}

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.

ReferenceURL : https://stackoverflow.com/questions/9059631/autoshrink-on-a-uilabel-with-multiple-lines

반응형