如何在SwiftUI中进行变换动画?

时间:2020-11-10 04:01:42

标签: swiftui

在这里,我得到了2个形状,一个矩形和一个圆形,通过按钮的作用,只有其中一个对用户可见,我尝试使用@Namespace进行此转换,但没有成功!

我的目标:拥有一种从一个形状到另一个形状的平滑流畅的变换动画。

enter image description here

struct ContentView: View {

@State var action: Bool = false
@Namespace var sameShape

var body: some View {
    
    
    ZStack
    {
        
        Group
        {
            if action
            {
                Circle()
                    .fill(Color.blue).frame(width: 150, height: 150, alignment: .center)
                    .matchedGeometryEffect(id: "Dakota148Zero", in: sameShape)
            }
            else
            {
                Rectangle()
                    .fill(Color.red).frame(width: 150, height: 150, alignment: .center)
                    .matchedGeometryEffect(id: "Dakota148Zero", in: sameShape)
            }
        }
        .animation(.easeInOut)
        
        
        
        VStack
        {
            
            Spacer()
            
            Button("transform") { action.toggle() }.font(Font.largeTitle).padding()
            
        }
        
    }
    
    
    }
}

3 个答案:

答案 0 :(得分:2)

Group不是真实的容器,因此请勿存储动画。将Group替换为某些堆栈,例如

        VStack
        {
            if action
              // ... other code no change

答案 1 :(得分:1)

这是通过将圆形和正方形表示为具有不同RoundedRectangle值的cornerRadius来实现的方法:

struct ContentView: View {
    
    @State var action = false
    
    var body: some View {
        
        ZStack
        {
            RoundedRectangle(cornerRadius: action ? 0 : 75)
                .fill(action ? Color.red : .blue)
                .frame(width: 150, height: 150, alignment: .center)
                .animation(.easeInOut)
            
            VStack
            {
                Spacer()
                
                Button("transform") {
                    action.toggle()
                }
                .font(Font.largeTitle)
                .padding()
            }
        }
    }
}

demo in simulator

答案 2 :(得分:1)

随着iOS14的发布,您可以使用matchedGeometryEffect()。如果您使用的是iOS14,我建议您使用这种方法。

https://www.hackingwithswift.com/quick-start/swiftui/how-to-synchronize-animations-from-one-view-to-another-with-matchedgeometryeffect

https://developer.apple.com/documentation/swiftui/view/matchedgeometryeffect(id:in:properties:anchor:issource:)

因此,在您的解决方案中,如果您在按钮代码中将action.toggle()替换为withAnimation{self.action.toggle()},它将进行动画处理。

Button("transform") { 
    withAnimation{self.action.toggle()} 
    }
.font(Font.largeTitle).padding()

此解决方案适用于我的模拟器(Xcode 12.1,iPhone 11 iOS 14.1):

import SwiftUI

struct ContentView: View {

@State var action: Bool = false
@Namespace var transition

var body: some View {
    ZStack {
        Group {
            if action {
                Circle()
                    .fill(Color.blue).frame(width: 150, height: 150, alignment: .center)
                    .matchedGeometryEffect(id: "shape", in: transition)
            } else {
                Rectangle()
                    .fill(Color.red).frame(width: 150, height: 150, alignment: .center)
                    .matchedGeometryEffect(id: "shape", in: transition)
            }
        }
        .animation(.easeInOut)
        
        VStack {
            Spacer()
            
            Button("transform") { withAnimation{self.action.toggle()} }.font(Font.largeTitle).padding()
                
        }
    }
}
}

matchedGeometryEffect()不想为不同的形状(包括cornerRadius)或颜色设置动画,不知道这是否是会在将来的补丁中修复的错误,或者只是需要常规解决的功能动画。在我使用matchGeometryEffect()的情况下,上下调整大小似乎很棒,如以下代码所示:

import SwiftUI

struct ContentView: View {
@State private var animate: Bool = false
@Namespace private var transition

    var body: some View {
        VStack {
            if animate {
                RoundedRectangle(cornerRadius: 75.0)
                    .matchedGeometryEffect(id: "shape", in: transition)
                    .frame(width: 250, height: 250, alignment: .center)
                    .foregroundColor(Color.blue)
                    .animation(.easeInOut)
                    .onTapGesture {
                        animate.toggle()
                    }
         
            } else {
                // Circle
                RoundedRectangle(cornerRadius: 75.0)
                    .matchedGeometryEffect(id: "shape", in: transition)
                    .frame(width: 150, height: 150, alignment: .center)
                    .foregroundColor(Color.red)
                    .animation(.easeInOut)
                    .onTapGesture {
                        animate.toggle()
                    }
            }
        }
    }
}