Swift backgroundColor comparison not working - ios

When programmatically adding background color to UIButton to system green sender.backgroundColor = UIColor.systemGreen it sets the backgroundColor to SystemGreen, yet when I check it with this if/else statement
if(sender.backgroundColor == UIColor.systemGreen)
{
doSomething()
}
else
{
otherThing()
}
doSomething() is not called, and code goes to the else statement. Why?

I tried this
Step 1: Set the background of UIButton in viewdidload
#IBOutlet weak var currentBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
currentBtn.backgroundColor = UIColor.systemBrown
}
step 2: compare the two colours with help of cgcolour in the button action, its works fine for me, please check your end too.
#IBAction func checkButton(_ sender: UIButton) {
if let getColour = sender.backgroundColor, getColour.cgColor == UIColor.systemBrown.cgColor{
print("comes here")
}else {
print("not here")
}
}

Related

App crashes unless UISwitch has been moved once to change which Array is loaded

My app has a UISwitch and depending on the position of the switch the app will choose which array is needed. If I start my app and go to use it without moving the switch (leaving it on the isOn position) the app will crash. (Thread 1: Fatal error: Index out of range)
As long as I move it once it will then function as I had hoped. Here is the code:
import UIKit
var selectedRunesArray = [Rune]()
class SettingsViewController: UIViewController {
#IBOutlet weak var allowReversedRunesSwitch: UISwitch!
#IBOutlet weak var allowDuplicateRunesSwitch: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func reversedRunesChanged(_ sender: UISwitch) {
if allowReversedRunesSwitch.isOn == true {
selectedRunesArray = runesIncReversedArray
} else {
selectedRunesArray = runesArray
}
}
#IBAction func duplicateRunesChanged(_ sender: UISwitch) {
if allowDuplicateRunesSwitch.isOn == true {
} else {
}
}
}
I thought maybe the issue here was that I had started the app off with selectedRunesArray being empty so decided to give it an initial value as follows:
var seletcedRunesArray = runesIncReversedArray
This stopped the app crashing when you try to use it without moving the switch but now it won't change between the 2 Arrays as I want it to depending on the switch state.
reversedRunesChanged method will not be called until you change state of switch.
so, you just need to define which array you want to load at first time in viewdidload. you can either use:
this
selectedRunesArray = runesIncReversedArray
or
selectedRunesArray = runesArray
and also you can do it by getting switch state in viewdidload method by putting this in viewdidload
if allowReversedRunesSwitch.isOn == true {
selectedRunesArray = runesIncReversedArray
} else {
selectedRunesArray = runesArray
}

Swift 3 changing the title of a UIButton

