UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Day 34: Challenge. Solved but I am looking for better solution.

Forums > 100 Days of SwiftUI

Hi everyone,

I am following 100 days of SwiftUI schedule. I have finished day 34 and my code run without error. However to be honest I am not very happy with my code;

  1. I used "newArr" variable array to keep track if the button is tapped or not which I beleive there is more effective solution for that purpose.
  2. I could not find a way to alert message to appear below flag images.
  3. Game is on as round < 5. I used if statement for this purpose but I think using while loop is better for this purpose. However I am not allowed to use while inside struct. Is there any specific reason ?

Please see my code below;


import SwiftUI

struct Title: ViewModifier {

    func body(content: Content) -> some View {
        content
        //            .font(.largeTitle.bold())
            .font(.system(size: 36.0).bold())
            .foregroundColor(.white)
    }
}

extension View {
    func titleStyle() -> some View {
        modifier(Title())
    }
}

struct FlagImage: View {
    var text: String
    var rotateAmount: Double
    var opacityAmount: Double
    var scaleAmount: Double

    var body: some View {
            Image(text)
                .renderingMode(.original)
                .clipShape(Capsule())
                .overlay(Capsule().stroke(Color.black, lineWidth: 2))
                .shadow(radius: 5)
                .opacity(opacityAmount)
                .scaleEffect(scaleAmount)
                .rotation3DEffect(.degrees(Double(rotateAmount)),
                                  axis: (x: 0.0, y: 1.0, z: 0.0))
    }
}

struct ContentView: View {
    @State private var showingScore = false
    @State private var scoreTitle = ""

    @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 score = 0
    @State private var round = 0
    @State private var newArr = [false, false, false]  // Used to check if the button is tapped. true if button is tapped.
    @State private var rotateAmount = 0.0
    @State private var opacityAmount = 1.0
    @State private var scaleAmount = 1.0

    var body: some View {
        ZStack {
            RadialGradient(stops: [
                .init(color: Color(red:0.1, green: 0.2, blue: 0.45), location: 0.3),
                .init(color: Color(red:0.76, green: 0.15, blue:0.26), location: 0.3)
            ], center: .top, startRadius: 200, endRadius: 700)
            .ignoresSafeArea()

            VStack {
                Text("Guess the Flag")
                    .titleStyle()
                if round<5 {

                    VStack(spacing: 15) {
                        VStack {

                            Text ("Tap the flag of")
                                .foregroundStyle(.secondary)
                                .font(.subheadline.weight(.heavy))
                            Text(countries[correctAnswer])
                                .font(.largeTitle.weight(.semibold))
                        }
                        ForEach(0..<3) { number in
                            Button {
                                flagTapped(number)
                                newArr[number] = true
                                withAnimation(.easeOut(duration: 1)) {
                                    rotateAmount += 360
                                }
                                withAnimation() {
                                    opacityAmount -= 0.75
                                    scaleAmount -= 0.5
                                }

                            } label: {
                                FlagImage(text: countries[number],
                                          rotateAmount: (newArr[number] ? rotateAmount : 0),
                                          opacityAmount: (!newArr[number] ? opacityAmount : 1),
                                          scaleAmount: (!newArr[number] ? scaleAmount : 1)
                                )
                            }
                        }
                    }
                    .frame(maxWidth: .infinity)
                    .padding(.vertical, 20)
                    .background(.thinMaterial)
                    .clipShape(RoundedRectangle(cornerRadius: 20))

                    Spacer()
                    Spacer()

                    Text("Score: \(score)")
                        .foregroundColor(.white)
                        .font(.title.bold())

                    Spacer()
                } else {
                    VStack(spacing: 15) {
                        VStack {
                            Spacer()
                            Text ("Game is over. Your score is \(score)/8")
                                .foregroundStyle(.secondary)
                                .font(.largeTitle.weight(.semibold))

                            Spacer()
                        }
                        Button {
                            restart()
                        } label: {
                            Text("Click to restart the game!")
                                .padding()
                                .foregroundColor(.white)

                        }
                        .buttonStyle(.borderedProminent)
                        .tint(.mint)                        
                    }
                }
            }
            .padding()
        }
        .alert(scoreTitle, isPresented: $showingScore) {
            Button("Continue", action: askQuestion)
        } message: {
            Text("Your score is \(score)")
        }
    }

    func flagTapped(_ number: Int) {
        if number == correctAnswer {
            scoreTitle = "Correct"
            score += 1
        } else {
            scoreTitle = "Wrong. That is the flag of \(countries[number])"
        }
        round += 1
        showingScore = true
    }

    func askQuestion() {
        countries.shuffle()
        correctAnswer = Int.random(in: 0...2)
        newArr = [false, false, false]
        opacityAmount = 1.0
        rotateAmount = 0.0
        scaleAmount = 1.0

    }

    func restart () {
        score = 0
        round = 0
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Thanks in advance.

2      

One thing I would ask is why you are using a whole array newArr when you only need a single number. You already have a function flagTapped that checks if the chose flag is the correct one, and does that by comparing just a single value.

Similarily, You could replace newArr, with just a single state variable integer, and modify that as needed when a flag is tapped or a new question is asked. Then use that new variable in your animation selection.

@State private var chosenFlag = -1
FlagImage(text: countries[number],
          rotateAmount: (chosenFlag == number ? rotateAmount : 0),
          opacityAmount: (chosenFlag == number ? 1 : opacityAmount),
          scaleAmount: (chosenFlag == number ? 1 : scaleAmount)

2      

Thanks, that's solved my problem.

I still do not know how to move alert message to below ..

2      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

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

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

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.