Switching between two strings after button is pressed - ios

So I am working on a BlackJack game for iOS using Swift. I have two buttons, HIT and STAY. Here is where my problem lies: When I press the originally, it is player 1's turn. But after clicking the HIT button, I want the turn label to oscillate between player 2 and player 1. So after player 1 clicks HIT button, I want the turn label to read player 2 and then player 1. Here is what I got so far:
class ViewController: UIViewController {
var deck = PlayingCardDeck()
#IBOutlet weak var cardLabel: UILabel!
#IBOutlet weak var playerTurn: UILabel!
#IBOutlet weak var scorePlayerOne: UILabel!
#IBOutlet weak var scorePlayerTwo: UILabel!
#IBAction func btnHit(_ sender: Any) {
cardLabel.text = String(describing: deck.drawCard()!)
playerTurn.text = String(describing: "Player 2")
}
#IBAction func btnStay(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
deck.shuffle()
}
}
With the method I have, it mimics what I want to happen (switches to player 2)

You need a variable to track which player is currently playing and then change the label based on those variable value.
var isFirstPlayerTurn: Bool = true
#IBAction func btnHit(_ sender: Any) {
cardLabel.text = String(describing: deck.drawCard()!)
isFirstPlayerTurn = !isFirstPlayerTurn
playerTurn.text = isFirstPlayerTurn ? "Player 1" : "Player 2"
}

Create two labels, one for each player. Then hide player 2's label and flip it while hidden in viewDidLoad(). Once the turn changes, flip both labels while simultaneously showing and hiding the labels based who's turn it is.
To flip the label, use CGAffineTransform. Use the labels' alpha property to show and hide the labels.
func changeTurn(_ player: Int) {
UIView.animate(withDuration: 0.5) {
playerTurn1.transform = CGAffineTransform(scaleX: -playerTurn1.transform.a, y: playerTurn1.transform.d)
playerTurn2.transform = CGAffineTransform(scaleX: -playerTurn2.transform.a, y: playerTurn2.transform.d)
playerTurn1.alpha = player == 1 ? 1 : 0
playerTurn2.alpha = player == 2 ? 1 : 0
}
}
Usage:
#IBOutlet weak var playerTurn1: UILabel!
#IBOutlet weak var playerTurn2: UILabel!
private var turn = 1
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
deck.shuffle()
playerTurn2.alpha = 0
playerTurn2.transform = CGAffineTransform(scaleX: -playerTurn2.transform.a, y: playerTurn2.transform.d)
}
#IBAction func btnHit(_ sender: Any) {
cardLabel.text = String(describing: deck.drawCard()!)
turn = turn == 1 ? 2 : 1
changeTurn(turn)
}
Make sure both labels are the same frames.

Related

Why isn't the score showing up after segue on different view controller?

