SwiftUI мигает анимация черным, а не предыдущим цветом - PullRequest
0 голосов
/ 06 мая 2020

У меня есть задача, выполняемая в моей модели представления, и когда она будет завершена, я хочу, чтобы цвет фона на несколько секунд мигал sh красным, а затем оставался красным. Я могу заставить это работать достаточно хорошо, используя приведенный ниже код игровой площадки.

Но проблема в том, что я хочу, чтобы мигание было [красным, черным, красным, черным, красным], а не [оранжевым, красным, оранжевым, красным, оранжевым, красным]. Я чувствую, что это должно быть довольно просто (это было в UIKit!), Но я не могу придумать хороший способ сделать это, чтобы я не добавлял что-то сложное и подверженное ошибкам, например, что-то в моей модели представления, которое переключает цвет назад и вперед быстро.

Предположительно что-то с использованием AnimatableModifier - это способ go, но, учитывая мою неспособность взять пример кода и заставить его делать что-то похожее на то, что я хочу, я явно не понимаю, как это работает ...

import SwiftUI
import PlaygroundSupport

class ViewModel: ObservableObject {
   enum TaskState {
      case started
      case nearlyFinished
      case finished
   }

   private var timer: Timer?
   private var timerTwo: Timer?
   @Published var myTaskState: TaskState

   init() {
      myTaskState = .started
      self.timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false, block: {_ in self.myTaskState = .nearlyFinished})
      self.timerTwo = Timer.scheduledTimer(withTimeInterval: 4.0, repeats: false, block: {_ in self.myTaskState = .finished})
   }
}

struct ContentView: View {
   @ObservedObject var viewModel: ViewModel

   var body: some View {
      ZStack {
         myColor
         Text("Test").foregroundColor(.white)
      }
   }

   private var myColor: some View {
      var repeatingAnimation: Animation {
         Animation
            .linear(duration: 0.5)
            .repeatCount(9, autoreverses: true)
      }

      switch viewModel.myTaskState {
         case .started: return Color.black.animation(.linear(duration: 0.0))
         case .nearlyFinished: return Color.orange.animation(.linear(duration: 0.0))
         case .finished: return Color.red.animation(repeatingAnimation)
      }

   }
}
PlaygroundPage.current.setLiveView(ContentView(viewModel: ViewModel()))

1 Ответ

0 голосов
/ 06 мая 2020

Я разобрался! Спасибо этому видео за объяснение, которое имело для меня смысл.

Возможно, есть более простой способ, но я создал AnimatableModifier, который регулирует его непрозрачность между прозрачным и входным цвет в соответствии с синусоидальной волной с введенным количеством пиков, а затем поместите его перед цветом фона. линии под ним никогда не становятся полностью черными. Возможно, потому, что анимируются и некоторые другие свойства?

import SwiftUI
import PlaygroundSupport

class ViewModel: ObservableObject {
   enum TaskState {
      case started
      case nearlyFinished
      case finished
   }

   private var timer: Timer?
   private var timerTwo: Timer?
   @Published var myTaskState: TaskState

   init() {
      myTaskState = .started
      self.timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false, block: {_ in self.myTaskState = .nearlyFinished})
      self.timerTwo = Timer.scheduledTimer(withTimeInterval: 4.0, repeats: false, block: {_ in self.myTaskState = .finished})
   }
}

struct ContentView: View {
   @ObservedObject var viewModel: ViewModel
   @State private var flashing = false

   var body: some View {
      ZStack {
         myColor
         Color.clear
            //.modifier(AnimatableFlasher(flashColor: .black, flashes: 10, pct: viewModel.myTaskState == .finished ? 1.0 : 0.0))
            //.animation(.linear(duration: 5.0))
            .modifier(AnimatableFlasher(flashColor: .black, flashes: 10, pct: self.flashing ? 1.0 : 0.0))
            .onReceive(viewModel.$myTaskState, perform: { myTaskState in
               withAnimation(.linear(duration: 5.0)) {
                  self.flashing = myTaskState == .finished
               }
            })

         Text("Test").foregroundColor(.white)

      }
   }

   struct AnimatableFlasher: AnimatableModifier {
      let flashColor: UIColor
      let flashes: Int
      var pct: CGFloat = 0

      var animatableData: CGFloat {
         get { pct }
         set { pct = newValue }
      }

      func body(content: Content) -> some View {
         let opacity = oscillateOpacity(flashes: flashes, pct: pct)
         return Color(flashColor).opacity(opacity)
      }

      func oscillateOpacity(flashes: Int, pct: CGFloat) -> Double {
         let reducedPct = pct - floor(pct)
         return (1.0 - cos(Double(reducedPct)*Double(flashes)*Double.pi*2)) / 2.0
      }
   }

   private var myColor: some View {
      switch viewModel.myTaskState {
      case .started: return Color(.black)
      case .nearlyFinished: return Color(.orange)
      case .finished: return Color(.red)
      }

   }
}
PlaygroundPage.current.setLiveView(ContentView(viewModel: ViewModel()))
...