How to set font size of SKLabelNode to fit in fixed size (Swift)

8.1k views Asked by At

I have a square (200X200) with a SKLabelNode in it. The label shows score and it my be reach a large number. I want fit the number in the square.

How can i change text size (or Size) of a SKLabelNode to fit it in a fixed size.

3

There are 3 answers

3
mike663 On

The size of the frame of the SKLabelNode can be compared against the given rectangle. If you scale the font in proportion to the sizes of the label's rectangle and the desired rectangle, you can determine the best font size to fill the space as much as possible. The last line conveniently moves the label to the center of the rectangle. (It may look off-center if the text is only short characters like lowercase letters or punctuation.)

Swift

func adjustLabelFontSizeToFitRect(labelNode:SKLabelNode, rect:CGRect) {

    // Determine the font scaling factor that should let the label text fit in the given rectangle.
    let scalingFactor = min(rect.width / labelNode.frame.width, rect.height / labelNode.frame.height)

    // Change the fontSize.
    labelNode.fontSize *= scalingFactor

    // Optionally move the SKLabelNode to the center of the rectangle.
    labelNode.position = CGPoint(x: rect.midX, y: rect.midY - labelNode.frame.height / 2.0)
}

Objective-C

-(void)adjustLabelFontSizeToFitRect:(SKLabelNode*)labelNode rect:(CGRect)rect {

    // Determine the font scaling factor that should let the label text fit in the given rectangle.
    double scalingFactor = MIN(rect.size.width / labelNode.frame.size.width, rect.size.height / labelNode.frame.size.height);

    // Change the fontSize.
    labelNode.fontSize *= scalingFactor;

    // Optionally move the SKLabelNode to the center of the rectangle.
    labelNode.position = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect) - labelNode.frame.size.height / 2.0);
}
0
Myoch On

I have written this for the width, but you can adapt it to the height to fit your CGRect. In the example, pg is a SKLabelNode initialized with the font you are using. Arguments are your String and the target width, and the result is the size you want to assign to your SKLabelNode. Of course, you can also put directly your SKLabelNode. If the size is too big, then the max size is 50, but that's personal.

 func getTextSizeFromWidth(s:String, w:CGFloat)->CGFloat {

    var result:CGFloat = 0;
    var fits:Bool = false
    pg!.text=s
    if(s != ""){
      while (!fits) {
        result++;
        pg!.fontSize=result
        fits = pg!.frame.size.width > w;
      }
    result -= 1.0
    }else{
        result=0;
    }

    return min(result, CGFloat(50))
}

Edit: Actually, I just realized I had also written this:

extension SKLabelNode {
func fitToWidth(maxWidth:CGFloat){
    while frame.size.width >= maxWidth {
        fontSize-=1.0
    }
}

func fitToHeight(maxHeight:CGFloat){
    while frame.size.height >= maxHeight {
        fontSize-=1.0
    }
0
DenverCoder9 On

This expansion of mike663's answer worked for me, and gets there much quicker than going 1 pixel at a time.

// Find the right size by trial & error...
var testingSize: CGFloat = 0    // start from here
var currentStep: CGFloat = 16   // go up by this much. It will be reduced each time we overshoot.
var foundMatch = false

while !foundMatch {
    var overShot = false
    while !overShot {
        testingSize += currentStep
        labelNode.fontSize = testingSize
        // How much bigger the text should be to perfectly fit in the given rectangle.
        let scalingFactor = min(rect.width / labelNode.frame.width, rect.height / labelNode.frame.height)

        if scalingFactor < 1         { overShot   = true }  // Never go over the space
        else if scalingFactor < 1.01 { foundMatch = true }  // Stop when over 99% of the space is filled
    }
    testingSize -= currentStep  // go back to the last one that didn't overshoot
    currentStep /= 4
}
labelNode.fontSize = testingSize // go back to the one we were happy with