I have been looking around the web and I am unable to find a solution for my problem. I want my "START" button text to change to "RESTART" once the game has ended. If any additional information is needed please let me know. Thanks, all help is appreciated. Below is my code.
class ViewController: UIViewController {
var gameOver = false
var stopFuncs = false
var gameTimer = Timer()
var counter = 10
var selected = "NONE"
var score = 0
var colour = "NONE"
#IBOutlet weak var colourLabel: UILabel!
let colourProvider = ColourProvider()
#IBOutlet weak var scoreLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
colour = colourProvider.randomColour()
print(colour)
colourLabel.text = colour
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var counterLabel: UILabel!
func updateCounter() {
if counter >= 0 {
counterLabel.text = String(counter)
counter -= 1
}
}
#IBAction func startButton() {
gameOver = false
counter = 10
stopFuncs = false
var _ = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true)
}
#IBAction func greenButton() {
if stopFuncs { return }
compareColours(selectedColour: "GREEN")
changeColourLabel()
colourLabel.textColor = UIColor.green
}
#IBAction func blueButton() {
if stopFuncs { return }
compareColours(selectedColour: "BLUE")
changeColourLabel()
colourLabel.textColor = UIColor.blue
}
#IBAction func yellowButton() {
if stopFuncs { return }
compareColours(selectedColour: "YELLOW")
changeColourLabel()
colourLabel.textColor = UIColor.yellow
}
#IBAction func pinkButton() {
if stopFuncs { return }
compareColours(selectedColour: "GREEN")
changeColourLabel()
colourLabel.textColor = UIColor.red
}
func compareColours(selectedColour: String) {
if gameOver == false && counter >= 1 {
if selectedColour == colour {
score += 1
let scoreString = String(score)
scoreLabel.text = scoreString
}
else {
gameOver = true
}
}
else {
stopFuncs = true
}
}
func changeColourLabel() {
colour = colourProvider.randomColour()
colourLabel.text = colour
print(colour)
}
}
You already have IBOutlets to some of your labels.
For buttons you may need to set up both an IBaction and an IBOutlet.
An IBOutlet is a thing. A noun. It's a reference to a view object.
An IBAction is a verb. An action. It's code that gets called when something happens to a UIControl (tapping a button, moving a slider, changing the selected value on a segmented control, etc.)
The IBaction is a function that gets called when the user interacts with a control (For buttons you almost always link the action to the .touchUpInside control event.)
Naming your actions with names like pinkButton is a bad idea, and leads to confusion. I suggest renaming your actions to verbs, like handlePinkButton (or perhaps "pinkButtonTapped", but I prefer action names to be verbs.)
Note that when you rename an IBAction and/or IBOutlet you have to go into IB (IB = Interface Builder), select the connections inspector, remove the now-incorrect IBOutlet/IBAction and control-drag from the object to the updated outlet/action.
If you don't do that you'll get very confusing crashes at runtime:
Renaming IBOutlets in code without also changing them in IB gives the error "this class is not key value coding-compliant for the key old_outlet_name" (where "old_outlet_name" will be the old name of your outlet.) when you try to load the view controller from the storyboard. Renaming actions )
Renaming IBActions in code without also changing them in IB gives an error "unrecognized selector sent to instance big_hex_number" when you try to tap the button (or trigger the action for other controls.)
You should open your storyboard in the main editor and open the source in the assistant editor, then control-drag from your button in IB into your source code just below your other outlet at the top of your class.
Your button needs to be an IBOutlet rather than an IBAction. When you ctrl-drag from Interface Builder to your code, make sure you select the 'Outlet' option as shown below:
Then you can change the button title like this:
else {
startButton.setTitle("Button Title", for: .normal)
stopFuncs = true
}

Could not cast value of type 'UIView' (0x10d54a4c0) to 'UIButton' (0x10d552120)

