代码之家  ›  专栏  ›  技术社区  ›  mikko

SwiftUI如何在不同堆栈中实现同一视图的平滑动画

  •  0
  • mikko  · 技术社区  · 5 月前

    我设置了一个视图,当状态不同时,它位于HStack或VStack中。但当我设置动画时,它不符合我的期望。我希望右边的景色能平稳地上下移动。(位置1平稳移动到位置2)

    struct ContentView: View {
        @State var show = false
        var body: some View {
            VStack {
                VStack {
                    HStack {
                        Text("test spacer")
                            .contentShape(Rectangle())
                            .frame(minWidth: 0, maxWidth: .infinity)
                            .background(Color.red)
                        if !show {
                            Text("xxxxxx")
                                .background(Color.blue) // position1
                        }
                    }
                    if show {
                        HStack {
                            Spacer()
                            Text("xxxxxx")
                                .background(Color.blue) // position2
                        }
                    }
                }
                .background(Color.yellow)
                .animation(.easeInOut(duration: 1), value: show)
    
                Button("button") {
                    show.toggle()
                }
            }
            .padding()
        }
    }
    

    enter image description here

    我使用了Sweeper的答案,并使用matchedGeometryEffect来解决这个问题。

    但我试图将文本更改为文本字段,并更改了触发动画聚焦的时间。当键盘弹出时,动画执行不顺畅。

    如何使视图中的元素平滑滑动?

    struct ContentView: View {
        @State private var text = ""
        @Namespace var ns
        @FocusState private var isFocused: Bool
        
        var body: some View {
            ZStack {
                Color.clear
                    .contentShape(Rectangle())
                    .onTapGesture {
                        isFocused = false
                    }
                VStack {
                    HStack {
                        TextField("placeholder", text: $text, axis: .vertical)
                            .focused($isFocused)
                            .frame(minHeight: 28)
                            .background(Color.red)
                        if !isFocused {
                            Text("xxxxxx")
                                .background(Color.blue)
                                .matchedGeometryEffect(id: "xxxxxx", in: ns)
                        }
                    }
                    if isFocused {
                        HStack {
                            Spacer()
                            Text("xxxxxx")
                                .background(Color.blue)
                                .matchedGeometryEffect(id: "xxxxxx", in: ns)
                        }
                    }
                }
                .background(Color.yellow)
                .animation(.easeInOut(duration: 1), value: isFocused)
            }
            .padding()
        }
    }
    

    enter image description here

    1 回复  |  直到 5 月前
        1
  •  2
  •   Sweeper    5 月前

    使用a matchedGeometryEffect 将两个视图的大小和位置“链接”在一起。

    struct ContentView: View {
        @State var show = false
        @Namespace var ns
        var body: some View {
            VStack {
                VStack {
                    HStack {
                        Text("test spacer")
                            .contentShape(Rectangle())
                            .frame(minWidth: 0, maxWidth: .infinity)
                            .background(Color.red)
                        if !show {
                            Text("xxxxxx")
                                .background(Color.blue)
                                // here
                                .matchedGeometryEffect(id: "xxxxxx", in: ns)
                        }
                    }
                    if show {
                        HStack {
                            Spacer()
                            Text("xxxxxx")
                                .background(Color.blue)
                                // and here
                                .matchedGeometryEffect(id: "xxxxxx", in: ns)
                        }
                    }
                }
                .background(Color.yellow)
                .animation(.easeInOut(duration: 1), value: show)
    
                Button("button") {
                    show.toggle()
                }
            }
            .padding()
        }
    }