swiftuitexturesresizable

How to tile a texture vertically only?


In my app, I need to display images (textures). Some are tiled, and some are stretched.

At the time, I have this code working:

Image(frame.image)
.resizable(resizingMode: (frame.repeatTexture == true) ? .tile : .stretch)
.frame(width: frame.width, height: frame.height)

However,the render of the .tile is not good. The texture is cropped in X axis. In the image sent, you can see that the bezel is not present at the right, it's truncated.

render

Original texture where there is a bezel at left and right:

texture

I would like to have the "repeat" only in Y for example. Seems there is no option for that by default.

Is there a tip, an option, or maybe another idea like using background() to force the texture to tile only in one axis?


Solution

  • You can draw a stretched version of the image using a GraphicsContext, and then tile that using resizable,

    let imageHeight = ... // replace this with the height of your image
    let frame = CGSize(width: 300, height: 700) // as an example
    Image(size: .init(width: frame.width, height: imageHeight)) { gc in
        let image = gc.resolve(Image(.tile))
        let scale = frame.width / image.size.width
        gc.scaleBy(x: scale, y: 1)
        gc.draw(image, at: .zero, anchor: .topLeading)
        gc.scaleBy(x: 1 / scale, y: 1)
    }
    .resizable(resizingMode: .tile)
    .frame(width: frame.width, height: frame.height)
    

    Or, just draw the entire tiled image with the GraphicsContext.

    Image(size: frame) { gc in
        var startY: CGFloat = 0
        let image = gc.resolve(Image(.tile))
        while startY < frame.height {
            let scale = frame.width / image.size.width
            gc.scaleBy(x: scale, y: 1)
            gc.draw(image, at: .init(x: 0, y: startY), anchor: .topLeading)
            gc.scaleBy(x: 1 / scale, y: 1)
            startY += image.size.height
        }
    }