WWDC24 SALE: 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      

Save 50% in my WWDC sale.

SAVE 50% To celebrate WWDC24, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

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.