there was a question like this already made but I tried the suggestions and it didn't work for me still. :/
I am trying to make a quiz app using this tutorial: https://www.youtube.com/watch?v=KNAQ3Y8PGkM&t=1s
Here is a link to download my project: https://www.dropbox.com/s/bwvg6fyrrzudued/kat%20quiz.zip?dl=0
Everything is perfect except I get the error:
Could not cast value of type 'UIView' (0x10d54a4c0) to 'UIButton' (0x10d552120)
I'd really appreciate some help. I tried everything in my knowledge and searching, thank you. :)
import UIKit
class ViewController: UIViewController {
let questions = ["What color is Courage the Cowardly Dog?", "What is the name of the woman that lives with Courage?", "What is the name of the man that lives with Courage?"]
let answers = [["Purple","Grey","Orange"],["Muriel", "Angela", "Elizabeth"], ["Eustace", "Robert", "Ezio"]]
//Variables
var currentQuestion = 0
var rightAnswerPlacement:UInt32 = 0
var points = 0
//Label
#IBOutlet weak var lbl: UILabel!
//Button
#IBAction func action(_ sender: AnyObject) {
if (sender.tag == Int(rightAnswerPlacement))
{
print ("RIGHT!")
points += 1
}
else
{
print ("WRONG!!!!!!")
}
if (currentQuestion != questions.count)
{
newQuestion()
}
else{
performSegue(withIdentifier: "showScore", sender: self)
}
}
override func viewDidAppear(_ animated: Bool) {
newQuestion()
}
//Function that displays new question
func newQuestion(){
lbl.text = questions[currentQuestion]
rightAnswerPlacement = arc4random_uniform(3)+1
//Create a button
var button:UIButton = UIButton()
var x = 1
for i in 1...3
{
//Create a button
button = view.viewWithTag(i) as! UIButton
if (i == Int(rightAnswerPlacement))
{
button.setTitle(answers[currentQuestion][0], for: .normal)
}
else
{
button.setTitle(answers[currentQuestion][x], for: .normal)
x = 2
}
}
currentQuestion += 1
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Did you create UI Button in your Story Board ??
In your youtube video, watch 1:45
And remember to set your 3 UI Button's tag to 1, 2 and 3
watch 3:20
Your problem occurs because you've set your View's tag = 1.
You can fix it easily by change View's tag value to a number != 1, 2 and 3
Because your ViewController have 2 view with the same tag = 1. (view and UI Button).
So when you use view.viewWithTag(i), It accidentally select view not UI Button. So it can not convert to UI Button type
P/s: next time, if you want to select your Button directly, you can make an #IBOutlet for that Button as you make with your Label. It will not cause confusing error like that

Loading a switch state from NSUserDefault on load

I am working on a settings view for a basic app. Basic, in there is just one switch in the settings view for the user. The switch setting is saved with NSUserDefault. I use a delegate to send the switch signal from the settings view to the main view. The delegation works properly.
The UI is basic. On the main view, a label will read On in green (if the switch is on) and Off in red (if the switch is off.) There is a setting button in the top right that will segue (settingsSegue) to the settings UITableViewController, where the UISwitch is located.
The problem is loading up the NSUserDefault once the app loads. In viewDidLoad, I check to see if there's a value saved for the switch key. If there is, load it up. If not, set it to false (in the storyboard, the switch is set to false as default.) The Switch Status loads up as Off every time. Even if the default value is On. This shouldn't be happening.
ViewController.swift:
import UIKit
var nsDefaults = NSUserDefaults.standardUserDefaults()
class ViewController: UIViewController, SettingsViewControllerDelegate {
var onFromMain = Bool()
#IBOutlet weak var switchStateLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
if let mySavedKey = nsDefaults.objectForKey("savedSwitchSettingDefault") {
// A value exists. Load it up.
nsDefaults.objectForKey("savedSwitchSettingDefault")
print("The switch is set! \(mySavedKey)")
checkSwitchState()
}
else {
// Nothing stored in NSUserDefaults yet. Set switch to False.
nsDefaults.setBool(false, forKey: "savedSwitchSettingDefault")
checkSwitchState()
}
}
func myVCDidFinish(controller: SettingsViewController, switchState: Bool) {
onFromMain = switchState.boolValue
checkSwitchState()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "settingsSegue" {
let nav = segue.destinationViewController as! UINavigationController
let secondVC = nav.topViewController as! SettingsViewController
secondVC.delegate = self
}
}
func checkSwitchState() {
if onFromMain {
switchStateLabel.text = "On"
switchStateLabel.textColor = UIColor.greenColor()
}
else {
switchStateLabel.text = "Off"
switchStateLabel.textColor = UIColor.redColor()
}
}
}
SettingsViewController.swift:
import UIKit
protocol SettingsViewControllerDelegate {
func myVCDidFinish(controller: SettingsViewController, switchState: Bool)
}
class SettingsViewController: UITableViewController {
var delegate: SettingsViewControllerDelegate? = nil
#IBOutlet weak var switchOutlet: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
switchOutlet.on = nsDefaults.boolForKey("savedSwitchSettingDefault")
}
#IBAction func closeSettingsPageBarButtonItemPressed(sender: UIBarButtonItem) {
if (delegate != nil) {
delegate!.myVCDidFinish(self, switchState: switchOutlet.on)
self.dismissViewControllerAnimated(true, completion: nil)
}
}
#IBAction func switchPressed(sender: UISwitch) {
// Tap the switch to change the setting.
nsDefaults.setBool(switchOutlet.on, forKey: "savedSwitchSettingDefault")
}
}
I believe my problem lies somewhere in loading up the default key for "savedSwitchSettingDefault". Is this correct? Or does the issue lie elsewhere in the code?
You can tidy things up quite a bit by relying on the fact that the default you want is false and that boolForKey gives you false when the key isn't present.
Also, by accessing the setting in viewWillAppear you can avoid the need for the delegate callback.
ViewController.swift
import UIKit
class ViewController: UIViewController {
let nsDefaults = NSUserDefaults.standardUserDefaults()
var onFromMain = false
#IBOutlet weak var switchStateLabel: UILabel!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.onFromMain = self.nsDefaults.boolForKey("savedSwitchSettingDefault")
self.checkSwitchState()
}
func checkSwitchState() {
if self.onFromMain {
switchStateLabel.text = "On"
switchStateLabel.textColor = UIColor.greenColor()
}
else {
switchStateLabel.text = "Off"
switchStateLabel.textColor = UIColor.redColor()
}
}
}
SettingsViewController.swift:
import UIKit
class SettingsViewController: UITableViewController {
let nsDefaults = NSUserDefaults.standardUserDefaults()
#IBOutlet weak var switchOutlet: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.switchOutlet.on = self.nsDefaults.boolForKey("savedSwitchSettingDefault")
}
#IBAction func closeSettingsPageBarButtonItemPressed(sender: UIBarButtonItem) {
self.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func switchPressed(sender: UISwitch) {
// Tap the switch to change the setting.
self.nsDefaults.setBool(self.switchOutlet.on, forKey: "savedSwitchSettingDefault")
}
}
When retrieving the bool value from User Defaults, boolForKey will return false if the value doesn't exist. So in this case there's no need for unwrapping. From the documentation:
If a boolean value is associated with defaultName in the user defaults, that value is returned. Otherwise, false is returned.
If the value is getting set (you are sure of it), and the behavior of the app is not working correctly your problem might lie elsewhere.
I would recommend using another approach, declare your "onFromMain" as an optional boolean, then unwrap it when you need it.
var onFromMain: Bool?
...
func checkSwitchState() {
//- This will unwrap your optional or set false if its nil
let switchSate = onFromMain ?? false
//- Then you can set the values based on the value (or the default false)
switchStateLabel.text = switchState ? "On" : "Off"
switchStateLabel.textColor = switchState ? UIColor.greenColor() : UIColor.redColor()
}
Then attach the debugger with a breakpoint and see if the value is being unwrapped or if its defaulting to false.
Also, you are setting your delegate only when the segue is called, depends of the scenario, and if i understand you correctly, you migt not get the value until you have actually navigated to the settings view. So when opening the app (without navigating to the settings view) the onFromMain will never get populated.
Alternatively you can fetch the value on the view did load method to get it straight away when you load the app.

