SwiftUI Card Flip animation with two views, one of which is embedded within a Stack

安稳与你 提交于 2020-07-10 08:13:45

问题


I'm trying to create a 'card flip' animation between two Views:

  • View 'A' is a CardView within a LazyVGrid
  • View 'B' is a custom modal overlay view

The LazyVGrid and View 'B' are together in a ZStack

Specifically, the ContentView is organized like so:

var body: some View {
    ZStack {
        NavigationView {
            ScrollView {
                LazyVGrid(columns: columns, spacing: 10) {
                    ForEach(model.events, id: \.self) { event in
                        SmallCardView(event: event)
                            .opacity(!showModal || event != modifiableEvent ? 1.0 : 0.0)
                    }
                }
            }
        }
        .brightness(self.showModal ? -0.1 : 0)
        .blur(radius: self.showModal ? 16 : 0)
        
        if self.showModal {
            AddEventView(
                showModal: $showModal,
                existingEvent: modifiableEvent,
            )
            .opacity(showModal ? 1.0 : 0.0)
            .padding(.horizontal, 16)
        }            
    }
}

I came across this SO post, and the answer seems super promising, however the answer doesn't take into account if one of the views is within a Stack / Grid, which is the case for me. So, my question is, how can I adapt the linked solution so that it works as expected if one of the views is indeed embedded within a Stack or a Grid.

Edit: Another thing to note is that the size and position of the Views are different

I tried adding .modifier(FlipEffect(flipped: $showModal, angle: animate3d ? 180 : 0, axis: (x: 0, y: 1))) to both the ZStack and SmallCardView, however neither yielded the expected results.

Thanks!

Edit: For clarity, I want to animate in a card flip style between these two views:


回答1:


This really simple construct should help you understand the necessary structure needed:

There is a specific rotation3DEffect modifier for this purpose.

struct ContentView: View {
    
    // What is the current status
    @State var flipped: Bool = false
    
    // Whats the initial "flip" degree
    @State var degrees: Double = 180.0
    
    @State var width: CGFloat = 200
    @State var height: CGFloat = 300
    
    var body: some View {
        ZStack {
            if flipped {
                //Cart Back
                CardBack(width: self.$width, height: self.$height)
                  
            } else {
                //Cart front

                CardFront(width: self.$width, height: self.$height)
                 
            }
        }//Styling
        .background(Color.gray)
        .cornerRadius(20)
        .rotation3DEffect(.degrees(degrees), axis: (x: 0, y: 1, z: 0))
            
            // When tapped turn it around
        .onTapGesture {
            if self.flipped {
                self.flipped = false
                withAnimation {
                    self.degrees += 180
                    self.width = 200 // add other animated stuff here
                    self.height = 300
                }
            } else {
                self.flipped = true
                withAnimation {
                    self.degrees -= 180
                    self.width = 300 // add other animated stuff here
                    self.height = 500
                }
            }
        }
    }
}

struct CardBack: View {
    
    @Binding var width: CGFloat
    @Binding var height: CGFloat
    
    var body: some View {
        Rectangle().foregroundColor(Color.red).frame(width: self.width, height: self.height).overlay(Text("Back"))
    }
}

struct CardFront: View {
    
    @Binding var width: CGFloat
    @Binding var height: CGFloat
    
    var body: some View {
        Rectangle().foregroundColor(Color.blue).frame(width: self.width, height: self.height).overlay(Text("Front"))
    }
}

This produces the following view:




回答2:


So to explain the answer, I want to explain what you need to achieve. You want your view/editView to animate when it comes in front. That means we need to use transition modifier.

Now Apple's inbuilt transition modifier use many transitions like easeIn, out, etc and that doesn't have this transition so we need to create custom transition to achieve it. Lets do that first.

extension AnyTransition {
    static var rotate: AnyTransition { get {
        AnyTransition.modifier(active: RotateTransition(percent: 0), identity: RotateTransition(percent: 1))
        }
    }
}


struct RotateTransition: GeometryEffect {
    var percent: Double
    
    var animatableData: Double {
        get { percent }
        set { percent = newValue }
    }
    
    func effectValue(size: CGSize) -> ProjectionTransform {

        let rotationPercent = percent
        let a = CGFloat(Angle(degrees: 170 * (1-rotationPercent)).radians)
        
        var transform3d = CATransform3DIdentity;
        transform3d.m34 = -1/max(size.width, size.height)
        
        transform3d = CATransform3DRotate(transform3d, a, 0, 1, 0)
        transform3d = CATransform3DTranslate(transform3d, -size.width/2.0, -size.height/2.0, 0)
        
        let affineTransform1 = ProjectionTransform(CGAffineTransform(translationX: size.width/2.0, y: size.height / 2.0))
        let affineTransform2 = ProjectionTransform(CGAffineTransform(scaleX: CGFloat(percent * 2), y: CGFloat(percent * 2)))
        
        if percent <= 0.5 {
            return ProjectionTransform(transform3d).concatenating(affineTransform2).concatenating(affineTransform1)
        } else {
            return ProjectionTransform(transform3d).concatenating(affineTransform1)
        }
    }
}

Now as we have the custom transition, we need to apply to that view.

so this is the code consider you have a cardView.

cardView(card: cardName)
.transition(.rotate)
.matchedGeometryEffect(id: "popup", in: animation)

The parent view like in your case your view where you are clicking edit

add this

  ParentView() //your view

  .matchedGeometryEffect(id: "popup", in: animation)

You can see output here:

https://imgur.com/pIhBQ72



来源:https://stackoverflow.com/questions/62754980/swiftui-card-flip-animation-with-two-views-one-of-which-is-embedded-within-a-st

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!