I am working on a custom delay Animation which I started my version 0.0.0 with asyncAfter
and then updated my code to Version 1.0.0 using sleep
, I would like to know if there is a native and easy way for such a delay Animation instead my custom way, see the deference in gif or code.
PS: If you see any bad coding or upgradable code, please let me know.
struct ContentView: View {
@State private var array: [Int] = [Int]()
var body: some View {
VStack(spacing: 10.0) {
Button("remove all") { array.removeAll() }.foregroundColor(.red)
Button("Way 1") { array = Array(repeating: 0, count: 5) }
Button("Way 2(Custom Code)") {
DispatchQueue.global(qos: .background).async {
for index in 0...5 {
usleep(50_000)
DispatchQueue.main.async { array.append(index) }
}
}
}
ForEach(array.indices, id:\.self) { index in
Image(systemName: "arrowtriangle.up.fill").scaledToFit().frame(width: 25, height: 25)
}
}
.animation(.default, value: array)
}
}
CodePudding user response:
You could use Combine Publisher
s to manage the appending/delays. There are multiple ways to accomplish this -- here's one using a Timer
Publisher
that is connected with a simple [Int]
that get zipped together:
class ViewModel : ObservableObject {
@Published var array: [Int] = [Int]()
private var cancellable : AnyCancellable?
func run() {
cancellable = Publishers.Zip(Array(0...5).publisher,
Timer.publish(every: 0.5, on: .main, in: .default).autoconnect()
)
.map { $0.0 }
.sink(receiveValue: { newValue in
self.array.append(newValue)
})
}
func cancel() {
cancellable?.cancel()
}
func removeAll() {
array.removeAll()
}
}
struct ContentView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
VStack(spacing: 10.0) {
Button("remove all") { viewModel.removeAll() }.foregroundColor(.red)
Button("Add arrows") {
viewModel.run()
}
Button("Cancel") {
viewModel.cancel()
}
ForEach(viewModel.array.indices, id:\.self) { index in
Image(systemName: "arrowtriangle.up.fill").scaledToFit().frame(width: 25, height: 25)
}
}
.animation(.default, value: viewModel.array)
}
}
This approach lets you avoid sleep
, but also allows you do other useful things like cancel mid-session.
CodePudding user response:
you could try something simple, like this (especially without using usleep(50_000)
):
struct ContentView: View {
@State private var array: [Int] = [Int]()
var body: some View {
VStack(spacing: 10.0) {
Button("remove all") { array.removeAll() }.foregroundColor(.red)
Button("Way 1") { array = Array(repeating: 0, count: 5) }
Button("Way 2(Custom Code)") {
for index in 0...5 {
array.append(index)
}
}
ForEach(array.indices, id:\.self) { index in
Image(systemName: "arrowtriangle.up.fill").scaledToFit().frame(width: 25, height: 25)
}
}
.animation(.linear(duration: 0.5), value: array) // <-- here
}
}