my sender.tag switch statement does not work

My switch doesn't work, when i press the first button which is "buttonAclicked" a label is showing which is also what i wanted but when i try to click on the button with the tag 3 i get a SIGABRT error which crashes the whole thing.
I know that the button with the tag 3 works since i have made an IBAction for itself where it printed out:
">"
but when i set it into the switch statement it does not work, nothing is being printed besides errors and the other prints but not the last print
func buttonAclicked(sender: UIButton) {
print("button A was pressed")
label.hidden = false
label.setNeedsDisplay()
if(sender.isKindOfClass(UIButton)) {
print("hey")
}
switch sender.tag {
case 3 :
print("buuuutttttooonnn")
break
default :
label.text = "0"
}
}
create proper Button IBAction and connect to storyboard proper. it should solve your SIGABRT error.
your error because of not connect IBAction Properly to Storyboard
#IBOutlet weak var buttonOutlat: UIButton! // creat Button Outlet hera
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
buttonOutlat.tag = 3 // <- you can set sender.tag here
}
#IBAction func buttonAclicked(_ sender: UIButton) { // creat proper Button Action
print("button A was pressed")
switch sender.tag{
case 3 :
print("buuuutttttooonnn---3")
label.text = "button-3"
break
default :
label.text = "button-default"
}
}

Resources