LAST CHANCE: Save 50% on all my Swift books and bundles! >>

SOLVED: SOLVED Day 34 -- how can I make the alert wait for the animations to finish?

Forums > 100 Days of SwiftUI

title says it all. Right now, the alert is covering up the animations I've so carefully crafted. How can I get the alert to wait a second before showing?

2      

Hey Now seeks advice for delaying code execution:

how can I make the alert wait for the animations to finish?

This hint might be useful?
See -> Rooster's Delay function

Wait?! Is it pronouced "Hey Now?" or "Hal No?"

3      

@Bnerd  

How I would approach:

  • Use a timer
  • Timer starts when your criteria for the alert is met
  • When timer reaches the time duration of your animation you trigger you boolean alert value
  • Stop/reset timer
  • Alert pops up

:) I am sure there is a more sophisticated way, but this what I thought for now..

3      

@Obelix -- thanks for the help. I take it the answer is, "Answer non-trivial, ask again later when we get to concurrency."

Originally I was @haineux, which is the French word for "Heinous" ("horrible and evil") for silly reasons. "Haineux" is pronounced "Hey No" in French.

2      

@ioannisfa Not quite figuring it out. Here's my code:

import SwiftUI

struct ContentView: View {
    @State private var countries = ["Estonia", "France", "Germany", "Ireland", "Italy", "Nigeria", "Poland", "Russia", "Spain", "UK", "US"].shuffled()
    @State private var correctAnswer = Int.random(in: 0...2)
    @State private var showingScore = false
    @State private var scoreTitle = ""
    @State private var attempts = 0
    @State private var correct = 0
    @State private var selectedFlag = -1
    @State private var hasTimeElapsed = false

    var body: some View {
        ZStack {
            LinearGradient(gradient: Gradient(colors: [.blue, .gray]), startPoint: .top, endPoint: .bottom)
                .ignoresSafeArea()

            VStack(spacing: 30) {
                VStack {
                    Text("Tap the flag of")
                        .foregroundColor(.white)
                        .font(.subheadline.weight(.heavy))

                    Text(countries[correctAnswer])
                        .foregroundColor(.white)
                        .font(.largeTitle.weight(.semibold))

                }

                ForEach(0..<3) { number in
                    Button {
                        flagTapped(number)
                    } label: {
                        Image(countries[number])
                        }
                        .clipShape(RoundedRectangle(cornerRadius: 20))
                        .shadow(radius: 10, x:5, y:5)
                        .rotation3DEffect(.degrees(selectedFlag == number ? 360 : 0), axis: (x: 0, y: 1, z: 0))
                        .opacity(((selectedFlag == number) || (selectedFlag == -1))  ? 1 : 0.3)
                        .scaleEffect((selectedFlag == -1)  ? 1 : ((selectedFlag == number) ? 1.25 : 0.5))
                        .animation(.default, value: selectedFlag)
            }
        }
            .alert(scoreTitle, isPresented:$showingScore) {
                Button("Continue", action: askQuestion)
            } message: {
            Text("Your score is \(correct) out of \(attempts)")
            }
        }
    }

    func askQuestion() {
        countries.shuffle()
        correctAnswer = Int.random(in: 0...2)
        selectedFlag = -1
        hasTimeElapsed = false
 }

    func flagTapped(_ number: Int) {
        selectedFlag = number
        attempts += 1
        if number == correctAnswer {
            scoreTitle = "Correct"
            correct += 1
        } else {
            scoreTitle = "Wrong"
        }
        showingScore = true
    }

    private func delay() async {
        // Delay of 1 seconds (1 second = 1_000_000_000 nanoseconds)
        try? await Task.sleep(nanoseconds: 1_000_000_000)
        hasTimeElapsed = true
    }
}

3      

I like Rooster's delay function. When I did this challenge I used google to get the answer I used.

DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
        showingScore = true}

I don't know if it's the right way or best practice, but it worked for me.

4      

@WileECodee I like your solution a lot. I come from AppKit, so it's obvious in retrospect, ughhhhhh.

2      

Late to the show, but I wanted to do the same thing, and I ended up using the following code to make it work.

Update flagTapped(_ number: Int) method by replacing showingScore = true with

Task {
    try? await Task.sleep(nanoseconds: 1_000_000_000)
    await MainActor.run {
        showingScore = true
    }
}

I actually learned this today from the following article: Main Actor

Task will allow us to run the code in the background, sleep for x amount of seconds, then MainActor will ensure the next code runs on the UI thread.

3      

@daffyduck Thank you very many of this code snippet. I tried to found this kind of easy solution for delaying Alert. Have to check also that Main Actor infopage.

2      

Hacking with Swift is sponsored by Essential Developer.

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until July 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.