Call #IBAction function without pressing the button - swift

How i can call button1() func from calc() with original sender(like if i tap this button)? Call func with nil is bad soulution, because original sender is lost and i cant change button image through sender?.image = UIImage(named: "").
All answers in others topics offer use button.sendActions(for: .touchUpInside). But this solution isn't working.
var var1 = true
var var2 = 1
#IBAction func button1(_ sender: UIBarButtonItem?) {
if var2 == 1 {
sender?.image = UIImage(named: "1")
} else {
sender?.image = UIImage(named: "2")
}
}
func calc() {
if var1 {
button1(nil)
}
}

Make outlet of button and pass the reference as sender
#IBOutlet var outletButton: UIButton!
func calc() {
if var1 {
button1(outletButton)
}
}

var var1 = true
var var2 = 1
#IBAction func button1(_ sender: UIBarButtonItem?) {
button1Action()
}
func button1Action() {
if var2 == 1 {
sender?.image = UIImage(named: "1")
} else {
sender?.image = UIImage(named: "2")
}
}
func calc() {
if var1 {
button1Action()
}
}

you can callself.perform(Selector, with: self)

Related

How to determine a struct's return value based on UI Objects

I currently have a struct in which my ViewController references to return a random value from a group of arrays (aList, bList, cList, & dList). I ideally would like to be have aList always enabled while being able to toggle bList, cList, & dList on/off.
I have all this in a struct (shown below) but I'm very new to Swift and am learning that structs are "value types" while the ViewController is a "reference type" and apparently UI objects can only be referenced from reference types?
import GameKit
import Foundation
struct PromptProvider {
var aListIndex = 0
var bListIndex = 0
var cListIndex = 0
var dListIndex = 0
var includeB = false
var includeC = false
var includeD = false
var aList = [
“A1”,
“A2”,
“A3”,
“A4”]
var bList = [
“B1”,
“B2”,
“B3”,
“B4”]
var cList = [
“C1”,
“C2”,
“C3”,
“C4”]
var dList = [
“D1”,
“D2”,
“D3”,
“D4”]
init() {
self.restart()
}
mutating func restart() {
if includeB == true {self.aList.append(contentsOf: bList)}
if includeC == true {self.aList.append(contentsOf: cList)}
if includeD == true {self.aList.append(contentsOf: dList)}
self.aList.shuffle()
self.aListIndex = 0
}
mutating func randomPrompt() -> String {
if aListIndex == aList.count {
return "Out of String Values"
}
else {
defer {aListIndex += 1}
return aList[aListIndex]
}
}
}
I think I'm treating the struct as a reference type here but am unable to connect a UI object to the structs code.
Any help/guidance on how to pivot my strategy with this would be greatly appreciated. Thank you
As long as you have a single instance of PromptProvider you can make changes to that instance at will. If you need multiple references to a PromptProvider then you will need to make PromptProvider a class.
I mentioned that you had a bug in your restart method. You were appending data to your aList array without clearing out the previous appended data. What you need is a master list to contain your aList as well as the contents of your other lists so you can reset it as needed.
If I were you, I'd implement your PromptProvider struct like this:
struct PromptProvider {
let aList = [
"A1",
"A2",
"A3",
"A4"]
let bList = [
"B1",
"B2",
"B3",
"B4"]
let cList = [
"C1",
"C2",
"C3",
"C4"]
let dList = [
"D1",
"D2",
"D3",
"D4"]
var includeB = false {
didSet { restart() }
}
var includeC = false {
didSet { restart() }
}
var includeD = false {
didSet { restart() }
}
var masterList: [String] = []
init() {
self.restart()
}
mutating func restart() {
masterList.removeAll()
masterList.append(contentsOf: aList)
if includeB == true {masterList.append(contentsOf: bList)}
if includeC == true {masterList.append(contentsOf: cList)}
if includeD == true {masterList.append(contentsOf: dList)}
masterList.shuffle()
}
mutating func randomPrompt() -> String? {
return masterList.popLast()
}
}
And then in your view controller you can declare and use your PromptProvider like so:
class ViewController: UIViewController {
var promptProvider = PromptProvider()
#IBAction func onPrintRandomPrompt(_ sender: UIButton) {
if let prompt = promptProvider.randomPrompt() {
print(prompt)
} else {
print("Out of String Values")
promptProvider.restart()
}
}
}
If you want to change the contents of the provider all you have to do is toggle its flags and that will reset the contents of the master list.
extension ViewController {
#IBAction func onIncludeB(_ sender: UIButton) {
promptProvider.includeB.toggle()
sender.setTitleColor(promptProvider.includeB ? .green : .red, for: .normal)
}
#IBAction func onIncludeC(_ sender: UIButton) {
promptProvider.includeC.toggle()
sender.setTitleColor(promptProvider.includeC ? .green : .red, for: .normal)
}
#IBAction func onIncludeD(_ sender: UIButton) {
promptProvider.includeD.toggle()
sender.setTitleColor(promptProvider.includeD ? .green : .red, for: .normal)
}
}

