SwiftUI 中的动画文本循环
Posted
技术标签:
【中文标题】SwiftUI 中的动画文本循环【英文标题】:Animating text cycle in SwiftUI 【发布时间】:2021-10-30 12:07:48 【问题描述】:几天来,我一直在努力使我的应用程序具有某种“启动画面”,但无法完全按照我需要的方式实现它。
第一次打开应用程序时,我希望它通过不同的指令显示一个循环。例如,页面应该先说“欢迎”,然后在 2 秒后更改为“感谢您下载此应用”,然后在 2 秒后淡入我的主要内容视图。之后,我在 UI 按钮上方的某些位置出现了动画气泡,告诉用户如果单击它们会发生什么。
到目前为止,我的工作正常。为此,我有多个视图的 ZStack,经过延迟,旧视图的不透明度设置为 100%,因此出现了下面的视图。可以在下面找到主要代码,为简单起见删除了重复的功能。主屏幕视图是带有上述 UI 按钮的主视图。 setupscreen 视图包含给用户的文本说明。
代码如下:
ZStack
homescreen()
setupScreen6()
.onAppear(perform: animateSplash6)
.opacity(settings.endSplash6 ? 0 : 1)
.opacity(settings.animate6 ? 1 : 0)
setupScreen5()
.onAppear(perform: animateSplash5)
.opacity(settings.endSplash5 ? 0 : 1)
.opacity(settings.animate5 ? 1 : 0)
setupScreen4()
.onAppear(perform: animateSplash4)
.opacity(settings.endSplash4 ? 0 : 1)
setupScreen3()
.onAppear(perform: animateSplash3)
.opacity(settings.endSplash3 ? 0 : 1)
setupScreen2()
.onAppear(perform: animateSplash2)
.opacity(settings.endSplash2 ? 0 : 1)
setupScreen()
.onAppear(perform: animateSplash)
//.onAppear(perform: endanimateSplash)
.opacity(settings.endSplash ? 0 : 1)
.environmentObject(settings)
func animateSplash()
DispatchQueue.main.asyncAfter(deadline: .now() + 2)
withAnimation(Animation.easeOut(duration: 1))
settings.animate.toggle()
withAnimation(Animation.easeOut(duration: 1))
settings.endSplash.toggle()
但是,在应用程序中,我有一个设置,用户可以单击以“重置”应用程序。当用户单击该按钮时,我将选项卡切换回上面驻留的选项卡,然后将所有 animateSplash 和 endSplash 变量重置为 false。这导致 setupscreen() 视图被显示(如预期的那样),但它永远不会再次淡出以在 ZStack 中显示其下方的视图。
我意识到我的实现方式是错误的,必须有一个更简单的方法。
任何帮助将不胜感激!
【问题讨论】:
【参考方案1】:我无法完全重现您的情况,但根据我从您的代码中得出的信息,以下代码可能会有所帮助。
您正在使用带有不透明度的 ZStack。为什么不使用带有枚举的开关,该枚举包含所有视图并随着时间的推移在它们之间切换?
试试下面的示例代码:
struct TestView: View
enum ViewState: CaseIterable
case splash1
case splash2
case splash3
case home
mutating func next()
let allCases = type(of: self).allCases
self = allCases[(allCases.firstIndex(of: self)! + 1) % allCases.count]
@State var currentSplashView: ViewState = .splash1
var body: some View
VStack
switch self.currentSplashView
case .splash1:
Text("1")
// setupScreen1()
case .splash2:
Text("2")
// setupScreen2()
case .splash3:
Text("3")
// setupScreen3()
case .home:
Text("home")
// homescreen()
.onAppear
Timer.scheduledTimer(withTimeInterval: 2, repeats: true) timer in
self.currentSplashView.next()
if self.currentSplashView == .home
timer.invalidate()
枚举包含要显示的当前视图。 .onAppear 管理变量 currentSplashView 中的状态变化。 VStack 中的切换最终在不同视图之间切换。一旦到达主页视图,计时器就会失效并停留在主页视图上。 (您可以在 Timer 中更改经过的时间)
重新加载整个视图后,计时器会再次启动,并且所有视图都会再次单独显示。当您“重置”应用程序时,您可以销毁并实例化此结构。
要为视图之间的过渡设置动画,请将“self.currentSplashView.next()”包装在 withAnimation 块中。
已添加
由于您想要一些其他结构并将飞溅与家庭分离,请为您的应用尝试以下结构:
@main
struct YourAppName: App
var body: some Scene
WindowGroup
ViewRouter(currentView: .splash) // <---- you can determine if you want to show splash or go to home immediately
enum Views
case home
case settings
case splash
struct ViewRouter: View
@State var currentView: Views
var body: some View
VStack
switch self.currentView
case .home:
HomeView(currentView: self.$currentView)
case .settings:
Settings(currentView: self.$currentView) // <---- in this view reset the app
case .splash:
SplashView(currentView: self.$currentView) // <--- this is the view I created earlier. Remove the .home case from that view, we move home 1 level up in the hierarchy (watch the changes closely)
struct HomeView: View
@Binding var currentView: Views
var body: some View
VStack
Text("This is your home").font(.largeTitle)
Button(action:
withAnimation
self.currentView = .settings
)
Text("Go to settings")
struct Settings: View
@Binding var currentView: Views
var body: some View
VStack
Text("This is your settings").font(.largeTitle)
Button(action:
withAnimation
self.currentView = .splash
)
Text("Reset app") // go to start from app and star with splash
struct SplashView: View
enum ViewState: CaseIterable
case splash1
case splash2
case splash3
mutating func next()
let allCases = type(of: self).allCases
self = allCases[(allCases.firstIndex(of: self)! + 1) % allCases.count]
@Binding var currentView: Views
@State var currentSplashView: ViewState = .splash1
var body: some View
VStack
switch self.currentSplashView
case .splash1:
Text("Splash 1").font(.largeTitle)
// setupScreen1()
case .splash2:
Text("Splash 2").font(.largeTitle)
// setupScreen2()
case .splash3:
Text("Splash 3").font(.largeTitle)
// setupScreen3()
.onAppear
Timer.scheduledTimer(withTimeInterval: 2, repeats: true) timer in
if self.currentSplashView == .splash3
timer.invalidate()
withAnimation
self.currentView = .home // <---- when the last splash is reached, set the hierarchy to home
withAnimation
self.currentSplashView.next()
将来,如果您有任何其他视图想要在没有 NavigationView 的情况下访问,只需将它们添加到枚举和主层次结构的开关中即可。
【讨论】:
谢谢,我让它工作了,但我不知道如何从另一个视图重置它。当您说要销毁和实例化时,您是什么意思?对于上下文,用户导航离开主视图,并且用户在其他视图中重置应用程序。主视图是 tab1,第二个视图是 tab2。 我不确定我是否完全理解了您正在实现的视图层次结构,但据我了解,您打开应用程序,显示 TestView(我创建的那个)。到达主屏幕视图后,用户可以在主视图中导航到另一个选项卡(设置)。在那里,用户可以重置应用程序。它是否正确?通过销毁和实例化,我的意思是当 TestView 留给 Tab2View 并且您再次将用户重定向到 TestView 时,整个过程和启动画面会再次显示。请参阅我的更新答案。 @Carl Bryers 如果这给了你预期的结果,你能给我一个更新吗? 嗨@Bjorn。抱歉,我不在,所以还没有机会实施您的更新。一旦我试一试,我会在下周通知你。非常感谢。 @CarlBryers And?以上是关于SwiftUI 中的动画文本循环的主要内容,如果未能解决你的问题,请参考以下文章