如何在 SwiftUI 中以动画方式将形状从一个网格移动到另一个网格

问题描述 投票:0回答:1

我正在尝试为 LazyVGrid 视图之间的移动设置动画。我有两个小圆圈形状(即“点”)数组,排列在两个不同的 LazyVGrid 视图中(一个在左侧,一个在右侧)。当用户点击按钮时,我希望对每个点进行动画处理,使其从原始位置滑入第三个 LazyVGrid 中的位置,并将所有这些点组合起来。

就好像你有自主但听话的弹珠,在桌子上的两个地方排列成网格,当你命令它们这样做时,它们滚动在一起,在桌子中央形成一个新的网格(但更简单——没有碰撞)等)。

经过多次尝试,下面的代码本质上是我迄今为止最好的尝试(尽管脱离上下文看起来很愚蠢)。 “填充”按钮填充原始点,但它仍然只是动画,就像原始点网格淡出和新点淡入,然后随着第一组新点向上滑动,更多新点淡入。

关于实施此类行为有什么建议吗?

import SwiftUI

struct CT2: View {
    @StateObject private var viewModel = DotManager()

    var body: some View {
        VStack {
            ZStack {
                HStack {

                    LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 20), count: 5), content: {
                        ForEach(viewModel.blueDots) { dot in
                            dot.dot
                                .frame(width: 20, height: 20)
                                .foregroundColor(.blue)
                        }
                    })

                    LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 20), count: 5), content: {
                        ForEach(viewModel.redDots) { dot in
                            dot.dot
                                .frame(width: 20, height: 20)
                                .foregroundColor(.red)
                        }
                    })
                }

                LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 20), count: 5), content: {
                    ForEach(viewModel.combinedDots) { dot in
                        dot.dot
                            .frame(width: 20, height: 20)
                            .foregroundColor(.purple)
                    }
                })
            }
        }

        HStack {
            Button(action: {
                viewModel.addDots()
            }, label: {
                Text("Fill")
            })
            .padding()
            .foregroundColor(.primary)
            .background(Color(UIColor.systemFill))

            Button(action: {
                viewModel.combineDots()
            }, label: {
                Text("Combine dots")
            })
            .padding()
            .foregroundColor(.primary)
            .background(Color(UIColor.systemFill))
        }
    }
}

enum DotType {
    case blue, red, combined
}

class Dot: Identifiable {
    let id = UUID()
    let dot = Circle()
}

class DotManager: ObservableObject {
    @Published var blueDots: [Dot] = []
    @Published var redDots: [Dot] = []
    @Published var combinedDots: [Dot] = []

    func addDots() {
        for _ in 0..<17 {
            let newDot = Dot()
            blueDots.append(newDot)
        }

        for _ in 17..<28 {
            let newDot = Dot()
            redDots.append(newDot)
        }
    }

    func combineDots() {
        for _ in 0..<blueDots.count {
            withAnimation(.easeInOut) {
                if let movedDot = blueDots.first {
                    blueDots.removeFirst()
                    combinedDots.append(movedDot)
                }
            }
        }
        for _ in 0..<redDots.count {
            withAnimation(.easeInOut) {
                if let movedDot = redDots.first {
                    redDots.removeFirst()
                    combinedDots.append(movedDot)
                }
            }
        }
    }
}
ios swift xcode animation swiftui
1个回答
0
投票

要使视图达到所需的效果,您可以使用 @Namespace 和 .matchedGeometryEffect

修改后的代码如下。

struct CT2: View {
    @StateObject private var viewModel = DotManager()
    @Namespace private var namespace
    ... same ...
}

并在每个 ForEach 循环内(这里有三个 ForEach),添加 .matchedGeometryEffect。

ForEach(viewModel.blueDots) { dot in
    dot.dot
        .frame(width: 20, height: 20)
        .foregroundColor(.blue)
        .matchedGeometryEffect(id: dot.id, in: namespace)
}
© www.soinside.com 2019 - 2024. All rights reserved.