How can I check the currentTitle of a UIButton in a switch? - swift

I have a UIButton which changes the title in different situations.
If you press the button the currentTitle should be checked by a Swift Switch (and certain Code executed).
The following isn't working:
#IBAction func button(_ sender: UIButton) {
switch sender.currentTitle {
case "":
//Code
case "OK":
//Code
default:
}
}
Xcode just shows "Expected Pattern" (1st cause) and "Expected Expression" (2nd case)

per the documentation, UIButton.currentTitle is of type String?. (optional type String)
Your switch statement is comparing an optional type to a non-optional type which is the reason you see errors.
Suggest first unwrapping the value before checking it's value with a switch like:
guard let title = sender.currentTitle() else { return }
switch title {
...
}

Every case in a switch statement must have a line of code. If you don't want to do anything you can use break:
#IBAction func button(_ sender: UIButton) {
switch sender.currentTitle {
case "":
//Code
case "OK":
//Code
default:
break
}
}

I solved the problem accidentally by adding questions marks
#IBAction func button(_ sender: UIButton) {
switch sender.currentTitle {
case ""?:
//Code
case "OK"?:
//Code
default:
break
}
}
I don't know why but it works nevertheless thanks for helping me.

Related

Catch inconsistent application state with Swift's `guard` statement?

I've a function in Swift like so:
#IBAction func doSomething(_ sender: AnyObject) { }
I need the sender to be of type NSMenuItem, so I check it with a guard:
guard let menuItem = sender as? NSMenuItem else { return }
But this will silently let the application continue if there is some serious error in my application logic, resulting in a different object type being passed.
Wouldn't it be better to just crash the application, rather than 'presenting' the user with a mysteriously non-working function?
What is the best way to check and react for these super-basic assumptions?
Note that you can write your #IBActions like this:
#IBAction func doSomething(_ sender: NSMenuItem)
sender does not have to be AnyObject.
In other situations though, if you want to crash with a guard statement, you could do:
guard ... else { fatalError("a message") }
fatalError returns Never, so it can be used in the else clause.

Storing sender.tag progress after view closes

I‘m coding an app, where a view pop ups and has a button which executes a different action every time it is clicked (see code). But after I close the view, the progress of the sender.tag is lost, and it starts from the beginning again.
I'd be really glad for some help from the community!
My first idea was that it has something to do with storing the data, but after some tries with string, int and so on, I couldn't really figure out how to store the sender.tag progress, so that if the view opens again, I don't have to go through the whole options again, but just start there where I left.
#IBAction func nextTask(_ sender: UIButton) {
sender.tag += 1
switch sender.tag {
case 1: print("Hello")
case 2: print("Yes")
default:
print("The End")
}
}
If you need to save some single value even after view controller is deinitialized (e.g. app is terminated, view controller is dismissed), you can save this value to UserDefaults for some key (actionID) and then you can get this value anytime and anywhere in your app by getting object for this key
Saving:
UserDefaults.standard.set(value, forKey: "key")
Getting:
var intValue = UserDefaults.standard.integer(forKey: "key")
#IBAction func nextTask(_ sender: UIButton) {
var actionID = UserDefaults.standard.integer(forKey: "actionID")
if actionID <= 2 {
actionID += 1
UserDefaults.standard.set(actionID, forKey: "actionID") // set increased `actionID`
}
switch actionID {
case 1: print("Hello")
case 2: print("Yes")
default: print("The End")
}
}
You can try creating a property in your class and use that to show the view
var counter = 0
#IBAction func nextTask(_ sender: UIButton) {
counter += 1
switch counter {
case 1: print("Hello")
case 2: print("Yes")
default:
counter = 0
print("The End")
}
}
So, next time once you click on the button on that view, the counter will have correct value.

Warnings from String interpolation

