如何将动画添加到由 AnyView 制作的自定义导航项之间的过渡?
Posted
技术标签:
【中文标题】如何将动画添加到由 AnyView 制作的自定义导航项之间的过渡?【英文标题】:How do I add Animations to Transitons between custom NavigationItems made from AnyView? 【发布时间】:2019-11-18 06:00:33 【问题描述】:如this article 中所述,有没有一种方法可以将动画添加到由AnyView
制作的自定义NavigationItem
s 之间的过渡?
我喜欢这个NavigationStack
系统的一切,除了不能添加任何动画过渡。
我知道问题与使用AnyView
擦除some View
s 相关,如this answer 中所述,并且Group
似乎是动画自定义视图导航的更好选择。
比起使用 AnyView 和类型擦除,我更喜欢将条件逻辑封装在组视图中。那么你返回的类型是Group,它会正确地动画。
我的想法已经用完了,我需要一些帮助。
我将为上下文添加我的代码。
场景代理:
class SceneDelegate: UIResponder, UIWindowSceneDelegate
...
let contentView = ContentView().environmentObject(UserData())
...
内容视图:
struct ContentView: View
@EnvironmentObject var userData: UserData
var body: some View
NavigationHost()
.environmentObject(NavigationStack(
NavigationItem(view: AnyView(
(userData.isSignedIn ? AnyView(HomeView()) : AnyView(RegisterView1(profile: Profile.default)) )
.transition(.move(edge: .trailing))
.animation(Animation.linear(duration: 1))))))
//this animation works for some reason, but only this one.
.environmentObject(UserData())
导航主机:
struct NavigationHost: View
@EnvironmentObject var navigation: NavigationStack
@EnvironmentObject var userData: UserData
var body: some View
ZStack
self.navigation.currentView.view
.environmentObject(self.userData)
.environmentObject(self.navigation)
...
导航栈:
final class NavigationStack: ObservableObject
@Published var viewStack: [NavigationItem] = []
@Published var currentView: NavigationItem
init(_ currentView: NavigationItem )
print("Navigation Stack Initialized")
self.currentView = currentView
func back()
if viewStack.count == 0
return
let last = viewStack.count - 1
currentView = viewStack[last]
viewStack.remove(at: last)
navigation.advance(NavigationItem(AnyView))
func advance(_ view: NavigationItem)
viewStack.append( currentView )
currentView = view
func home()
currentView = NavigationItem( view: AnyView(HomeView()) )
viewStack.removeAll()
struct NavigationItem
var view: AnyView
【问题讨论】:
据我从文章中得出的结论,在所提供的方法中几乎不可能过渡/动画,因为只要根视图中有一些视图层次结构更改,.transition 和 .animation 修饰符就会生效,即.一个视图被另一个视图替换,但在所描述的方法中 ContentView 始终只有一个 NavigationHost 视图。它是更新的,是的,但它是唯一的,没有替代过渡。 哦,我从没想过问题出在场景委托中。感谢您为我详细说明问题。 @Asperi 有没有办法避免这个问题?比如1:每次修改‘NavigationStack’时故意重新加载rootView,并传递一个‘.environmentObject(navigationStack)’给新的rootView,或者2:创建自定义动画?编辑:对不起,如果我似乎对 swiftUI 的工作方式感到困惑。 【参考方案1】:这里是该文章中更新的实体,以采用屏幕之间的简单动画转换。所有这些都是沙哑的,仅用于演示,但希望它可以以某种方式有所帮助。
注意:转场在 Preview 中不起作用(至少在我的 Xcode 11.2/ios 13.2 中),因此在模拟器或真实设备中进行了测试。
它的外观如下:
代码如下:
final class NavigationStack: ObservableObject
enum Direction
case root
case forward
case backward
@Published var viewStack: [NavigationItem] = []
@Published var currentView: NavigationItem
@Published var navigate: Direction = .root
init(_ currentView: NavigationItem )
self.currentView = currentView
func unwind()
if viewStack.count == 0
return
let last = viewStack.count - 1
currentView = viewStack[last]
withAnimation
self.navigate = .backward
viewStack.remove(at: last)
func advance(_ view:NavigationItem)
viewStack.append( currentView)
currentView = view
withAnimation
self.navigate = .forward
func home( )
currentView = NavigationItem(view: AnyView(HomeView()))
viewStack.removeAll()
withAnimation
self.navigate = .root
struct NavigationHost: View
@EnvironmentObject var navigation: NavigationStack
var body: some View
ZStack(alignment: .topLeading)
if navigation.navigate == .root
self.navigation.currentView.view
else if navigation.navigate == .backward
self.navigation.currentView.view
.transition(.move(edge: .leading))
if navigation.navigate == .forward
self.navigation.currentView.view
.transition(.move(edge: .trailing))
【讨论】:
如此有趣的是,动画发生在最后一次渲染 body 和新渲染 view 之间,对吧?就像在最后一次调用 body 时没有呈现两个版本的视图(只显示最后一个视图),对吗? 吹毛求疵的小评论,也许值得使用 push/pop 函数名来模仿UINavigationController
的 API?美好的旧时光?
另外,NavigationItem
的代码是什么?它有什么用?
...hm,这段代码实际上不起作用。从那以后有什么变化吗?使用此代码,您看到的是第 1 页、第 2 页、第 2 页的动画幻灯片?
这是一个糟糕的建议。这基本上破坏了 UINavigationController 内置的任何可访问性支持,以及提供的所有内置工具。就像网络“应用程序”这样做很糟糕一样,SwiftUI 也同样糟糕。以上是关于如何将动画添加到由 AnyView 制作的自定义导航项之间的过渡?的主要内容,如果未能解决你的问题,请参考以下文章
React-Native:将焦点设置到由数组构建的自定义组件
如何将自定义 AxisItem 添加到现有的 PlotWidget?
DragonBones龙骨骨骼中的自定义事件(另有声音动画事件)