leontedev / GuessTheFlag

#100DaysOfSwiftUI - Project 2 - Days 20-22

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

GuessTheFlag

A simple SwuiftUI game to learn flags.

gif.gif

Day 20

  • Stacks: vertical, horizontal and z axis
  • The Color view: Color.red. To use it as a background color, use a ZStack:
ZStack {
    Color.gray.edgesIgnoringSafeArea(.all)
    VStack { }
}
  • Color view’s frame modifier
Color.blue.frame(width: 20, height: 20)
  • Custom color for the Color view
Color(red: 1, green: 0.8, blue: 0, opacity: 0.9)
  • Gradient views
LinearGradient(gradient: Gradient(colors: [Color.white, Color.gray]), startPoint: .top, endPoint: .bottom)
RadialGradient(gradient: Gradient(colors: [Color.gray, Color.white]), center: .top, startRadius: 20, endRadius: 400).edgesIgnoringSafeArea(.all)
AngularGradient(gradient: Gradient(colors: [Color.white, Color.black]), center: .topTrailing).edgesIgnoringSafeArea(.all)
  • Buttons & Image with the systemName initializer - using SF Symbols eg (systemName: “pencil”)
Button(action: { print("hit") }) {
                    HStack {
                        Image(systemName: "pencil")
                        Text("Edit")
                    }
}
  • Alerts need a @State variable to track when they are shown
struct ContentView: View {
    @State private var showingAlert = false
    
    var body: some View {

    Button(action: {
                    self.showingAlert = true
                }) {
                    HStack {
                        Image(systemName: "pencil.circle")
                        Text("Edit")
                    }
                }.alert(isPresented: $showingAlert) {
                    Alert(title: Text("Edit Mode"), message: Text("You have entered edit mode."), dismissButton: .default(Text("OK")))
                }
    }
}

Day 21

  • Completion handler on Alert
Alert(title: Text(scoreTitle), message: Text("Your score is ??"), dismissButton: .default(Text("Continue")) { self.askQuestion() })
  • .clipShape(Shape) modifier
Image(self.countries[number])
                            .renderingMode(.original)
                            .clipShape(Capsule())
                            .overlay(Capsule().stroke(Color.black, lineWidth: 1))
                            .shadow(color: .black, radius: 2)

Day 22 - Challenge

  1. Add an @State property to store the user’s score, modify it when they get an answer right or wrong, then display it in the alert.
  2. Show the player’s current score in a label directly below the flags.
  3. When someone chooses the wrong flag, tell them their mistake in your alert message – something like “Wrong! That’s the flag of France,” for example.

Day 24 - Challenge 3

Go back to project 2 and create a FlagImage() view that renders one flag image using the specific set of modifiers we had.

For the full list of Code Actions to appear (on cmd + click on the view), the Canvas has to be enabled. I used the Extract Subview Code Action, however I had to add a string property to the View, to pass the country name.

struct FlagImage: View {
    var country: String
    
    var body: some View {
        Image(country)
            .renderingMode(.original)
            .clipShape(Capsule())
            .overlay(Capsule().stroke(Color.black, lineWidth: 1))
            .shadow(color: .black, radius: 2)
    }
}

Day 34 - Challenges

  1. When you tap the correct flag, make it spin around 360 degrees on the Y axis.
  2. Make the other two buttons fade out to 25% opacity.
  3. And if you tap on the wrong flag? Well, that’s down to you – get creative!

1. I’m using an array of Boolean values to manage the state of the animation.

// Challenge #1
@State private var isFlipAnimated: [Bool] = [false, false, false]

And then the 3D rotation view modifier will be triggered when the corresponding Bool value changes.

ForEach(0 ..< 3) { number in
                    Button(action: {
                        self.flagTapped(number)
                    }) {
                        FlagImage(country: self.countries[number])
                    }.rotation3DEffect(.degrees(self.isFlipAnimated[number] ? 360 : 0), axis: (x: 0, y: 1, z: 0))

This is triggered in the the flagTapped function:

withAnimation(.default) {
    isFlipAnimated[number] = true
}

And finally - when a new round starts, I revert back to false values for all 3 buttons:

isFlipAnimated = [false, false, false]

2. A similar solution to make the other 2 buttons opaque, but I’m tracking the state in a different array.

3. For the final challenge, I’ve decided to hide the incorrect flags and leave the correct answer to be displayed.

Again, I’m using a third @State variable to track the required state. And if the wrong flag is selected, the two incorrect flags are going to be updated with the .opacity view modifier.

.opacity(self.isOpacityAnimated[number] ? (self.isWrongAnswer ? 0 : 0.25) : 1)

Day 75 - Accessibility

We will provide a custom label for each flag image/button - describing the aspect of the flag. We will store these descriptions in a dictionary:

let labels = [
    "Estonia": "Flag with three horizontal stripes of equal size. Top stripe blue, middle stripe black, bottom stripe white",
...
]

...

.accessibility(label: Text(self.labels[self.countries[number], default: "Unknown flag"])) 

About

#100DaysOfSwiftUI - Project 2 - Days 20-22


Languages

Language:Swift 100.0%