简体   繁体   English

SwiftUI 中的动画 ViewBuilder 内容大小变化

[英]Animate ViewBuilder content size change in SwiftUI

I am wondering how I can animate the content size of a ViewBuilder view.我想知道如何为 ViewBuilder 视图的内容大小设置动画。 I have this:我有这个:

struct CardView<Content>: View where Content: View {
    
    private let content: Content
    
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
    
    var body: some View {
        VStack(spacing: 0) {
            content
                .padding(16)
        }
        .background(.white)
        .cornerRadius(14)
        .shadow(color: .black.opacity(0.07), radius: 12, x: 0, y: 2)
    }
}

I would like to animate any size changes to content , but I can't find a nice way of doing this.我想为content的任何大小更改设置动画,但我找不到这样做的好方法。 I found two ways that work:我发现了两种可行的方法:

  • Using animation(.linear) in CardView works, but is deprecated and discouraged since I have no value to attach the animation to.CardView中使用animation(.linear)是可行的,但由于我没有附加动画的value ,因此不推荐使用和不鼓励使用。
  • Using withAnimation inside content when changing the content works, too, but I would like to encapsulate this behaviour in CardView .在更改content时在内容中使用withAnimation也可以,但我想将此行为封装在CardView中。 CardView is heavily reused and doing it in content is easy to forget and also not where this behaviour belongs in my opinion. CardView被大量重用,并且在content中执行它很容易忘记,而且在我看来也不属于这种行为。

I also tried using GeometryReader but could not find a good way of doing it.我也尝试过使用GeometryReader ,但找不到一个好的方法。

Here is an approach for you:这是适合您的方法:

You may take look at this link as well:你也可以看看这个链接:

How to replace deprecated .animation() in SwiftUI?如何在 SwiftUI 中替换已弃用的 .animation()?


struct ContentView: View {

    @State private var cardSize: CGSize = CGSize(width: 150, height: 200)
    
    var body: some View {
        
        VStack {

            CardView(content: {
                
                Color.red
                    .overlay(Image(systemName: "dollarsign.circle").resizable().scaledToFit().padding())
                    .onTapGesture {
                        cardSize = CGSize(width: cardSize.width + 50, height: cardSize.height + 50)
                    }
                
            }, cardSize: cardSize)
  
        }

    }
}

struct CardView<Content>: View where Content: View {
    
    let content: Content
    let cardSize: CGSize
    
    init(@ViewBuilder content: () -> Content, cardSize: CGSize) {
        self.content = content()
        self.cardSize = cardSize
    }

    var body: some View {
        
        content
            .frame(width: cardSize.width, height: cardSize.height)
            .cornerRadius(14)
            .padding(16)
            .background(.white)
            .shadow(color: .black.opacity(0.07), radius: 12, x: 0, y: 2)
            .animation(.easeInOut, value: cardSize)
    }
}

You might find this useful.您可能会发现这很有用。

It uses a looping animation and a user gesture for add size and resting.它使用循环动画​​和用户手势来增加尺寸和休息。

struct PilotTestPage: View {
    
    @State private var cardSize = CGSize(width: 150, height: 200)

    var body: some View {
        return ZStack {
            Color.yellow
                        
            CardView() {
                Color.clear
                    .overlay(
                        Image(systemName: "dollarsign.circle")
                            .resizable()
                            .scaledToFit()
                            .padding()
                    )
            }
            .frame(
                width: cardSize.width
                ,height: cardSize.height
            )
            .onTapGesture {
                withAnimation {
                    cardSize = CGSize(
                        width: cardSize.width + 50
                        ,height: cardSize.height + 50
                    )
                }
            }
            
            RoundedRectangle(cornerRadius: 12, style: .continuous)
                .fill(.red)
                .frame(
                    width: 200
                    ,height: 44
                )
                .offset(y: -300)
                .onTapGesture {
                    withAnimation {
                        cardSize = CGSize(
                            width: 150
                            ,height: 200
                        )
                    }
                }

        }
        .ignoresSafeArea()
    }
    
    struct CardView<Content>: View where Content: View {
        let content: Content
        
        init(
            @ViewBuilder content: () -> Content
        ) {
            self.content = content()
        }
        
        @State private var isAtStart = true
        
        var body: some View {
            ZStack {
                content
                    .background(
                        RoundedRectangle(cornerRadius: 12)
                            .fill(.white)
                            .shadow(
                                color: .black.opacity(0.25)
                                ,radius: 12
                                ,x: 0
                                ,y: 2
                            )
                    )
            }
            .scaleEffect(isAtStart ? 0.9 : 1.0)
            .rotationEffect(.degrees(isAtStart ? -2 : 2))
            .onAppear {
                withAnimation(
                    .easeInOut(duration: 1)
                    .repeatForever()
                ) {
                    self.isAtStart.toggle()
                }
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM