将一个 SwiftUI 视图从另一个视图下方滑出 [英] Sliding one SwiftUI view out from underneath another

查看:28
本文介绍了将一个 SwiftUI 视图从另一个视图下方滑出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

开始:[ A ][ B ][ D ]结束:[A][B][C][D]

动画的关键元素是:

  • C 应该看起来从 B 下方滑出(而不是从零宽度扩展)
  • 所有视图的宽度由子视图定义,未知
  • 在动画期间或之后,所有子视图的宽度不应改变(因此,在结束状态时,总视图宽度更大)

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

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

我的第二次尝试是保留 HStack,但使用具有不对称移动动画的过渡.这非常接近,但动画期间 B 和 C 的移动并没有产生 C 直接在 B 下方的效果.

我最近的尝试是放弃对两个动画视图的 HStack 依赖,并使用 ZStack 代替.通过这种设置,我可以通过结合使用 offsetpadding 来使我的动画完美.然而,只有让 B 和 C 的帧大小已知值,我才能做到正确.

有没有人对如何在不要求 B 和 C 的固定帧大小的情况下实现这种效果有任何想法?

解决方案

自从我最初回答这个问题以来,我一直在研究 GeometryReader、View Preferences 和 Anchor Preferences.我已经收集了一个详细的解释,进一步阐述.您可以在以下位置阅读:

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

import SwiftUIstruct RectPreferenceKey:PreferenceKey {静态变量 defaultValue = CGRect()static func reduce(value: inout CGRect, nextValue: () -> CGRect) {值 = 下一个值()}类型别名值 = CGRect}结构内容视图:查看{@State 私有 var textRect = CGRect()@State 私有 var slideOut = falsevar主体:一些视图{返回 VStack {HStack(间距:0){文本(AAAAAA").font(.largeTitle).background(颜色.黄色).zIndex(4)文本(BBBB").font(.largeTitle).background(颜色.红色).zIndex(3)Text("我是一个很长的文字").zIndex(2).font(.largeTitle).background(GeometryGetter()).background(颜色.绿色).offset(x: slideOut ? 0.0 : -textRect.width).剪辑().onPreferenceChange(RectPreferenceKey.self) { self.textRect = $0 }文本(DDDDDDDDDDDDD").字体(.largeTitle).z索引(1).background(Color.blue).offset(x: slideOut ? 0.0 : -textRect.width)}.offset(x: slideOut ? 0.0 : +textRect.width/2.0)分隔线()按钮(动作:{withAnimation(.basic(duration: 1.5)) {self.slideOut.toggle()}}, 标签: {文本(动画我")})}}}struct GeometryGetter:查看{var主体:一些视图{GeometryReader { 几何输入返回矩形().fill(颜色.清除).preference(key: RectPreferenceKey.self, value:geometry.frame(in: .global))}}}

I'm attempting to construct an animation using SwiftUI.

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

The key elements of the animation are:

  • C should appear to slide out from underneath B (not expand from zero width)
  • The widths of all views are defined by subviews, and are not known
  • The widths of all subviews should not change during or after the animation (so, total view width is larger when in the end state)

I'm having a very difficult time satisfying all of these requirements with SwiftUI, but have been able to achieve similar affects with auto-layout in the past.

My first attempt was a transition using an HStack with layoutPriorities. This didn't really come close, because it affects the width of C during the animation.

My second attempt was to keep the HStack, but use a transition with asymmetrical move animations. This came really close, but the movement of B and C during the animation does not give the effect that C was directly underneath B.

My latest attempt was to scrap relying on an HStack for the two animating views, and use a ZStack instead. With this setup, I can get my animation perfect by using a combination of offset and padding. However, I can only get it right if I make the frame sizes of B and C known values.

Does anyone have any ideas on how to achieve this effect without requiring fixed frame sizes for B and C?

解决方案

Since I originally replied to this question, I have been investigating GeometryReader, View Preferences and Anchor Preferences. I have assembled a detailed explanation that elaborates further. You can read it at: https://swiftui-lab.com/communicating-with-the-view-tree-part-1/

Once you get the CCCCCCCC view geometry into the textRect variable, the rest is easy. You simply use the .offset(x:) modifier and 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))
        }
    }
}

这篇关于将一个 SwiftUI 视图从另一个视图下方滑出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