swift - 将一个 SwiftUI View 从另一个下方滑出

标签 swift swiftui

我正在尝试使用 SwiftUI 构建动画。

Start: [ A ][ B ][ D ]
End:   [ A ][ B ][    C    ][ D ]

动画的关键元素是:

  • C 应该从 B 下方滑出(不是从零宽度展开)
  • 所有 View 的宽度由 subview 定义,并且是未知的
  • 所有 subview 的宽度在动画期间或之后不应该改变(因此,总 View 宽度在结束状态时更大)

我很难用 SwiftUI 满足所有这些要求,但过去已经能够通过自动布局实现类似的效果。

我的第一次尝试是使用带有 layoutPriorities 的 HStack 进行转换。这并没有真正接近,因为它会影响动画期间 C 的宽度。

我的第二次尝试是保留 HStack,但使用带有不对称移动动画的过渡。这非常接近,但是动画中 B 和 C 的移动并没有给出 C 在 B 正下方的效果。

我最近的尝试是放弃对两个动画 View 的 HStack 的依赖,而是使用 ZStack。通过此设置,我可以通过结合使用 offsetpadding 使我的动画完美。 但是,只有使 B 和 C 的帧大小已知值,我才能正确处理。

有没有人对如何不需要 B 和 C 的固定帧大小有任何想法?

最佳答案

自从我最初回答这个问题以来,我一直在研究 GeometryReader、View Preferences 和 Anchor Preferences。我已经收集了一个详细的解释,进一步阐述。您可以在以下位置阅读:https://swiftui-lab.com/communicating-with-the-view-tree-part-1/

enter image description here

一旦将 CCCCCCCC View 几何体放入 textRect 变量中,剩下的就很简单了。您只需使用 .offset(x:) 修饰符和 clipped()。

import SwiftUI

struct RectPreferenceKey: PreferenceKey {
    static var defaultValue = CGRect()

    static func reduce(value: inout CGRect, nextValue: () -> CGRect) {
        value = nextValue()
    }

    typealias Value = CGRect
}

struct ContentView : View {
    @State private var textRect = CGRect()
    @State private var slideOut = false

    var body: some View {

        return VStack {
            HStack(spacing: 0) {
                Text("AAAAAA")
                    .font(.largeTitle)
                    .background(Color.yellow)
                    .zIndex(4)


                Text("BBBB")
                    .font(.largeTitle)
                    .background(Color.red)
                    .zIndex(3)

                Text("I am a very long text")
                    .zIndex(2)
                    .font(.largeTitle)
                    .background(GeometryGetter())
                    .background(Color.green)
                    .offset(x: slideOut ? 0.0 : -textRect.width)
                    .clipped()
                    .onPreferenceChange(RectPreferenceKey.self) { self.textRect = $0 }

                Text("DDDDDDDDDDDDD").font(.largeTitle)
                    .zIndex(1)
                    .background(Color.blue)
                    .offset(x: slideOut ? 0.0 : -textRect.width)

            }.offset(x: slideOut ? 0.0 : +textRect.width / 2.0)

            Divider()
            Button(action: {
                withAnimation(.basic(duration: 1.5)) {
                    self.slideOut.toggle()
                }
            }, label: {
                Text("Animate Me")
            })
        }

    }
}

struct GeometryGetter: View {
    var body: some View {
        GeometryReader { geometry in
            return Rectangle()
                .fill(Color.clear)
                .preference(key: RectPreferenceKey.self, value:geometry.frame(in: .global))
        }
    }
}

关于swift - 将一个 SwiftUI View 从另一个下方滑出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56633604/

相关文章:

swift - 特定功能同时在所有 View 上运行。我怎样才能让它一个接一个地运行?

ios - Swift - tableView 行高仅在滚动或切换展开/折叠后更新

ios - 如何拍照并将其传递给另一个 View Controller

ios - 如何在 Swift 中设置委托(delegate)

ios - SwiftUI - 如何防止工作表中的键盘推高我的主 UI

SwiftUI ForEach 刷新使 View 弹出

ios - 使用 Swift + FMDB 打开和关闭数据库

Swift 2.0 - 无法写入文本文件 (OS X)

swift - 如何在主体 View 中设置对齐方式?

ios - 如何删除 SwiftUI NavigationView 中的默认导航栏空间