Paboka
Paboka

Reputation: 442

Tile a rectangle with a custom shape in SwiftUI

Is there a way to fill an area tiling it with a custom shape? Like we can do with

Image("SomeImage")
    .resizable(resizingMode: .tile)

but I want to be able to change the shape of an Image (like changing a circle size) I fill with dynamically. Something like an analogue of VisualBrush with a propper TileMode in WPF.

Using ForEach + HStack/VStack gives really poor performance.

Upvotes: 0

Views: 1183

Answers (1)

Asperi
Asperi

Reputation: 257719

Image has own content size, so can be tiled as-is, but Shape by design has no own size and fill all provided rectangle, so it is needed to specify which size to use for tiles.

Here is possible solution. Tested with Xcode 12 / iOS 14

demo

struct DemoShapeTiling: View {
    var body: some View {
        Rectangle().fill(Color.blue)
            .tile(Circle(), of: CGSize(width: 40, height: 40))
            .foregroundColor(.red)
            .clipped()
    }
}

struct TilingShape<S: Shape>: Shape {
    let shape: S
    let size: CGSize

    func path(in rect: CGRect) -> Path {
        var path = Path()
        for x in stride(from: CGFloat.zero, to: rect.size.width, by: size.width) {
            for y in stride(from: CGFloat.zero, to: rect.size.height, by: size.height) {
                let r = CGRect(origin: CGPoint(x: x, y: y), size: size)
                path.addPath(shape.path(in: r))
            }
        }
        return path
    }
}

extension View {
    public func tile<S: Shape>(_ shape: S, of size: CGSize) -> some View {
        self.overlay(TilingShape(shape: shape, size: size))
    }
}

Upvotes: 1

Related Questions