Ios 根据保存的状态自动在SwiftUI中激活动画

Ios 根据保存的状态自动在SwiftUI中激活动画,ios,swiftui,ios-animations,Ios,Swiftui,Ios Animations,我正在尝试编写一个显示3个按钮的视图,但无法在加载时启动动画 点击按钮时,我希望它设置动画,直到: 它被第二次点击 点击3个按钮中的另一个 我已经使用@Environment对象来存储运行状态,使代码正常工作。它可以在3个按钮之间切换: 代码如下: struct ContentView : View { @EnvironmentObject var model : ModelClockToggle var body: some View { VStack

我正在尝试编写一个显示3个按钮的视图,但无法在加载时启动动画

点击按钮时,我希望它设置动画,直到:

  • 它被第二次点击
  • 点击3个按钮中的另一个
  • 我已经使用@Environment对象来存储运行状态,使代码正常工作。它可以在3个按钮之间切换:

    代码如下:

    struct ContentView : View {
    
        @EnvironmentObject var model : ModelClockToggle
    
    
    
        var body: some View {
    
            VStack {
    
                ForEach(0...2) { timerButton in
    
                 ActivityBreak(myId: timerButton)
                    .padding()
    
                }
            }
        }
    }
    
    
    import SwiftUI
    
    struct ActivityBreak : View {
        var myId: Int
        @EnvironmentObject var model : ModelClockToggle
    
        let anim1 = Animation.basic(duration: 1.0, curve: .easeInOut).repeatCount(Int.max)
        let noAni = Animation.basic(duration: 0.2, curve: .easeInOut).repeatCount(0)
    
        var body: some View {
    
                return Circle()
                    .foregroundColor(.red)
    
                    .scaleEffect(self.model.amIRunning(clock: self.myId) ? 1.0 : 0.6)
                    .animation( self.model.amIRunning(clock: self.myId) ? anim1 : noAni )
                    .tapAction {
    
                        self.model.toggle(clock: self.myId)
    
    
    
                }
    
        }
    }
    
    为完整起见,模型为:

    import Foundation
    import SwiftUI
    import Combine
    
    
    class ModelClockToggle: BindableObject  {
    
        let didChange = PassthroughSubject<ModelClockToggle, Never>()
    
    
        private var clocksOn: [Bool]  = [false,false,false]
    
        init() {
            clocksOn = []
            clocksOn.append(UserDefaults.standard.bool(forKey: "toggle1"))
            clocksOn.append(UserDefaults.standard.bool(forKey: "toggle2"))
            clocksOn.append(UserDefaults.standard.bool(forKey: "toggle3"))
            debugPrint(clocksOn)
    
        }
    
    
    
        func toggle(clock: Int) {
            debugPrint(#function)
    
    
            if clocksOn[clock] {
                clocksOn[clock].toggle()
    
    
    
    
    
            } else {
                clocksOn  = [false,false,false]
                clocksOn[clock].toggle()
            }
            saveState()
            didChange.send(self)
        }
    
    
        func amIRunning(clock: Int) -> Bool {
            debugPrint(clocksOn)
    
            return clocksOn[clock]
    
    
        }
    
        private func saveState() {
    
            UserDefaults.standard.set(clocksOn[0], forKey: "toggle1")
            UserDefaults.standard.set(clocksOn[1], forKey: "toggle2")
            UserDefaults.standard.set(clocksOn[2], forKey: "toggle3")
    
    
        }
    }
    
    <代码>导入基础 导入快捷键 进口联合收割机 类ModelClockToggle:BindableObject{ 让didChange=PassthroughSubject() 私有变量clocksOn:[Bool]=[false,false,false] init(){ clocksOn=[] append(UserDefaults.standard.bool(forKey:“toggle1”)) append(UserDefaults.standard.bool(forKey:“toggle2”)) append(UserDefaults.standard.bool(forKey:“toggle3”)) 调试打印(clocksOn) } 功能切换(时钟:Int){ debugPrint(#函数) 如果是clocksOn[时钟]{ clocksOn[clock].toggle() }否则{ clocksOn=[假,假,假] clocksOn[clock].toggle() } saveState() 发送(自我) } func amIRunning(时钟:Int)->Bool{ 调试打印(clocksOn) 返回时钟 } 私有func存储状态(){ UserDefaults.standard.set(clocksOn[0],forKey:“toggle1”) UserDefaults.standard.set(clocksOn[1],forKey:“toggle2”) UserDefaults.standard.set(clocksOn[2],forKey:“toggle3”) } } 如何根据传入视图的@Environment对象在加载时开始重复动画?现在SWIFTUI似乎只考虑视图加载后的状态变化。< /P> 我尝试添加一个.onAppear修改器,但这意味着我必须使用不同的动画师,这有非常奇怪的效果


    感谢您的帮助。

    在您的示例中,您使用的是隐式动画。这些动画将查找任何可设置动画的参数的更改,如大小、位置、不透明度、颜色等。当SwiftUI检测到任何更改时,它将设置动画

    在您的特定情况下,圆在未激活时通常缩放为0.6,在激活时缩放为1.0。在“非活动”和“活动”状态之间进行更改,使您的圆圈改变比例,这些更改将在循环中设置动画

    但是,您的问题是,最初以1.0比例加载的圆(因为模型说它处于活动状态)将检测不到更改:它从1.0开始,保持在1.0。所以没有什么可以动画化的

    在您的评论中,您提到了一个解决方案,该解决方案涉及让模型延迟加载圆状态的状态。这样,首先创建视图,然后要求模型加载状态,然后在视图中进行可以设置动画的更改。这是可行的,但是,这有一个问题

    您正在使模型的行为依赖于视图。当它真的应该是相反的时候。假设屏幕上有两个视图实例。根据时间的不同,一个启动良好,但另一个不会

    解决这个问题的方法是确保整个逻辑由视图本身处理。你想要完成的是,你的圆圈总是以0.6的比例创建。然后,检查模型以查看圆圈是否应处于活动状态。如果是,请立即将其更改为1.0。这样可以保证视图的动画效果

    下面是一个可能的解决方案,它使用名为
    booted
    @State
    变量来跟踪此情况。您的圆将始终以0.6的比例创建,但一旦调用onAppear()方法,视图将缩放到1.0(如果活动),从而生成相应的动画

    struct ActivityBreak:视图{
    var myId:Int
    @环境对象变量模型:ModelClockToggle
    @已启动状态私有变量:Bool=false
    //β4
    让anim1=Animation.easeInOut(持续时间:1.0)。repeatCount(Int.max)
    让noAni=Animation.easeInOut(持续时间:0.2)。repeatCount(0)
    //贝塔3
    //让anim1=Animation.basic(持续时间:1.0,曲线:.easeInOut)。repeatCount(Int.max)
    //让noAni=Animation.basic(持续时间:0.2,曲线:.easeInOut)。repeatCount(0)
    var body:一些观点{
    返回圆()
    .foregroundColor(.red)
    .scaleEffect(!booted?0.6:self.model.amIRunning(时钟:self.myId)?1.0:0.6)
    .animation(self.model.amIRunning(clock:self.myId)?anim1:noAni)
    .tapAction{
    self.model.toggle(时钟:self.myId)
    }
    奥纳佩尔先生{
    self.booted=true
    }
    }
    }
    
    在您的示例中,您使用的是隐式动画。这些动画将查找任何可设置动画的参数的更改,如大小、位置、不透明度、颜色等。当SwiftUI检测到任何更改时,它将设置动画

    在您的特定情况下,圆在未激活时通常缩放为0.6,在激活时缩放为1.0。在“非活动”和“活动”状态之间进行更改,使您的圆圈改变比例,这些更改将在循环中设置动画

    但是,您的问题是,最初以1.0比例加载的圆(因为模型说它处于活动状态)将检测不到更改:它从1.0开始,保持在1.0。所以没有什么可以动画化的

    在您的评论中,您提到了一个解决方案,该解决方案涉及让模型延迟加载圆状态的状态。这样,首先创建视图,然后要求模型加载状态,然后在视图中进行可以设置动画的更改。这是可行的,但是,这有一个问题

    您正在使模型的行为依赖于视图。当它真的应该是相反的时候。