4
votes

I'm setting an image as the background for a SKScene with code similar to the following

/* In SKScene subclass */
background = SKSpriteNode()
background.anchorPoint = CGPointMake(0,1)
background.position = CGPointMake(0, size.height)
background.zPosition = Layer.Background
background.size = view!.bounds.size
background.texture = SKTexture(image: <# UIImage #>)
addChild(background)

This works to insert the image as a background for the scene, but if the image isn't the same aspect ratio as the background node, it is stretched to fill it.

(on the left is the result when an image is cropped to fit the aspect ratio of the SKSpriteNode, and on the right is the result when the image is of a different aspect ratio)

Is there a way to make the SKSpriteNode respect the original aspect ratio of the image, in the way that a UIImageView can be set to use UIViewContentModeScaleAspectFill?

EDIT Changed mention of UIViewContentModeScaleAspectFit to UIViewContentModeScaleAspectFill, got them mixed up.

2
Does your background image cover the whole screen?rakeshbs
No, it's a square whose dimensions are equal to the width of the device (e.g. on a 4in iPhone it would be 320x320).Ziewvater
so you want the image to always fit the width of the device without changing the aspect ratio?rakeshbs
I just realized that I made a mistake with my question. I want it to copy UIViewContentModeScaleAspectFill, not Fit, whoops. So I would want the texture to completely fill the scene, while respecting the image's aspect ratio, even if it were to leave out parts of the image to do so. I'll edit my question to reflect my mistake.Ziewvater

2 Answers

17
votes

You can create an extension for SKSpriteNode to do this.

extension SKSpriteNode {

    func aspectFillToSize(fillSize: CGSize) {

        if texture != nil {
            self.size = texture!.size()

            let verticalRatio = fillSize.height / self.texture!.size().height
            let horizontalRatio = fillSize.width /  self.texture!.size().width

            let scaleRatio = horizontalRatio > verticalRatio ? horizontalRatio : verticalRatio

            self.setScale(scaleRatio)
        }
    }

}

To use it

let background = SKSpriteNode()
background.anchorPoint = CGPointMake(0,1)
background.position = CGPointMake(0, size.height)
background.texture = SKTexture(imageNamed: "1.png")
background.aspectFillToSize(view.frame.size) // Do this after you set texture
addChild(background)
0
votes

Rakeshbs's answer is great. But I think the key point of this solution is to change the size of SKSpriteNode dynamically. Don't need to sale the node . So I update his code as below

extension SKSpriteNode {
func aspectFillToSize(fillSize: CGSize) {
    if let texture = self.texture {
        let horizontalRatio = fillSize.width / texture.size().width
        let verticalRatio = fillSize.height / texture.size().height
        let finalRatio = horizontalRatio < verticalRatio ? horizontalRatio : verticalRatio
        size = CGSize(width: texture.size().width * finalRatio, height: texture.size().height * finalRatio)
    }
}

}