Iv created a quiz app, that tracks the user's score till the user gets to the end. There is an if statement - when it reaches the end of the quiz, a results View controller pops up. My Question is I want to get the same score value presented at the end on the resultsViewContoller. I have connected it via segue and the identifier has been placed. The app runs but when i get to the end the segue opens up the page but the score doesnt change?
How can i solve this problem?
The user finishes the app. I call the segue method and the screen comes up.
Results View Controller
import UIKit
class ResultsViewController: UIViewController {
var fruitness = Fruitness()
#IBOutlet weak var finalScoreLabel: UILabel!
var finalScore: String!
override func prepare(for: UIStoryboardSegue, sender: Any?) {
func viewWillAppear(_ animated: Bool) {
finalScoreLabel.text = finalScore
if finalScore != nil {
finalScoreLabel.text = "FINALSCORE:\(String(describing: Int(finalScore!)))"
}
}
}
}
Game View Controller
import UIKit
class GameViewController: UIViewController {
#IBOutlet weak var progressBar: UIProgressView!
#IBOutlet private weak var fruitLabel: UILabel!
#IBOutlet private weak var scoreLabel: UILabel!
#IBOutlet weak var replayGame: UIButton!
#IBOutlet weak var optionButton0: UIButton!
#IBOutlet weak var optionButton1: UIButton!
#IBOutlet weak var optionButton2: UIButton!
#IBOutlet weak var optionButton3: UIButton!
#IBOutlet weak var optionButton4: UIButton!
#IBOutlet weak var optionButton5: UIButton!
#IBOutlet weak var optionButton6: UIButton!
#IBOutlet weak var optionButton7: UIButton!
#IBOutlet weak var optionButton8: UIButton!
#IBOutlet private var fruitButtons: [UIButton]!
var score = 0
var fruitness = Fruitness()
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
}
#IBAction func replayGame(_ sender: UIButton) {
fruitness.restartGame() //Calling restart
updateUI()
}
#IBAction func touchButton(_ sender: UIButton) {
let userAnswer = sender.currentTitle!
//The userGotItRight is = to the checkAnswer function which goes through the fruitOptions array to make sure the answer is corrct. T/F
let userGotItRight = checkAnswer(userAnswer: userAnswer)
if userGotItRight {
//Depending if the user got the answer correct the button turns green/red
sender.backgroundColor = UIColor.green
} else {
sender.backgroundColor = UIColor.red
}
nextFruit() //Calling the next Fruit untill all the fruit items have been displayed.
//This timer is responsible for the UIColors green, red and clear. Without this timer the color drags onto the next fruit.
Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(updateUI), userInfo:nil, repeats: false)
}
// This check answer method needs an input to work. The input is the answer the user-choose (String).
func checkAnswer(userAnswer: String) -> Bool {
// Checks if the user got the answer correct. T/F
if userAnswer == fruitness.fruitOptions[fruitness.fruitNumber].fruit {
fruitness.score += 1 //We increase the value of score when we get the answer right.
// fruitness.finalScorez = fruitness.score
return true
} else {
return false
}
}
//Checks to make sure the eveytime it hits 0 it will shuffle.
func nextFruit() {
if fruitness.fruitNumber == 0 {
fruitness.fruitOptions.shuffle()
}
// print(fruitness.fruitOptions[fruitness.fruitNumber]) //Only gets printed in the consol
if fruitness.fruitNumber + 1 < fruitness.fruitOptions.count {
fruitness.fruitNumber += 1
} else {
self.performSegue(withIdentifier: "goToResultsVC", sender: self) //To call Segue
}
}
//Connecting and controlling oF the Segue and from GameView COntroller -> Results view controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToResultsVC" {
let destinationVC = segue.destination as! ResultsViewController
destinationVC.finalScore = scoreLabel.text //Printing the final score at the end.
}
}
#objc func updateUI() {
//Controlls the background. Clearing ability between T/F answers.
optionButton0.backgroundColor = UIColor.clear
optionButton1.backgroundColor = UIColor.clear
optionButton2.backgroundColor = UIColor.clear
optionButton3.backgroundColor = UIColor.clear
optionButton4.backgroundColor = UIColor.clear
optionButton5.backgroundColor = UIColor.clear
optionButton6.backgroundColor = UIColor.clear
optionButton7.backgroundColor = UIColor.clear
optionButton8.backgroundColor = UIColor.clear
//The fruit name available that the user needs to match.
fruitLabel.text = fruitness.getFruitText()
//Displaying the progress of the user till they reach the end.
progressBar.progress = fruitness.getProgress()
//Displaying the score at all times
scoreLabel.text = "SCORE: \(fruitness.score)"
}
}
You have a number of problems with your code.
First off, the prepare(for:sender:) method gets called on the source view controller that triggers the segue, not the destination view controller.
Second, your ResultsViewController has a viewWillAppear(_:) method nested inside its prepare(for:sender:) method. Don't do that. The viewWillAppear(_:) method needs to be a top-level method of your view controller or it won't get called.
Also, viewWillAppear(_:) methods should call super.viewWillAppear(animated).
Your ResultsViewController's viewWillAppear method should look like this:
func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
finalScoreLabel.text = finalScore
}
The prepare(for:sender:) method in your GameViewController should set finalScore in your ResultsViewController so that when its viewWillAppear is called, finalScore has a value.
Edit:
(It looks like you got that last part correct. Your GameViewController's prepare(for:sender) does appear to set finalScore. Note, however, that you are using scoreLabel to hold your finalScoreValue. You should not save state in view objects. You should have a var in GameViewController that holds your score. You could make GameViewController set your score var, and have a didSet() method on the score var that installs the updated score value into the scoreLabel.)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToResultsVC" {
let destinationVC = segue.destination as! ResultsViewController
destinationVC.finalScore = scoreLabel.text //Printing the final score at the end.
}
}
Maybe you wish to refactor your repetitive code to this one:
let optionButtonArray = [optionButton0,optionButton1 ...]
optionButtonArray.forEach { $0.backgroundColor = UIColor.clear }

Is it possible to toss data to another view controller?