Unresolved Identifier 'count'

Here is the error that I am seeing.
The "cardButton" is responsible for showing the next question. This is a small card app game that I am trying to make and as you can see from the image this is where I am having the issue with the code.
Here is the code:
import UIKit
class MultipleViewController: UIViewController {
#IBOutlet weak var questionLabel2: UILabel!
#IBOutlet var answerButtons: [UIButton]!
#IBOutlet weak var cardButton: UIButton!
#IBAction func cardButtonHandle(_ sender: Any) {
cardButton.isEnabled = true
if questionIdx < count(mcArray) - 1 { // There are still more questions
questionIdx += 1 //
} else {
questionIdx = 0
}
nextQuestion()
}
#IBAction func answerButtonHandle(_ sender: UIButton) {
if sender.titleLabel?.text == correctAnswer{
sender.backgroundColor = UIColor.green
print("Correct!")
} else {
sender.backgroundColor = UIColor.red
print("Wrong Answer")
}
for button in answerButtons{
button.isEnabled = false
if button.titleLabel?.text == correctAnswer {
button.backgroundColor = UIColor.green
}
}
cardButton.isEnabled = true // next question
}
var correctAnswer: String? // correct answers
var answers = [String]() // answers
var question : String? // Questions
var questionIdx = 0 // not sure what this is ?
override func viewDidLoad() {
super.viewDidLoad()
// titleForButtons() // call buttons as soon its loaded..
cardButton.isEnabled = false
nextQuestion()
}
func nextQuestion (){
let currentQuestion = mcArray![questionIdx]
answers = currentQuestion["Answers"] as! [String]
correctAnswer = currentQuestion["CorrectAnswer"] as? String
question = currentQuestion["Question"] as? String
titleForButtons ()
}
func titleForButtons (){
for (idx,button) in answerButtons .enumerated() {
button.titleLabel?.lineBreakMode = .byWordWrapping
button.setTitle(answers[idx],for:.normal)
button.isEnabled = true
}
questionLabel2.text = question
}
}
The following should work, you did not have the correct syntax for the length of the array. Note that if you have not initialized your questions array, this would cause a crash. Therefore you might want to add a guard into your code. Perhaps use the following
#IBAction func cardButtonHandle(_ sender: Any) {
cardButton.isEnabled = true
if questionIdx < (mcArray!.count) - 1 { // There are still more questions
questionIdx += 1 //
} else {
questionIdx = 0
}
nextQuestion()
}

How can I get the game to show the word after all guesses have been used up?

