最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

swift - Tiled Background Image Gets Cut Off and Dimmed During Movement - Stack Overflow

programmeradmin4浏览0评论

When trying to create the illusion of movement with a tiled background image, I noticed that the image gets cut off when I shift it. This happens because the offset moves parts of the image outside of the view’s boundaries, exposing gaps. Also, the image seems to get dimmed when I press the move button. I want the image to shift across the screen seamlessly, without any gaps or dimming, and to keep the tiling consistent across the entire screen.

import SwiftUI

struct ContentView: View {
    @State private var offset: CGFloat = 0.0
    var body: some View {
        ZStack {
            Image("carrot")
                .resizable(resizingMode: .tile)
                .offset(x: offset)
            VStack {
                Spacer()
                HStack {
                    Button("<<<<<") {
                        offset -= 50.0
                    }
                    Button(">>>>>") {
                        offset += 50.0
                    }
                }
                .buttonStyle(.borderedProminent)
                .padding()
            }
        }
        .animation(.easeInOut, value: offset)
    }
}

When trying to create the illusion of movement with a tiled background image, I noticed that the image gets cut off when I shift it. This happens because the offset moves parts of the image outside of the view’s boundaries, exposing gaps. Also, the image seems to get dimmed when I press the move button. I want the image to shift across the screen seamlessly, without any gaps or dimming, and to keep the tiling consistent across the entire screen.

import SwiftUI

struct ContentView: View {
    @State private var offset: CGFloat = 0.0
    var body: some View {
        ZStack {
            Image("carrot")
                .resizable(resizingMode: .tile)
                .offset(x: offset)
            VStack {
                Spacer()
                HStack {
                    Button("<<<<<") {
                        offset -= 50.0
                    }
                    Button(">>>>>") {
                        offset += 50.0
                    }
                }
                .buttonStyle(.borderedProminent)
                .padding()
            }
        }
        .animation(.easeInOut, value: offset)
    }
}
Share Improve this question edited Jan 18 at 11:19 Benzy Neez 22.5k3 gold badges15 silver badges43 bronze badges asked Jan 18 at 4:33 MangoMango 556 bronze badges 0
Add a comment  | 

1 Answer 1

Reset to default 0

When an area is filled using a tiled image, the tiling always starts in the top-left corner. So if you want to have the effect of an offset, it means moving the starting position for the tiling.

  • A move to the left is easy: just add negative padding to the image. There is then no need to apply a negative x-offset as well.

  • Moving to the right is much harder. There needs to be a "buffer region" that is off-screen. This is then moved into view with a positive x-offset.

The size of the buffer region needs to be an exact multiple of the image width. If the image size is not known then it can be measured using a hidden version of the image in the background of the ZStack.

The width of the display area also needs to be known. This can be measured using a GeometryReader.

Finally, you need to control which changes are animated and which are not. This can be done with a combination of .animation modifiers and the modifier .geometryGroup.

Here is an adaption of your example that works this way:

struct ContentView: View {
    @State private var offset: CGFloat = 0.0
    @State private var imageWidth = CGFloat.zero

    var body: some View {
        ZStack {
            GeometryReader { proxy in
                let fullWidth = proxy.size.width
                let nExtraImages = imageWidth > 0 ? (max(0, offset) / imageWidth).rounded(.up) : 0
                let extendedWidth = fullWidth + ((nExtraImages + 1) * imageWidth)
                Image("carrot")
                    .resizable(resizingMode: .tile)
                    .padding(.leading, min(0, offset))
                    .frame(width: extendedWidth)
                    .animation(nil, value: extendedWidth)
                    .frame(width: fullWidth, alignment: .trailing)
                    .geometryGroup()
                    .offset(x: max(0, offset))
                    .animation(.easeInOut, value: offset)
            }
            VStack {
                Spacer()
                HStack {
                    Button("<<<<<") {
                        offset -= 50.0
                    }
                    Button(">>>>>") {
                        offset += 50.0
                    }
                }
                .buttonStyle(.borderedProminent)
                .padding()
            }
        }
        .background {
            Image("carrot")
                .hidden()
                .onGeometryChange(for: CGFloat.self) { proxy in
                    proxy.size.width
                } action: { width in
                    imageWidth = width
                }

        }
    }
}

Regarding dimming, I did not see this problem happening. However, it may be related to the way the image is being prepared for render. So it may help to add versions of the image with different resolutions to your asset catalogue (something like: carrot.png, [email protected], [email protected]).

Btw, if your ultimate objective is to show the tiles moving in the background with a continuous animation then I would not use an ever-increasing offset to do it. Use a repeating animation instead.

发布评论

评论列表(0)

  1. 暂无评论