I just Make timer that can use in life. just like image that I push in here, if I go back to main ViewController then I wanna the number that I input in set view controller are tossed to viewController so when I go back to main ViewController and press restart then that number gonna be in text of CountTimeLabel.. but I really don't know how to toss data that I input in another view controller to root viewController... pleas help me.. if I write code like ViewController().variableName = 30 in setViewController, that dose not make things well..(I already know about prepare function but that is not what I am finding..because this is happen when I go back to ViewController(RootViewController)) I will put my code in below..
is it possible to toss data to another view controller from other view controller?
import UIKit
class ViewController: UIViewController{
#IBOutlet var AllTileLabel: UILabel!
#IBOutlet var SumTimeLabel: UILabel!
#IBOutlet var CountTimeLabel: UILabel!
#IBOutlet var StartButton: UIButton!
#IBOutlet var StopButton: UIButton!
#IBOutlet var ResetButton: UIButton!
var timeTrigger = true
var realTime = Timer()
var second : Int = 3000
var sum : Int = 14400
var allTime : Int = 14400
var IntSecond : Int = 0
var ifReset = false
override func viewDidLoad() {
StartButton.layer.cornerRadius = 10
StopButton.layer.cornerRadius = 10
ResetButton.layer.cornerRadius = 10
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func StartButtonAction(_ sender: UIButton) {
if timeTrigger { checkTimeTrigger() }
print("Start")
}
#IBAction func StopButtonAction(_ sender: UIButton) {
endGame()
}
#IBAction func ResetButtonAction(_ sender: UIButton) {
print(second)
getTimeData()
//second = 3000
//CountTimeLabel.text = "0:50:00"
CountTimeLabel.text = printTime(temp: second)
ifReset = true
}
#IBAction func Reset(_ sender: UIButton) {
endGame()
timeTrigger = true
realTime = Timer()
second = 3000
sum = 14400
allTime = 14400
IntSecond = 0
ifReset = false
AllTileLabel.text = "8:00:00"
SumTimeLabel.text = "0:0:0"
CountTimeLabel.text = "0:50:00"
}
#objc func updateCounter(){
// if String(format: "%.2f",second) == "0.00"{
if second < 1 {
endGame()
CountTimeLabel.text = "종료"
} else {
second = second - 1
sum = sum + 1
allTime = allTime - 1
AllTileLabel.text = printTime(temp: allTime)
SumTimeLabel.text = printTime(temp: sum)
CountTimeLabel.text = printTime(temp: second)
print("update")
}
}
func checkTimeTrigger() {
realTime = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true)
timeTrigger = false
}
func endGame() {
realTime.invalidate()
timeTrigger = true
}
func printTime(temp : Int) -> String
{
let S = temp%60
let H = temp/3600
let M = temp/60 - H*60
let returnString = String(H) + ":" + String(M) + ":" + String(S)
return returnString
}
func getTimeData() {
second = 20
sum = SetViewController().real.sum
allTime = SetViewController().real.allTime
print(second)
}
}
import UIKit
class SetViewController: UIViewController {
#IBOutlet var View1: UIView!
#IBOutlet var View2: UIView!
#IBOutlet var InputView1: UIView!
#IBOutlet var InputView2: UIView!
#IBOutlet var SetButton: UIButton!
#IBOutlet var H1TextField: UITextField!
#IBOutlet var M1TextField: UITextField!
#IBOutlet var H2TextField: UITextField!
#IBOutlet var M2TextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
H1TextField.keyboardType = .numberPad
M1TextField.keyboardType = .numberPad
H2TextField.keyboardType = .numberPad
M2TextField.keyboardType = .numberPad
View1.layer.cornerRadius = 14
View2.layer.cornerRadius = 14
InputView1.layer.cornerRadius = 10
InputView2.layer.cornerRadius = 10
SetButton.layer.cornerRadius = 10
// Do any additional setup after loading the view.
}
#IBAction func SetButton(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
enter image description here
If you're a hobbyist programmer and you just want to "get it done", simply use a static.
Let's say Bottom: UIViewController is the "main", root, view controller at the absolute "base" of your app. no matter what happens "Bottom" is always there.
Say Timer: UIViewController is (any) other view controller you put on top for some reason.
In Bottom, do this
class Bottom: UIViewController, etc ... {
static weak var current: Bottom? = nil
override func viewDidLoad() {
super.viewDidLoad()
Bottom.current = self
}
func testing() {
print("it works, WTH")
}
Note that in ViewDidLoad, you simply set it.
Next, say you are in Timer, try this:
class Timer: UIViewController, etc ... {
func someFunction() {
Bottom.current.testing() // it's that easy
}
It's that easy.
Note there is a huge amount of confusion about using statics, singletons, and similar approaches in iPhone programming.
(Just for example, many engineers will say "avoid singletons!" This is remarkably confused because in iOS engineering, almost everything is a singleton (notably the app itself (!!!!!), the screen, the GPS, etc etc.)
In any event, as a beginner hobbyist, learn how to use statics (it's simple .. Bottom.current. ... as above), and eventually you can learn about the pros and cons of such things.

Not Repeating Words in Swift when clicking button - IOS

How can I not repeat the array when I click on the button in swift? I'm trying to generate fruits without them repeating. Can I sort the string that way it runs through all the fruits one by one? It doesn't have to be randomized. I just want each word to show only once when I click the button and show the last array "There aren't any fruit options left"
I tried to randomize the string but that repeats the fruits. I just want it to go one by one. When I press the button on my screen the output on the image label should give me each fruit one at a time.
ie. Button pressed"
Output: "Apple"
button pressed again
Output: "Banana"
and so on until the last string shows "There aren't any fruit options left"
import UIKit
class fruitrandomViewController: UIViewController {
#IBOutlet weak var nextfruitButton: UIButton!
#IBOutlet weak var fruitbox: UILabel!
#IBAction func fruitbutton(_ sender: UIButton) {
let array = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear","T"There aren't any fruit options left",]
let randomFruitgenerator = Int(arc4random_uniform(UInt32(array.count)))
fruitbox.text = array[randomFruitgenerator]
}
}
You need to somehow keep track of the array elements that you have already used. You could do this in a couple of ways:
Keep an index property that tracks the next element of the array
Mutate the array itself as elements are consumed
Either way, you should make the array an instance property, not a local variable in the function itself.
Here is an example of the second approach (I prefer this since I think it makes the code a little simpler, as you don't need to track the next index).
class fruitrandomViewController: UIViewController {
#IBOutlet weak var nextfruitButton: UIButton!
#IBOutlet weak var fruitbox: UILabel!
var fruit = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear",].shuffled()
#IBAction func fruitbutton(_ sender: UIButton) {
if fruit.isEmpty {
fruitbox.text = "There's no more fruit left"
} else {
fruitbox.text = self.fruit[0]
self.fruit.remove(at:0)
}
}
}
For completeness, here is the first approach (with an added "previous fruit" button):
class fruitrandomViewController: UIViewController {
#IBOutlet weak var nextfruitButton: UIButton!
#IBOutlet weak var fruitbox: UILabel!
let fruit = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear",].shuffled()
var nextFruit = 0
#IBAction func fruitbutton(_ sender: UIButton) {
if nextFruit < fruit.count {
fruitbox.text = self.fruit[nextFruit]
nextFruit += 1
} else {
fruitbox.text = "There's no more fruit left"
}
}
#IBAction func previousFruitButton(_ sender: UIButton) {
guard nextFruit > 0 else {
return
}
nextFruit -= 1
fruitbox.text = self.fruit[nextFruit]
}
}
If you don't want the fruit in a random order, just remove the .shuffled()

