SKTexture initialized with system UIImage has slightly wrong aspect ratio and ignores system symbol color

On macOS, system symbols displays in a SKTexture as expected, with the correct color and aspect ratio.

But on iOS they are always displayed in black, and sometimes with slightly wrong aspect ratio.

Is there a solution to this problem?

import SpriteKit
#if os(macOS)
import AppKit
#else
import UIKit
#endif

class GameScene: SKScene {
    
    override func didMove(to view: SKView) {
        let systemImage = "square.and.arrow.up"
        let width = 400.0
        #if os(macOS)
        let image = NSImage(systemSymbolName: systemImage, accessibilityDescription: nil)!.withSymbolConfiguration(.init(hierarchicalColor: .white))!
        let scale = NSScreen.main!.backingScaleFactor
        image.size = CGSize(width: width * scale, height: width / image.size.width * image.size.height * scale)
        #else
        let image = UIImage(systemName: systemImage)!.applyingSymbolConfiguration(.init(pointSize: width))!.applyingSymbolConfiguration(.init(hierarchicalColor: .white))!
        #endif
        let texture = SKTexture(image: image)
        print(image.size, texture.size(), image.size.width / image.size.height)
        let size = CGSize(width: width, height: width / image.size.width * image.size.height)
        addChild(SKSpriteNode(texture: texture, size: size))
    }

}
Answered by Nickkk in 803966022

In principle you could render the UIImage into a graphics context and then create your SKTexture from that.

That's a great idea. I changed the iOS code to this and it seems to work for both aspect ratio and color:

var image = UIImage(systemName: systemImage)!.applyingSymbolConfiguration(.init(pointSize: width))!.applyingSymbolConfiguration(.init(hierarchicalColor: .white))!
image = UIGraphicsImageRenderer(size: image.size).image { context in
    image.draw(at: .zero)
}

This may be a bug in SpriteKit given your description.

Have you filed a bug report? If so please reply with the Feedback ID.

Have you filed a bug report? If so please reply with the Feedback ID.

I filed FB15095279 shortly before I created this post.

Acknowledged and appreciated!

UIImage and CGImage have different aspect ratios for SF Symbols, due to padding. Have a look at this thread:

https://developer.apple.com/forums/thread/716773

It wouldn’t surprise me if SKTexture does the same thing.

UIImage and CGImage have different aspect ratios for SF Symbols, due to padding.

I tried using SKTexture(image: image) and SKTexture(cgImage: image.cgImage!) and they both produce the same result. Do you think this is expected or do you have a suggestion for how one could do it differently?

I believe there is no way to get the extra padding info. Yes I filed a bug ha ha ha. In principle you could render the UIImage into a graphics context and then create your SKTexture from that. Do you actually care about accurate typographic alignment of your symbol? If not, just use what you have.

I know nothing about the colour issues you have mentioned.

Accepted Answer

In principle you could render the UIImage into a graphics context and then create your SKTexture from that.

That's a great idea. I changed the iOS code to this and it seems to work for both aspect ratio and color:

var image = UIImage(systemName: systemImage)!.applyingSymbolConfiguration(.init(pointSize: width))!.applyingSymbolConfiguration(.init(hierarchicalColor: .white))!
image = UIGraphicsImageRenderer(size: image.size).image { context in
    image.draw(at: .zero)
}

^ looks like a reasonable workaround.

The only remaining concern is performance but it seems like you would only call this sparingly during view layout.