I have a use case with a vertical menu with items: link_target1, ..., link_target5.
And I have a container that display a View associated with one of those items.
When I click on one of the menu items, I set a state and display the matching view according to this state.
What i'm trying to achieve, is a dynamic sliding effect on views transition:
If I go from target3 to target1 for example, i'm moving upward. So both views should slide to the bottom.
target3 view disappears by sliding down and target1 view appears by sliding down also.
So it's like all views are vertically stacked and by clicking to the menu I slide from one to another.
I need to be able to control, at the same time, the transition of the discarded and presented view and I'm only able to do that when the user actually click on the menu (cause otherwise, I don't know if we are going up or down).

代码如下所示:

enum ViewScreen: String, CaseIterable, Identifiable {
    case target1
    case target2
    case target3
    case target4
    case target5
        
    var id: String { return self.rawValue }
    var index: Int { ViewScreen.allCases.firstIndex(of: self) ?? 0 }
}

struct ContentView: View {
    @State var target: ViewScreen = .target1 {
        willSet {
            self.previousTarget = target
        }
    }
    @State var previousTarget: ViewScreen = .target1
    var direction: AnyTransition {
        get {
            previousTarget.index <= target.index ? .move(edge: .top) : .move(edge: .bottom)
        }
    }
    
    var body: some View {
        HStack(alignment: .top) {
            SidebarView(target: $target) // It just set target to the clicked link           
            Group {
                switch target {
                    case .target1: View1()
                    case .target2: View2()
                    case .target3: View3()
                    case .target4: View4()
                    case .target5: View5()
                }
            }.transition(AnyTransition.opacity.combined(with: direction))
        }
    }
}

有没有可能进行过渡?欢迎任何帮助.谢谢你们!

推荐答案

你不能在这里使用willSet,因为它实际上没有被调用.您正在向侧边栏传递一个绑定,侧边栏设置绑定的wrappedValue.willSet只有在ContentView中直接执行target = ...时才会被调用.

我会有一个@State来存储过渡来使用.当target改变时,改变这种状态.

@State var target: ViewScreen = .target1
@State var direction: AnyTransition = .asymmetric(insertion: .move(edge: .bottom), removal: .move(edge: .top))

...

.onChange(of: target) { oldValue, newValue in
    if oldValue.index > newValue.index {
        direction = .asymmetric(insertion: .move(edge: .top), removal: .move(edge: .bottom))
    } else {
        direction = .asymmetric(insertion: .move(edge: .bottom), removal: .move(edge: .top))
    }
}

请注意,过渡是不对称的.像.move(edge: .top)这样的过渡在视图显示时从上到下移动,但在视图消失时从下到上移动.国际海事组织,.move(towards:)这个名字比.move(edge:)更合适.

或者,您可以替换

.asymmetric(insertion: .move(edge: .top), removal: .move(edge: .bottom))

.push(from: .top),反之亦然,.push(from: .bottom).与始终将视图移动到同一边的.move不同,push始终将视图移动到同一方向..push也有内置的不透明度,所以你不需要把它和.opacity组合在一起.

现在我们有了从this question开始的情况,当改变方向时,过渡将不会正确更新.控制显示哪个视图的target需要稍晚于direction进行更新.

下面是一个最小的例子:

@State var pickerTarget: ViewScreen = .target1
@State var target: ViewScreen = .target1
@State var direction: AnyTransition = .push(from: .bottom)

var body: some View {
    HStack(alignment: .top) {
        Picker("Pick", selection: $pickerTarget) {
            ForEach(ViewScreen.allCases, id: \.self) {
                Text($0.rawValue)
            }
        }
        Group {
            switch target {
                case .target1: Text("1")
                case .target2: Text("2")
                case .target3: Text("3")
                case .target4: Text("4")
                case .target5: Text("5")
            }
        }
        .transition(direction)
    }
    .onChange(of: pickerTarget) { oldValue, newValue in
        if oldValue.index > newValue.index {
            direction = .push(from: .top)
        } else {
            direction = .push(from: .bottom)
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) {
            withAnimation(.linear(duration: 2)) {
                target = newValue
            }
        }
    }
    
}

Swift相关问答推荐

{Singleton类}类型的值没有成员$showegView'

如何防止自动状态恢复,如果应用程序被启动打开特定的文档?

如何把多个可观测性变成一个完备性?

我在SwiftUI中的动画无法正常工作

我需要一些关于 SwiftUI 动画的帮助

在SwiftUI中使用ForEach循环来显示字典项的键和值在Form视图中

如何使用 Swift 在非本地函数中切换()一个 Bool?

是否有一个带有空初始值设定项的 Swift 类/ struct 的标准协议

我怎样才能缩短(提高效率)这段代码?

签署GoogleSignIn-GoogleSignIn需要开发团队

如何使 SwiftUI Picker 换行文本

Swift - 如何更新多目录中的对象

Subclass.fetchRequest() Swift 3.0,扩展并没有真正帮助 100%?

在 Swift 中返回实例类型

为什么 swift 中的类没有存储类型属性?

swift 3 错误:参数标签 '(_:)' 不匹配任何可用的重载

UIButton 标题左对齐问题 (iOS/Swift)

如何在 Swift 中重写 setter

Void 函数中意外的非 Void 返回值 (Swift 2.0)

为什么枚举具有计算(computed)属性但在 Swift 中没有存储属性?