why my stepper does't give the correct value when I tap the increment button?

I am using kw stepper pod, since it is customizable. I can separate the increment button, decrement button and using my own label. but the behaviour should be the same as UIStepper
that is what it looks like, it consists of 1 increment button, 1 decrement button and counter label.
here is the code on my view controller:
import UIKit
import KWStepper
class ViewController: UIViewController {
#IBOutlet weak var counterLabel: UILabel!
#IBOutlet weak var decrementButton: UIButton!
#IBOutlet weak var incrementButton: UIButton!
var stepper: KWStepper!
override func viewDidLoad() {
super.viewDidLoad()
stepper = KWStepper(decrementButton: decrementButton, incrementButton: incrementButton)
stepper.autoRepeat = false
stepper.autoRepeatInterval = 1
stepper.wraps = false
stepper.minimumValue = 0
stepper.maximumValue = 100
stepper.incrementStepValue = 1
stepper.decrementStepValue = 1
stepper.value = 0.0
counterLabel.text = "\(stepper.value)"
}
#IBAction func incrementButtonDidTapped(_ sender: Any) {
counterLabel.text = "\(stepper.value)"
}
#IBAction func decrementButtonDidTapped(_ sender: Any) {
counterLabel.text = "\(stepper.value)"
}
}
I connect the increment and decrement button using #IBAction touch up inside event.
so I expect when I tap the increment button, it will increase from
0,0 -> 1.0 -> 2.0 -> 3.0 and so on.
but in my case, when tap the increment button it will give
0,0 -> 0,0 -> 1,0 -> 2,0
the 0,0 will appear twice. why it appears twice ? how to solve this issue
I know that I can see the stepper value from value change event like this
stepper
.valueChanged { stepper in
// ...
}
but I need to separate the event from increment and decrement button
here is the project on my google drive: https://drive.google.com/file/d/1IgeVW1OemRttoAOqJ6Ba8LpyZC_rc3-o/view?usp=sharing
The incrementButtonDidTapped and decrementButtonDidTapped methods are possibly being called before stepper.value changes, since KWStepper also listens for touchUpInside events from both of these buttons to change its value.
KWStepper exposes two properties decrementCallback and incrementCallback which you can use to get notified when the value gets decremented/incremented. You can use these instead of an IBAction on the two buttons.
stepper.decrementCallback = { (stepper) in
self.counterLabel.text = "\(stepper.value)"
}
stepper.incrementCallback = { (stepper) in
self.counterLabel.text = "\(stepper.value)"
}
Alternatively, you can confirm to KWStepperDelegate and implement KWStepperDidIncrement and KWStepperDidDecrement delegate methods to get notified.
import UIKit
import KWStepper
class ViewController: UIViewController, KWStepperDelegate {
#IBOutlet weak var counterLabel: UILabel!
#IBOutlet weak var decrementButton: UIButton!
#IBOutlet weak var incrementButton: UIButton!
var stepper: KWStepper!
override func viewDidLoad() {
super.viewDidLoad()
stepper = KWStepper(decrementButton: decrementButton, incrementButton: incrementButton)
stepper.autoRepeat = false
stepper.autoRepeatInterval = 1
stepper.wraps = false
stepper.minimumValue = 0
stepper.maximumValue = 100
stepper.incrementStepValue = 1
stepper.decrementStepValue = 1
stepper.value = 0.0
// Set the delegate
stepper.delegate = self
counterLabel.text = "\(stepper.value)"
}
#objc func KWStepperDidIncrement() {
counterLabel.text = "\(stepper.value)"
}
#objc func KWStepperDidDecrement() {
counterLabel.text = "\(stepper.value)"
}
}
You can replace your click events with following code
#IBAction func incrementButtonDidTapped(_ sender: Any) {
stepper.valueChanged { (steper) in
self.counterLabel.text = "\(steper.value)"
}
}
#IBAction func decrementButtonDidTapped(_ sender: Any) {
stepper.valueChanged { (steper) in
self.counterLabel.text = "\(steper.value)"
}
}

