0

I'm trying to display a right-aligned timecode in my game. I had expected that digits would all have the same width, but this doesn't seem to be the case in SpriteKit, even though it seems to be the case in AppKit.

In SpriteKit, with the default font there is a noticeable difference in width between the digit 1 and the rest (1 is thinner), so whenever displaying a number with the least significant digit 1 all preceding digits shift slightly to the right. This happens even when setting a NSAttributedString with a font that has a fixedAdvance attribute.

class GameScene: SKScene {
    
    override func didMove(to view: SKView) {
        let label = SKLabelNode(text: "")
        view.scene!.addChild(label)
//        label.horizontalAlignmentMode = .left
        label.horizontalAlignmentMode = .right
        var i = 11
        Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in
            label.text = "\(i)"
//            let font = NSFont(descriptor: NSFontDescriptor(fontAttributes: [.name: "HelveticaNeue-UltraLight", .fixedAdvance: 20]), size: 30)!
//            let paragraphStyle = NSMutableParagraphStyle()
//            paragraphStyle.alignment = .right
//            label.attributedText = NSAttributedString(string: "\(i)", attributes: [.font: font, .foregroundColor: SKColor.labelColor, .paragraphStyle: paragraphStyle])
            i += 5
        }
    }
    
}

With AppKit, when using SpriteKit's default font HelveticaNeue-UltraLight, this issue doesn't exist, regardless whether I set the fixedAdvance font attribute.

class ViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let font = NSFont(descriptor: NSFontDescriptor(fontAttributes: [.name: "HelveticaNeue-UltraLight"]), size: 30)!
//        let font = NSFont(descriptor: NSFontDescriptor(fontAttributes: [.name: "HelveticaNeue-Light", .fixedAdvance: 20]), size: 30)!
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.alignment = .right
        let textField = NSTextField(labelWithString: "")
        textField.font = font
        textField.alignment = .right
//        textField.alignment = .left
        textField.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
        view.addSubview(textField)
        var i = 11
        Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in
            textField.stringValue = "\(i)"
//            textField.attributedStringValue = NSAttributedString(string: "\(i)", attributes: [.font: font, .paragraphStyle: paragraphStyle])
            i += 5
        }
    }

}

Is there a solution to this problem?

1
  • i agree this is strange behavior. i just looked at label.calculateAccumulatedFrame().width: for the first 30 integers and single digits don't even have consistent widths. it appears to be highly contextual -- even for supposedly fixed-width fonts like Menlo or Courier. ex: you'd expect the width delta from 0 to 1 to be the same as the delta from 10 to 11; but no joy. one solution: you could roll your own font using textures for the integers 0-9. that way you have full control over width. i've done that before for fonts that require pixel perfect accuracy Commented Nov 21, 2024 at 13:55

1 Answer 1

0

I found a workaround that causes the width of the least significant digit to be equal as the others:

label.numberOfLines = 0
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.