I've encountered a problem I can't solve myself. I have tried the Internet without any luck.
I'm still pretty new to Swift and coding, and right now following a guide helping me create an app.
Unfortunately, as I can understand, the app was written for Swift 3, and is giving me some issues since I'm using Swift 4.
I have to lines that gives me this warning:
String interpolation produces a debug description for an optional value; did you mean to make this explicit?
Use 'String(describing:)' to silence this warning Fix
Provide a default value to avoid this warning Fix
However, when I click one of Xcode's solutions I get another problem.
If I use the first fix, the app crashes and I get the following message:
Thread 1: Fatal error: Unexpected Segue Identifier;
If I use the second fix I have to assign a default value. And I don't know what this should be.
The whole passage of code is as follows.
It's the line starting with guard let selectedMealCell and the last one after default: that is causing the issues.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
switch(segue.identifier ?? "") {
case "AddItem":
os_log("Adding a new meal.", log: OSLog.default, type: .debug)
case "ShowDetail":
guard let mealDetailViewController = segue.destination as? MealViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
guard let selectedMealCell = sender as? MealTableViewCell else {
fatalError("Unexpected sender: \(sender)")
}
guard let indexPath = tableView.indexPath(for: selectedMealCell) else {
fatalError("The selected cell is not being displayed by the table")
}
let selectedMeal = meals[indexPath.row]
mealDetailViewController.meal = selectedMeal
default:
fatalError("Unexpected Segue Identifier; \(segue.identifier)")
}
}
So, the first suggested fix worked for you. It quieted the compile time warning, although admittedly String(describing:) is a weak solution.
In both cases, you need to unwrap the optional value. For the first case you should use:
guard let selectedMealCell = sender as? MealTableViewCell else {
if let sender = sender {
fatalError("Unexpected sender: \(sender))")
} else {
fatalError("sender is nil")
}
}
and in the second case:
fatalError("Unexpected Segue Identifier; \(segue.identifier ?? "")")
Then you got a runtime error:
"Unexpected Segue Identifier;"
That is telling you that your switch didn't match the first 2 cases and it ran the default case. The crash is caused because your code is explicitly calling fatalError. Your segue.identifier is apparently an empty string.
So your problem is actually in your Storyboard. You need to assign identifiers to your segues. Click on the segue arrows between your view controllers, and assign identifiers "AddItem" and "ShowDetail" to the proper segues. The segue identifier is assigned in the Attributes Inspector on the right in Xcode.
If you are prepared to write an small extension to Optional, it can make the business of inserting the value of an optional variable less painful, and avoid having to write optionalVar ?? "" repeatedly:
Given:
extension Optional: CustomStringConvertible {
public var description: String {
switch self {
case .some(let wrappedValue):
return "\(wrappedValue)"
default:
return "<nil>"
}
}
}
Then you can write:
var optionalWithValue: String? = "Maybe"
var optionalWithoutValue: String?
print("optionalWithValue is \(optionalWithValue.description)")
print("optionalWithoutValue is \(optionalWithoutValue.description)")
which gives:
optionalWithValue is Maybe
optionalWithoutValue is <nil>
You can also write print("value is \(anOptionalVariable)") -- the .description is redundant since print() uses CustomStringConvertible.description anyway -- but although it works you still get the annoying compiler warning.
You can use the following to automatically produce "nil" (or any other String) for nil values and for non-nil values use the description provided by CustomStringConvertible
extension String.StringInterpolation {
mutating func appendInterpolation<T: CustomStringConvertible>(_ value: T?) {
appendInterpolation(value ?? "nil" as CustomStringConvertible)
}
}
For your own types you have to conform to CustomStringConvertible for this to work:
class MyClass: CustomStringConvertible {
var description: String {
return "Whatever you want to print when you use MyClass in a string"
}
}
With this set up, you can simply use your optionals the same way as any other type, without any compiler warnings.
var myClass: MyClass?
myClass = MyClass()
print("myClass is \(myClass)")

How do I capture a button event type in swift

I'm having trouble finding information on how to capture touch events. Event.type and event.description does not help me, and the documentation does not have examples as far as I can tell.
I'm creating an IBAction on several buttons and want the (shared) action to deal with named buttons (sender.currentTitle) and (events.??) to add code for whether the button was tapped or long pressed.
#IBAction func ChangeSort(sender: UIButton, forEvent event: UIEvent!) {
// Trying to capture the button action? Type?? Description?
switch event.type {
case "Touch Up Inside":
// Switching on currenttitle works fine.
switch sender.currentTitle! {
case "Amount":
MyQueries.QppQueryOrderBy = "Order by Amount"
// Mylabel.text = "sss"
default: ...
}
case "Touch Down Repeat":
print("In long press")
switch sender.currentTitle! {
case "Amount":
MyQueries.QppQueryOrderBy = "Order by Amount desc"
default: ...
}
default:
print("in def")
switch sender.currentTitle! {
case "Amount":...
MyQueries.QppQueryOrderBy = "Order by LastActivityDate desc"
default:
MyQueries.QppQueryOrderBy = "Order by Amount desc"
}
}
self.theTableView.reloadData()
}
The UIEvent you are getting back is not going to tell you what you want. UIEvent will only tell you Touches, Motion, or Remote Control. You are looking for UIControlEvent which is pretty hard to get ahold of from within the iBAction. The more standard way to solve this problem would be to make multiple #IBAction func and assign the different types of control events to the different ones.
So for all of your named buttons, you would assign the touch up inside event to the same sortByTitle func.
#IBAction func sortByTitle(sender: UIButton) {
// This is associated with TouchUpInside in IB
switch sender.currentTitle! {
case "Amount":
MyQueries.QppQueryOrderBy = "Order by Amount"
// Mylabel.text = "sss"
default: ...
}
self.theTableView.reloadData()
}
#IBAction func sortByTitleDescending(sender: UIButton) {
//This is associated with Touch Down Repeat
print("In long press")
switch sender.currentTitle! {
case "Amount":
MyQueries.QppQueryOrderBy = "Order by Amount desc"
default: ...
}
self.theTableView.reloadData()
}
Basically if you were to create your button in code, when you initialized it, you would add a func for each of the control events you wanted to watch for using the addTarget method on UIControl/UIButton. In addTarget you specify a different func for each one. In IB you are just doing this in graphical form.