so I'm new to coding and I've been doing practice games a such to build my skills. I've created this word guessing game and I'm trying to make the game show the word after all guesses have been used up. However, the program doesn't read the code I write to set the label to display the answer. Here is the code I've written so far:
class ViewController: UIViewController {
var listOfWords = ["ladybug", "program", "computer", "language", "glorious", "incandescent"]
let incorrectMovesAllowed = 7
var totalWins = 0 {
didSet {
newRound()
}
}
var totalLosses = 0 {
didSet {
newRound()
}
}
#IBOutlet var letterButtons: [UIButton]!
#IBAction func buttonPressed(_ sender: UIButton) {
sender.isEnabled = false
let letterString = sender.title(for: .normal)!
let letter = Character(letterString.lowercased())
currentGame.playerGuessed(letter: letter)
updateUI()
updateGameState()
}
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet weak var correctWordLabel: UILabel!
#IBOutlet weak var treeImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
newRound()
// Do any additional setup after loading the view, typically from a nib.
}
func enableLetterButtons (_enable: Bool){
for button in letterButtons {
button.isEnabled = _enable
}
}
var currentGame : Game!
func newRound() {
if !listOfWords.isEmpty{
let newWord = listOfWords.removeFirst()
currentGame = Game (word: newWord, incorrectMovesRemaining: incorrectMovesAllowed, guessedLetters: [])
enableLetterButtons(_enable: true)
updateUI()
} else {
enableLetterButtons (_enable: false)
}
}
func updateUI() {
var letters = [String] ()
for letter in currentGame.formattedWord.characters {
letters.append(String(letter))
}
let wordWithSpacing = letters.joined(separator: " ")
correctWordLabel.text = wordWithSpacing
scoreLabel.text = "Wins: \(totalWins), Losses:\(totalLosses)"
treeImageView.image = UIImage (named: "Tree \(currentGame.incorrectMovesRemaining)")
}
func updateGameState(){
var letters = [String] ()
for letter in currentGame.word.characters {
letters.append(String(letter))
}
let theAnswer = letters.joined(separator: " ")
if currentGame.incorrectMovesRemaining == 0 {
correctWordLabel.text = theAnswer
Thread.sleep(forTimeInterval: 3)
totalLosses += 1
} else if currentGame.word == currentGame.formattedWord {
totalWins += 1
} else {
updateUI()
}
}
}
In addition, I have a structure that is written like this:
import Foundation
struct Game {
var word : String
var incorrectMovesRemaining : Int
var guessedLetters: [Character]
mutating func playerGuessed (letter: Character){
guessedLetters.append(letter)
if !word.characters.contains(letter){
incorrectMovesRemaining -= 1
}
}
var formattedWord: String {
var guessedWord = ""
for letter in word.characters {
if guessedLetters.contains(letter) {
guessedWord += "\(letter)"
} else {
guessedWord += "_"
}
}
return guessedWord
}
}
You need to redraw your UI, this is done with self.setNeedsDisplay(). It notifies the system that the view's contents needs to be redrawn. In your updateUI() you can add this.
Regarding setNeedsDisplay you can get more information here
class ViewController: UIViewController {
var listOfWords = ["ladybug", "program", "computer", "language", "glorious", "incandescent"]
let incorrectMovesAllowed = 7
var totalWins = 0 {
didSet {
newRound()
}
}
var totalLosses = 0 {
didSet {
newRound()
}
}
#IBOutlet var letterButtons: [UIButton]!
#IBAction func buttonPressed(_ sender: UIButton) {
sender.isEnabled = false
let letterString = sender.title(for: .normal)!
let letter = Character(letterString.lowercased())
currentGame.playerGuessed(letter: letter)
updateUI()
updateGameState()
}
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet weak var correctWordLabel: UILabel!
#IBOutlet weak var treeImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
newRound()
// Do any additional setup after loading the view, typically from a nib.
}
func enableLetterButtons (_enable: Bool){
for button in letterButtons {
button.isEnabled = _enable
}
}
var currentGame : Game!
func newRound() {
if !listOfWords.isEmpty{
let newWord = listOfWords.removeFirst()
currentGame = Game (word: newWord, incorrectMovesRemaining: incorrectMovesAllowed, guessedLetters: [])
enableLetterButtons(_enable: true)
updateUI()
} else {
enableLetterButtons (_enable: false)
}
}
func updateUI() {
var letters = [String] ()
for letter in currentGame.formattedWord.characters {
letters.append(String(letter))
}
let wordWithSpacing = letters.joined(separator: " ")
correctWordLabel.text = wordWithSpacing
scoreLabel.text = "Wins: \(totalWins), Losses:\(totalLosses)"
treeImageView.image = UIImage (named: "Tree \(currentGame.incorrectMovesRemaining)")
self.setNeedsDisplay()
}
func updateGameState(){
var letters = [String] ()
for letter in currentGame.word.characters {
letters.append(String(letter))
}
let theAnswer = letters.joined(separator: " ")
if currentGame.incorrectMovesRemaining == 0 {
correctWordLabel.text = theAnswer
Thread.sleep(forTimeInterval: 3)
totalLosses += 1
} else if currentGame.word == currentGame.formattedWord {
totalWins += 1
} else {
updateUI()
}
}
}
Create a variable that will keep track of how many times you have guessed wrong. Then you can do this:
Use a while statement:
while numberOfTimesGuessedWrong <= 7{
}
//when you have guessed incorrectly 7 times, the compiler will move here:
wordLabel.text = "\(correctAnswer)"
So when you guess incorrectly 7 times, on the 8th time, it will then show the correct answer.

How to keep track of items answered on in a label?

