iosarraysswiftswiftuicore-motion

How to show random array's items only once in SwiftUI?


I'm trying to recreate a popular game: Heads up, basically the user has to try to guess the name putting the phone on his head, with friends' suggestions...if he raises the head he skips the word, if he lowers the head it means he guessed the name and he earns a point. He has limited time. I need that every time the user raises/lowers his head, the array's name changes, and each name must appear only once. Any suggestions?

This is my code:

import SwiftUI
import CoreMotion

struct ContentView: View {
    
    let motionManager = CMMotionManager()
    let queue = OperationQueue()
    @State private var roll = Double.zero
    
    @State private var people = ["John", "Marcus", "Steve", "Eric", "Philip"].shuffled()
    @State private var randomPerson = Int.random(in: 0...4)
    
    let timer = Timer.publish(every: 1, tolerance: 0.5, on: .main, in: .common).autoconnect()
    @State private var timeRemaining = 10
    @State private var score = 0
    
    var body: some View {
        NavigationView {
            ZStack {
                //Show a red background and "SKIP" if the user raises head
                if roll < 1 {
                    Color.red
                        .ignoresSafeArea()
                    
                    Text("SKIP")
                        .font(.largeTitle)
                        .bold()
                        .foregroundColor(.white)
                } else if roll > 2.1 {
                //Show a green background and "CORRECT" if user lowers head
                    Color.green
                        .ignoresSafeArea()
                    
                    Text("CORRECT")
                        .font(.largeTitle)
                        .bold()
                        .foregroundColor(.white)
                        .onAppear {
                            score += 1
                        }
                } else {
                //Otherwise show a cyan back with array's name
                    Color.cyan
                        .ignoresSafeArea()
                    
                    Text(people[randomPerson])
                        .font(.largeTitle)
                        .bold()
                        .foregroundColor(.white)
                }
                
                Text("\(timeRemaining)")
                    .font(.system(size: 39))
                    .padding(.bottom, 200)
                    .onReceive(timer) { _ in
                        if timeRemaining > 0 {
                            timeRemaining -= 1
                        }
                    }
                
                Text("Score: \(score)")
                    .font(.largeTitle)
                    .bold()
                    .foregroundColor(.white)
                    .padding(.top, 200)
            }
            .onAppear {
                //Detect device motion
                self.motionManager.startDeviceMotionUpdates(to: self.queue) { (data: CMDeviceMotion?, error: Error?) in
                    guard let data = data else {
                        print("Error: \(error!)")
                        return
                    }
                    let attitude: CMAttitude = data.attitude
                    
                    DispatchQueue.main.async {
                        self.roll = attitude.roll
                    }
                }
            }
        }
        .navigationViewStyle(.stack)
    }
}

Solution

  • You can do like this:

    1. a state variable for current selected person

    @State private var currerntPerson : String = ""

    1. a function to get random person

    getRandomPerson()

    1. change TextView show selected person name:
    Text(currerntPerson)
       .font(.largeTitle)
       .bold()
       .foregroundColor(.white)
       .onAppear {
            getRandomPerson()
       }
    

    ==== All code here:

    let motionManager = CMMotionManager()
    let queue = OperationQueue()
    @State private var roll = Double.zero
    
    @State private var people = ["John", "Marcus", "Steve", "Eric", "Philip"].shuffled()
    @State private var randomPerson = Int.random(in: 0...4)
    
    let timer = Timer.publish(every: 1, tolerance: 0.5, on: .main, in: .common).autoconnect()
    @State private var timeRemaining = 10
    @State private var score = 0
    @State private var currerntPerson : String = ""
    
    var body: some View {
        NavigationView {
            ZStack {
                //Show a red background and "SKIP" if the user raises head
                if roll < 1 {
                    Color.red
                        .ignoresSafeArea()
                    
                    Text("SKIP")
                        .font(.largeTitle)
                        .bold()
                        .foregroundColor(.white)
                } else if roll > 2.1 {
                    //Show a green background and "CORRECT" if user lowers head
                    Color.green
                        .ignoresSafeArea()
                    
                    Text("CORRECT")
                        .font(.largeTitle)
                        .bold()
                        .foregroundColor(.white)
                        .onAppear {
                            score += 1
                        }
                } else {
                    //Otherwise show a cyan back with array's name
                    Color.cyan
                        .ignoresSafeArea()
                    
                    Text(currerntPerson)
                        .font(.largeTitle)
                        .bold()
                        .foregroundColor(.white)
                        .onAppear {
                            getRandomPerson()
                        }
                }
                
                Text("\(timeRemaining)")
                    .font(.system(size: 39))
                    .padding(.bottom, 200)
                    .onReceive(timer) { _ in
                        if timeRemaining > 0 {
                            timeRemaining -= 1
                        }
                    }
                
                Text("Score: \(score)")
                    .font(.largeTitle)
                    .bold()
                    .foregroundColor(.white)
                    .padding(.top, 200)
            }
            .onAppear {
                //Detect device motion
                self.motionManager.startDeviceMotionUpdates(to: self.queue) { (data: CMDeviceMotion?, error: Error?) in
                    guard let data = data else {
                        print("Error: \(error!)")
                        return
                    }
                    let attitude: CMAttitude = data.attitude
                    
                    DispatchQueue.main.async {
                        self.roll = attitude.roll
                    }
                }
            }
        }
        .navigationViewStyle(.stack)
    }
    
    func getRandomPerson() {
        if people.count > 0 {
            let index = Int.random(in: 0..<people.count)
            currerntPerson = people[index]
            people.remove(at: index)
        }
    }