Get button pressed id on Swift via sender

So I have a storyboard with 3 buttons I want to just create 1 action for all those 3 buttons and decide what to do based on their label/id...
Is there a way to get some kind of identifier for each button?
By the way they are images, so they don't have a title.
#IBAction func mainButton(sender: UIButton) {
println(sender)
}
You can set a tag in the storyboard for each of the buttons. Then you can identify them this way:
#IBAction func mainButton(sender: UIButton) {
println(sender.tag)
}
EDIT: For more readability you can define an enum with values that correspond to the selected tag. So if you set tags like 0, 1, 2 for your buttons, above your class declaration you can do something like this:
enum SelectedButtonTag: Int {
case First
case Second
case Third
}
And then instead of handling hardcoded values you will have:
#IBAction func mainButton(sender: UIButton) {
switch sender.tag {
case SelectedButtonTag.First.rawValue:
println("do something when first button is tapped")
case SelectedButtonTag.Second.rawValue:
println("do something when second button is tapped")
case SelectedButtonTag.Third.rawValue:
println("do something when third button is tapped")
default:
println("default")
}
}
If you want to create 3 buttons with single method then you can do this by following code...Try this
Swift 3
Example :-
override func viewDidLoad()
{
super.viewDidLoad()
Button1.tag=1
Button1.addTarget(self,action:#selector(buttonClicked),
for:.touchUpInside)
Button2.tag=2
Button2.addTarget(self,action:#selector(buttonClicked),
for:.touchUpInside)
Button3.tag=3
Button3.addTarget(self,action:#selector(buttonClicked),
for:.touchUpInside)
}
func buttonClicked(sender:UIButton)
{
switch sender.tag
{
case 1: print("1") //when Button1 is clicked...
break
case 2: print("2") //when Button2 is clicked...
break
case 3: print("3") //when Button3 is clicked...
break
default: print("Other...")
}
}
You can create an outlet for your buttons and then implement:
#IBAction func mainButton(sender: UIButton) {
switch sender {
case yourbuttonname:
// do something
case anotherbuttonname:
// do something else
default: println(sender)
}
}
Swift 4 - 5.1
#IBAction func buttonPressed(_ sender: UIButton) {
if sender.tag == 1 {
print("Button 1 is pressed")
}
}
You have to set tag value to what you need and access it with
sender.tag
Assuming you gave them all proper names as #IBOutlets:
#IBOutlet var weak buttonOne: UIButton!
#IBOutlet var weak buttonTwo: UIButton!
#IBOutlet var weak buttonThree: UIButton!
You can use the following to determine which is which
#IBAction func didPressButton(sender: AnyObject){
// no harm in doing some sort of checking on the sender
if(sender.isKindOfClass(UIButton)){
switch(sender){
case buttonOne:
//buttonOne action
break
case buttonTwo:
//buttonTwo action
break
case buttonThree:
//buttonThree action
break
default:
break
}
}
Swift 3 Code:
In xcode Please set tag for each button first to work following code.
#IBAction func threeButtonsAction(_ sender: UIButton) {
switch sender.tag {
case 1:
print("do something when first button is tapped")
break
case 2:
print("do something when second button is tapped")
break
case 3:
print("do something when third button is tapped")
break
default:
break
}
}
You can do like this, just you have to give tag to all the buttons and do like this:
#IBAction func mainButton(sender: AnyObject)
{
switch sender.tag {
case 1:
println("do something when first button is tapped")
case 2:
println("do something when second button is tapped")
case 3:
println("do something when third button is tapped")
default:
println("default")
}
}
Use the outlets instead, tags clutter the code and make the readability way worse. Think about the poor developer that reads the code next and sees if sender.tag = 381 { // do some magic }, it just won't make any sense.
My example:
class PhoneNumberCell: UITableViewCell {
#IBOutlet weak var callButton: UIButton!
#IBOutlet weak var messageButton: UIButton!
#IBAction func didSelectAction(_ sender: UIButton) {
if sender == callButton {
debugPrint("Call person")
} else if sender == messageButton {
debugPrint("Message person")
}
}
[...]
}
You could also do this in a nice switch as well, which would make it even better.
Tested on Swift 5.1
Swift 4
add tag on button
let button = UIButton()
button.tag = 10
click event
#IBAction func mainButton(sender: UIButton) {
switch sender.tag {
case 10:
print("10")
case 11:
print("11")
default:
print("yes")
}
}
In this case you can use NSObject extension Accessibility Element UIAccessibility.
I have used accessibilityLabel and accessibilityIdentifier both are success in call and condition checking.
First
You can set a Accessibility Label or Identifier in the storyboard for each of the buttons in Identity inspector. Accessibility should be enabled.
To check/Identify button by
#IBAction func selectionPicker(_ sender: UIButton){
if sender.accessibilityLabel == "childType"{ //Check by accessibilityLabel
print("Child Type")
}
if sender.accessibilityIdentifier == "roomType"{ //Check by accessibilityIdentifier
print("Room Type")
}
performSegue(withIdentifier: "selectionViewSegue", sender:sender)
}
On Swift 3.2 and 4.0 with Xcode 9.0
Given the case you labeled your buttons "1", "2", "3":
#IBAction func mainButton(sender: UIButton) {
switch sender.titleLabel?.text {
case "1":
print("do something when first button is tapped")
case "2":
print("do something when second button is tapped")
case "3":
print("do something when third button is tapped")
default:
() // empty statement or "do nothing"
}
}
Swift 5.5
I have utility methods in other classes that do not have an instance of my ViewController, so I don't compare the sent objects to what is defined in the ViewController's IBOutlets.
I don't use tags if I can use a plain language identifier on my UI objects. I'd rather have plain language identifiers than numbers to identify my objects because it is easier for me. Just another way of doing it.
If I need to use a utility method, I set it up with a sender parameter so I can send the button and then figure out which button was clicked based on the assigned identity of the button within Storyboard.
For example:
class Utility {
func doSomething(sender: Any?) {
guard let button = sender as? NSButton else {
print("Unable to set button from sender.")
return
}
guard case buttonID = button.identifier?.rawValue else {
print("Unable to get button identifier.")
return
}
switch buttonID {
case: "firstButton":
_ = buttonID // Perform firstButton action
case: "secondButton":
_ = buttonID // Perform secondButton action
case: "thirdButton":
_ = buttonID // Perform thirdButton action
default:
// shouldn't get here - error?
}
}
}
In my ViewController I have the following buttons set up as IBOutlets and their identity is the same in Storyboard.
#IBOutlet weak var firstButton: NSButton?
#IBOutlet weak var secondButton: NSButton?
#IBOutlet weak var thirdButton: NSButton?
Then I have my IBActions:
#IBAction func firstButtonClicked(sender: Any?) {
utility.doSomething(sender: sender)
}
#IBAction func secondButtonClicked(sender: Any?) {
utility.doSomething(sender: sender)
}
#IBAction func thirdButtonClicked(sender: Any?) {
utility.doSomething(sender: sender)
}
In my case what i did, just like the answers above i used the tag to identify the specific button, what i added is that i added a UIButton extension that adds an id so that i can set a string id
i had three buttons with tags 0, 1 and 2
Then created the extension
extension UIButton {
var id: String {
let tag = self.tag
switch tag {
case 0:
return "breakfast"
case 1:
return "lunch"
case 2:
return "dinner"
default:
return "None"
}
}
}
When accessing a button in an IBAction i would just call:
sender.id
Select your first button and give it tag 0, and select second button and give it tag 1 and so on, in action check the tag bit and perform you functionalities on the basis of tag bit.:
switch sender as! NSObject {
case self.buttoneOne:
println("do something when first button is tapped")
case self.buttoneTwo:
println("do something when second button is tapped")
default:
println("default")
}