0

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)
        
    }
    
}

enter image description here

2 Answers 2

1

You could use Combine Publishers 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.

Sign up to request clarification or add additional context in comments.

7 Comments

I really thank you for your help, but as you can see in your answer you are appending items to array in more advanced way that I done, which is great! But my question key point was that using a native way instead making a custom way, let me give more explain, I am looking for a code or method that it make us free to appending items to array with a custom code! for example I want just add 10 items all together to array with an action, and I want this native way if it exist could handle it internally like we done it in custom way.
Your question does not specify that it needs to be "free from appending items to the array". I wouldn't describe anything about my answer as non-native or not internal. I guess I'm a little confused about your requirements and what type of answer you hope it will produce. Are you just looking for something that would add all of the elements at once and then animate the height of the container?
I would also point out that you specifically said "If you see any bad coding or upgradable code, please let me know." -- this is exactly what I've tried to do.
Seems like it would at least be worth an upvote, then.
Sorry I was Busy to answer in comments! I really forgot! thanks a lot! 👍
|
0

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
    }
}

1 Comment

Thanks, but your answer is just the same way that used in Way 1, your answer does not create the animation that I am looking for. PS: About 'sleep', I am not using it happily, but it makes the animation that I want be possible.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.