In my quiz app, if I wanted to keep track of the question number the user was on and display it, how would I do so with my following code? So if the user has answered two questions when the third appears I want it to display "question number 3". Basically I want the user to know what number question they are on and it should be equivalent to the number of questions they've answered plus one.
Here's my code:
import UIKit
class ViewController: UIViewController {
var questionList = [String]()
func updateCounter() {
counter -= 1
questionTimer.text = String(counter)
if counter == 0 {
timer.invalidate()
wrongSeg()
}
}
func randomQuestion() {
//random question
if questionList.isEmpty {
questionList = Array(QADictionary.keys)
}
let rand = Int(arc4random_uniform(UInt32(questionList.count)))
questionLabel.text = questionList[rand]
//matching answer values to go with question keys
var choices = QADictionary[questionList[rand]]!
questionList.remove(at: rand)
//create button
var button:UIButton = UIButton()
//variables
var x = 1
rightAnswerBox = arc4random_uniform(4)+1
for index in 1...4
{
button = view.viewWithTag(index) as! UIButton
if (index == Int(rightAnswerBox))
{
button.setTitle(choices[0], for: .normal)
}
else {
button.setTitle(choices[x], for: .normal)
x += 1
}
}
randomImage()
}
let QADictionary = ["Who is Thor's brother?" : ["Atum", "Loki", "Red Norvell", "Kevin Masterson"], "What is the name of Thor's hammer?" : ["Mjolinr", "Uru", "Stormbreaker", "Thundara"], "Who is the father of Thor?" : ["Odin", "Sif", "Heimdall", "Balder"]]
//wrong view segue
func wrongSeg() {
performSegue(withIdentifier: "incorrectSeg", sender: self)
}
//proceed screen
func rightSeg() {
performSegue(withIdentifier: "correctSeg", sender: self)
}
//variables
var rightAnswerBox:UInt32 = 0
var index = 0
//Question Label
#IBOutlet weak var questionLabel: UILabel!
//Answer Button
#IBAction func buttonAction(_ sender: AnyObject) {
if (sender.tag == Int(rightAnswerBox))
{
rightSeg()
timer.invalidate()
print ("Correct!")
}
if counter != 0 {
counter = 15
}
else if (sender.tag != Int(rightAnswerBox)) {
wrongSeg()
print ("Wrong!")
timer.invalidate()
questionList = []
}
}
override func viewDidAppear(_ animated: Bool)
{
randomQuestion()
questionTimer.text = String(counter)
timer = Timer.scheduledTimer(timeInterval: 1, target:self, selector: #selector(ViewController.updateCounter), userInfo: nil, repeats: true)
}
//variables
var counter = 15
var timer = Timer()
#IBOutlet weak var questionTimer: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
Take counter for total number of question globally and update it in random question like
var answerdQuestion = 1;
func randomQuestion() {
//random question
if questionList.isEmpty {
questionList = Array(QADictionary.keys)
}
lblQuestionNumber.text = Strint(answerdQuestion)
answerdQuestion += 1
:
:
}

Swift: Missing Argument Error

Im having an issue here when trying to follow the first stanford lecture. I am assuming it is because I am using swift 2.... which was just recently released. (And the class is not) Below I show where I am getting errors. In the class the "enter" button on the calculator has the code #IBAction func enter() { but in mine it is #IBAction func enter(sender: UIButton) {
Is this something I did wrong when bringing the button into my code? Any ways to fix? Let me know if I can clarify anything.
import UIKit
class ViewController: UIViewController
{
#IBOutlet var display: UILabel!
var userIsInTheMiddleOfTypingANumber: Bool = false
#IBAction func appendDigit(sender: UIButton) {
let digit = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber {
display.text = display.text! + digit
} else {
display.text = digit
userIsInTheMiddleOfTypingANumber = true
}
}
#IBAction func operate(sender: UIButton) {
let operation = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber {
enter() // Here is my error: *Missing Argument for parameter on the line of code that says enter()
}
switch operation {
case "x":
if operandStack.count >= 2 {
displayValue = operandStack.removeLast() * operandStack.removeLast()
enter() // Here is my error: Again...
}
default: break
}
}
var operandStack = Array<Double>()
#IBAction func enter(sender: UIButton) {
userIsInTheMiddleOfTypingANumber = false
operandStack.append(displayValue)
print("operandStack = \(operandStack)")
}
var displayValue: Double {
get {
return NSNumberFormatter().numberFromString(display.text!)!.doubleValue
}
set {
display.text = "\(newValue)"
userIsInTheMiddleOfTypingANumber = false
}
}
}
enter requires an input parameter but you didn't supply it with any. Replace it with enter(sender)
#IBAction func operate(sender: UIButton) {
let operation = sender.currentTitle!
if userIsInTheMiddleOfTypingANumber {
enter(sender)
}
switch operation {
case "x":
if operandStack.count >= 2 {
displayValue = operandStack.removeLast() * operandStack.removeLast()
enter(sender)
}
default: break
}
}