Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
530 views
in Technique[技术] by (71.8m points)

SwiftUI unexpected position changes during animation

I have made a simple animation in SwiftUI which repeats forever, directly when starting the app. However when running the code the animated circle goes to the left top and then back to its normal position while doing the rest of the animation. This top left animation is not expected and nowhere written in my code.

Sample code:

struct Loader: View {
    
    @State var animateFirst = false

    @State var animationFirst = Animation.easeInOut(duration: 3).repeatForever()
    var body: some View {
        ZStack {
            Circle().trim(from: 0, to: 1).stroke(ColorsPalette.Primary, style: StrokeStyle(lineWidth: 3, lineCap: .round)).rotation3DEffect(
                .degrees(self.animateFirst ? 360 : -360),
                axis: (x: 1, y: 0, z: 0), anchor: .center).frame(width: 200, height: 200, alignment: .center).animation(animationFirst).onAppear {
                self.animateFirst.toggle()
            }
        }
    }
    
}

and then showing it in a view like this:

struct LoaderViewer: View {
    
    var body: some View {
        VStack {
            Loader().frame(width: 200, height: 200)
        }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I don't know exactly why this happens but I have a vague idea. Because the animation starts before the view is fully rendered in the width an height, the view starts in the op left corner. The animation takes the starting position in the animation and the change to its normal position and keep repeating this part with the given animation. Which causes this unexpected behaviour.

I think it is a bug, but with the theory above I have created a workaround for now. In the view where we place the loader we have to wait till the parent view is fully rendered before adding the animated view (Loader view), we can do that like this:

struct LoaderViewer: View {

    @State showLoader = false

    var body: some View {
        VStack {
            if showLoader {
                Loader().frame(width: 200, height: 200)
            }
        }.onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
                showLoader = true
            }
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...