Counting Up and Down using Volume buttons on iOS (Swift)

Hi I creating an app for myself, where I want to count and display the # on the screen. I want to use the volume buttons to count up and down. I was able create a button on screen to count up and down but I want to use the physical buttons. I have listed what I have so far, but the volume buttons doesn't seem to work. I've replaced AVAudioSession with AVAudioPlayer hoping to control the volume with the slider. I get a error EXC_BAD_ACCESS (Code=1, Adress=0x48)
import UIKit
import AVFoundation
class ViewController: UIViewController {
var audioPlayer = AVAudioPlayer()
#IBOutlet weak var counter: UILabel!
#IBOutlet weak var tapCounterUp: UIButton!
#IBOutlet weak var tapCounterDown: UIButton!
#IBOutlet weak var tapReset: UIButton!
#IBOutlet weak var volumeController: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
//Starts the count at 0
var count : Int = 0
//Adds +1 to Count
#IBAction func buttonUp(_ sender: Any) {
count += 1
counter.text = String(count)
}
//Subtracts -1 to Count
#IBAction func buttonDown(_ sender: Any) {
count -= 1
counter.text = String(count)
}
//Resets the Count to "0"
#IBAction func buttonReset(_ sender: Any) {
count = 0
counter.text = String(count)
}
//Volume Controller
#IBAction func volumeControl(_ sender: UISlider) {
let sliderValue = lroundf(sender.value)
counter.text = "\(sliderValue)"
/// **This is were the Error occurs.**
audioPlayer.volume = volumeController.value
}
// End of function, don't delete
}

Resources