【问题标题】:Animate ViewBuilder content size change in SwiftUISwiftUI 中的动画 ViewBuilder 内容大小变化
【发布时间】:2022-07-17 22:57:39
【问题描述】:

我想知道如何为 ViewBuilder 视图的内容大小设置动画。我有这个:

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)
    }
}

我想为content 的任何大小更改设置动画,但我找不到这样做的好方法。我发现了两种可行的方法:

  • CardView 中使用animation(.linear) 有效,但由于我没有value 可将动画附加到,因此不推荐使用和劝阻。
  • 在更改内容时在content 中使用withAnimation 也可以,但我想将此行为封装在CardView 中。 CardView 被大量重用,在 content 中执行此操作很容易忘记,而且在我看来也不属于这种行为。

我也尝试过使用GeometryReader,但找不到一个好方法。

【问题讨论】:

  • 请同时添加一个使用此 CardView 的视图演示,包括您想要动画的尺寸,因为不清楚这里到底应该动画什么。
  • 这是可能的,但您已经为 maxWidth = .infinity 制作了框架,所以可以将您的问题更正为高度,或者您应该删除框架修饰符。
  • @Asperi 我会尽快添加一个演示以进行澄清,但我不确定这里有什么不清楚的地方。内容的大小会发生变化,应该通过放大或缩小 CardView 以适应新的大小来进行动画处理。
  • @swiftPunk 我删除了它,你是对的,但我认为一般方法/问题是相同的,无论你想为一维还是二维动画。

标签: ios swift animation swiftui viewbuilder


【解决方案1】:

这里有一个适合你的方法:

你也可以看看这个链接:

How to replace deprecated .animation() in SwiftUI?


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)
    }
}

【讨论】:

  • 谢谢,但我不认为硬编码大小是一种可靠或理想的方法。 ViewBuilder 的整个想法是放入“一些”内容并使其工作。这也不适用于很多场景,比如当用户启用辅助字体大小等时。
  • 我并不是要硬编码大小!这只是一个简单的示例,说明如何为大小更改设置动画,将大小发送到自定义视图构建器的部分可以按照您喜欢的方式进行编码,除了让 SwiftUI 知道当前大小和传入的新大小之外别无他法我做到了。但是,如果您不能使用此答案,则没有问题。
【解决方案2】:

您可能会发现这很有用。

它使用循环动画​​和用户手势来增加尺寸和休息。

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()
                }
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-04
    • 1970-01-01
    相关资源
    最近更新 更多