【问题标题】:SwiftUI: How to animate a child of a parent which is currently animatedSwiftUI:如何为当前动画的父母的孩子制作动画
【发布时间】:2021-05-23 03:42:48
【问题描述】:

我正在尝试创建一个应用程序,其中有多个视图可以在它们之间转换,其中包含动画。到目前为止,这是通过在 scrollView 中为 VStack 的偏移量设置动画来完成的,其中偏移量是通过将屏幕高度乘以当前屏幕的索引来计算的。我决定采用这条路线而不是使用 scrollTo,因为我想自定义过渡动画以缓入和缓出。

这是 ContentView 代码:

import SwiftUI

struct ContentView: View {
    
    @Namespace var carousel
    @Namespace var splash
    @Namespace var home

    @State private var activeIndex = CGFloat(0)

    var body: some View {
        GeometryReader { geo in
            ScrollViewReader { proxy in
                ScrollView([]) {
                    VStack (spacing: 0) {
                        ZStack {
                            Color.red
                            
                            Splash(home: home, geo: geo, proxy: proxy)
                            .frame(width: geo.size.width, height: geo.size.height, alignment: .leading)
                            .id(splash)
                        }
                        
                        ZStack {
                            Color.blue
                            
                            VStack {
                                Home(activeIndex: self.$activeIndex, splash: splash, home: home, geo: geo, proxy: proxy)
                                .frame(width: geo.size.width, height: geo.size.height, alignment: .leading)
                                .offset(y: -200)
                                .id(home)
                            }
                        }
                    }
                    .offset(y: activeIndex > 0 ? -geo.size.height * activeIndex + 200 : -geo.size.height * activeIndex)
                    .animation(.easeInOut(duration: 0.8))
                    .id(carousel)
                }
                .frame(width: geo.size.width, height: geo.size.height)
            }
        }.ignoresSafeArea()
    }
}

这是按钮所在的代码:

import SwiftUI

struct Home: View {
    
    @Binding var activeIndex: CGFloat
    var splash: Namespace.ID
    var home: Namespace.ID
    var geo: GeometryProxy
    var proxy: ScrollViewProxy

    var body: some View {
        VStack (alignment: .leading, spacing: 0) {
            HStack {
                Spacer()
                VStack {
                    Button(activeIndex == 0 ? "Let's go" : "Go Back") {
                        if activeIndex == 0 {
                            self.activeIndex = 1;
                        }
                        else {
                            self.activeIndex = 0;
                        }
                    }
                    .frame(width: 200, height: 50)
                    .font(.custom("Montserrat-SemiBold", size: 18))
                    .background(Color.green)
                    .cornerRadius(25)
                    .foregroundColor(.white)
                    .offset(y: 25)
                }
                Spacer()
            }
            .zIndex(1)
            VStack {
            }
            .frame(width: geo.size.width, height: geo.size.height * 0.8, alignment: .leading)
            .background(Color.white)
            .cornerRadius(40)
        }
        .frame(width: geo.size.width, height: geo.size.height, alignment: .leading)
        .cornerRadius(40)
    }
}

Here's a gif of the functionality so far

我想为主页上的“Let's Go”按钮设置动画,使其轻微摆动,然后当按下它时,动画在屏幕转换开始时停止。但是,这并不像我想象的那么容易,因为这个动画似乎受到了 ContentView 中 VStack 上的动画的影响。

这是我制作此动画的尝试:

import SwiftUI

struct Home: View {
    
    @State private var isAnimating = false
    @State private var stopAnimating = false
    
    @Binding var activeIndex: CGFloat
    var splash: Namespace.ID
    var home: Namespace.ID
    var geo: GeometryProxy
    var proxy: ScrollViewProxy

    var body: some View {
        VStack (alignment: .leading, spacing: 0) {
            HStack {
                Spacer()
                VStack {
                    Button(activeIndex == 0 ? "Let's go" : "Go Back") {
                        if activeIndex == 0 {
                            self.activeIndex = 1;
                        }
                        else {
                            self.activeIndex = 0;
                        }
                        isAnimating.toggle()
                        stopAnimating.toggle()
                    }
                    .frame(width: 200, height: 50)
                    .font(.custom("Montserrat-SemiBold", size: 18))
                    .background(Color.green)
                    .cornerRadius(25)
                    .foregroundColor(.white)
                    .offset(y: 25)
                    .shadow(color: Color.black.opacity(0.35), radius: 10)
                    .rotationEffect(Angle(degrees: self.isAnimating ? 5 : self.stopAnimating ? 0 : -5))
                    .animation(self.isAnimating ? Animation // Animation added
                            .easeInOut(duration: 0.2)
                                .repeatForever() : .default, value: self.isAnimating)
                    .onAppear { self.isAnimating = true }
                }
                Spacer()
            }
            .zIndex(1)
            VStack {
            }
            .frame(width: geo.size.width, height: geo.size.height * 0.8, alignment: .leading)
            .background(Color.white)
            .cornerRadius(40)
        }
        .frame(width: geo.size.width, height: geo.size.height, alignment: .leading)
        .cornerRadius(40)
    }
}

And here is the (hilarious) output

任何帮助将不胜感激!

【问题讨论】:

    标签: swift animation swiftui


    【解决方案1】:

    我最终使用DispatchQueue.main.asyncAfter 设置了一个超时,以在屏幕转换完成后停止和启动摆动动画。目前看来工作正常。

    import SwiftUI
    
    struct Home: View {
        
        @State private var isAnimating = false
        @State private var stopAnimating = false
        
        @Binding var activeIndex: CGFloat
        var splash: Namespace.ID
        var home: Namespace.ID
        var geo: GeometryProxy
        var proxy: ScrollViewProxy
    
        var body: some View {
            VStack (alignment: .leading, spacing: 0) {
                HStack {
                    Spacer()
                    VStack {
                        Button(activeIndex == 0 ? "Let's go" : "Go Back") {
                            if activeIndex == 0 {
                                self.activeIndex = 1;
                            }
                            else {
                                self.activeIndex = 0;
                            }
                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) { // THIS
                                isAnimating.toggle()
                                stopAnimating.toggle()
                            }
                        }
                        .frame(width: 200, height: 50)
                        .font(.custom("Montserrat-SemiBold", size: 18))
                        .background(Color.green)
                        .cornerRadius(25)
                        .foregroundColor(.white)
                        .offset(y: 25)
                        .shadow(color: Color.black.opacity(0.35), radius: 10)
                        .rotationEffect(Angle(degrees: self.isAnimating ? 5 : self.stopAnimating ? 0 : -5))
                        .animation(self.isAnimating ? Animation // Animation added
                                .easeInOut(duration: 0.2)
                                    .repeatForever() : .default, value: self.isAnimating)
                        .onAppear { self.isAnimating = true }
                    }
                    Spacer()
                }
                .zIndex(1)
                VStack {
                }
                .frame(width: geo.size.width, height: geo.size.height * 0.8, alignment: .leading)
                .background(Color.white)
                .cornerRadius(40)
            }
            .frame(width: geo.size.width, height: geo.size.height, alignment: .leading)
            .cornerRadius(40)
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-01-09
      • 1970-01-01
      • 2021-01-22
      • 1970-01-01
      • 2011-07-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多