I am using xcode8+swift3.
I have multiple UITextField in my controller view. Each UITextField has a outlet connection in code.
I know I can use “tag” to identify UITextField, but it seems I can only use number as tag (I tried with string value for tag field, my Xcode always get stuck, only number as tag works).
But I don’t want to use magic number in my code like:
If (textField.tag == 0) {
}
I am wondering, is there a better way or more descriptive way in code to identify UITextField?
Tag is the correct tool. Just create an enum for them to track.
enum FieldIdentifier: Int {
case name = 0
case age = 1
}
if let fieldIdentifier = FieldIdentifier(rawValue: textField.tag) {
switch fieldIdentifier {
case .name: ...
case .age: ...
}
}
(Note that Larme's comment about using == is also appropriate, and if you already have outlets is better.)
Related
I have a Button labeled "3x3" now I want to create an Int variable with the value "3" in button click function.
I have tried
let newBoardSizeString = String.localizedStringWithFormat("\(sender.currentTitle?.characters.first)")
let newBoardSize = Int(newBoardSizeString)
The string i get is like this
"Optional(\"3\")"
and Int I get is
nil
I want only 3
This is an XY problem.
You ask: "How do I convert the first character of my button label to an Int."
The real answer is "Don't do that." It is a very bad idea to make the logic of your code depend on display information.
One simple solution is to use the tag property of your button and set the tag value to 3 in IB when you create the button:
let newBoardSize = sender.tag
Note that you either need to set your IBAction up with the sender as type UIButton (the better solution) or cast sender to the correct class in your IBAction.
Another approach is to set up outlets to your different buttons (e.g. threeButton and fourButton) and then in your IBAction, check which button is the sender and use a switch statement to figure out the appropriate value for each button:
func buttonTapped(sender: UIButton) {
switch sender {
case threeButton:
//Use the value 3
case fourButton
//Use the value 4
}
}
!
let newBoardSizeString = String.localizedStringWithFormat("\(sender.currentTitle?.characters.first!)")
let newBoardSizeString = String.localizedStringWithFormat("\(sender.currentTitle?.characters.first)")
if let unwrapped = Int(newBoardSizeString){
let newBoardSize = unwrapped
}
This works.
let newBoardSize = newBoardSizeString as! Int
Did you try ?
Int(newBoardSizeString)!
and make sure you are handling the nil before unwrapping the value.
I think this is impossible but I'm hoping someone knows a work around to use a unique int or string as a uibutton tag
Ideally I want something like this:
let uuid = NSUUID().UUIDString
myBTN.tag = uuid.toInt()!
It's frustrating swift only allows int as tags
If you'd like to assign a unique integer to the tag of the view as you've described you can do something like this:
myButton.tag = Int(arc4random())
That will give you a very random tag which is unlikely to collide.
This is almost certainly a poor solution to the broader context though. If adding a UUID is actually correct, consider subclassing UIButton and adding a unique identifier property instead of using the tag.
Consider, as #dimpiax suggests, storing references to your buttons as properties, most likely an array of buttons, rather than using identifiers and going back to find the related button later.
You can do a trick with a helper class and then, do it like this;
let uuid:Int = NSUUIDI.UUIDInt; // NSUUIDI() Not required here
myBTN.tag = uuid;
//Reverse (String value from tag)
println(NSUUIDI.UUIDStringFromInt(myBTN.tag));
Helper Class here;
//NSUUIDI.swift
class NSUUIDI:NSObject {
private static var _uUIDs:[String] = [String]();
class var UUIDInt:Int {
get {
let uUIDInt:Int = NSUUIDI._uUIDs.count;
NSUUIDI._uUIDs.append(NSUUID().UUIDString);
return uUIDInt;
}
}
class func UUIDStringFromInt(uUID:Int) -> String {
return (uUID < NSUUIDI._uUIDs.count) ? NSUUIDI._uUIDs[uUID]:"\(uUID)";
}
}
I have the problem, to have a high amount of buttons which have a number as their label, so i thought i could take the label as an integer instead of creating an action for every button?!
#IBAction func NumberInput(sender: UIButton) {
var input:Int = sender.titleLabel as Int
}
If you want to do this, you can convert the string to an Int by using string.toInt() such as:
if let input = sender.titleLabel?.text?.toInt() {
// do something with input
} else {
// The label couldn't be parsed into an int
}
However, I'd suggest either using UIView.tag or subclassing UIButton and adding an Int property to it to accomplish this, in case you ever change the display of your labels.
You should make sure that the text exists
var input:Int = (sender.titleLabel.text! as NSString).integerValue
You can't convert a UILabel to an Int. I think you want this instead:
var input : Int? = sender.titleLabel.text?.toInt()
Another way to convert a label in swift:
let num = getIntFromLabel(labelView)
connect all your buttons to 1 IBAction. then create the following variable and the set/get methods based on how you will use it.
note: "something" is a UILabel. The variable I wrote below should help you do conversions easily and with cleaner syntax. "newValue" comes with all setter methods. It basically takes into account any value that could possibly used to set "num" to a new value.
var num : Int {
get {
return Int(something!)!
}
set {
something.text = Int(newValue)
}
}
For Swift 3, what you can do is to directly convert it from an String input to an integer, like this
Int(input.text!)
And then, if for any reason, if you wish to print it out or return is as a String again, you can do
String(Int(input.text!)!)
The exclamation mark shows that it is an optional.
I know I am missing something obvious, but I just cannot see it. This method is meant to compare the text of a label to the text of a text box, and then delete the text. So if the 1st label reads "Puppy" and the 2nd label reads "Kittens," and the text box says "Kittens," the method should delete the text of the 2nd label and leave the 1st label's text. If the 2nd label is blank, then the method should delete the text of the 1st label.
But no matter how I mess with the method, either it deletes the 2nd label but not the 1st, deletes both of them, or deletes neither of them. Here's what I've tried
(lblFabric1 is the 1st label, lblFabric2 is the 2nd label, txtType is the text box):
-(IBAction)btnDelete:(id)sender
{
if ((self.lblFabric2.text== self.txtType.text))
{
self.lblFabric2.text = #"";
}
else if ((self.lblFabric2.text != self.txtType.text))
{
self.lblFabric1.text=#"";
}
}
It deletes the 2nd label, but not the 1st label. If I try to set the "Else if" to:
else if ((self.lblFabric2.text==#""))
it gives me an error (""Direct comparison of a string literal has undefined behavior.") Am I just going about this the wrong way? What am I missing?
You should not use == or != for string comparison in Objective C. You need to use the isEqualToString or isEqual method.
if (([self.lblFabric2.text isEqualToString:self.txtType.text]))
When you use == or != you are comparing the pointers where the strings are stored.
To compare NSStrings use:
if ([myString1 isEqualToString:myString2])
Documentation
Compairing String literals using == is not guaranteed to behave as you might expect. Use isEqual: or isEqualToString: instead.
See http://nshipster.com/equality/.
When you are comparing NSStrings with == what you are actually comparing are two memory addresses and that is not what you are really intended for. You want to compare the values of two strings what == operator is not suitable for and thus you are getting the warning
Direct comparison of a string literal has undefined behavior.
To compare the values of NSStrings you should use isEqualToString: method. You could have also use isEqual: method derived from NSObject class but that is not suitable for Unicode comparison. So isEqualToString: is always the safest bet.
After using isEqualToString: your code should look something like:
-(IBAction)btnDelete:(id)sender
{
if ([self.lblFabric2.text isEqualToString:self.txtType.text])
{
self.lblFabric2.text = #"";
}
else
{
self.lblFabric1.text=#"";
}
}
I have the following IBAction that is linked to several switch in my application. I would like to figure out which switch is clicked. Each UISwitch has a specific name. I want that name.
- (IBAction)valueChanged:(UISwitch *)theSwitch { //Get name of switch and do something... }
You can either use tags:
When you create the switches you need to set their tags.
- (IBAction)valueChanged:(UISwitch *)theSwitch {
switch(theSwitch.tag){
case 0:
{
//things to be done when the switch with tag 0 changes value
}
break;
case 1:
{
//things to be done when the switch with tag 0 changes value
}
break;
// ...
default:
break;
}
}
Or check if the switch is one of your controller properties
- (IBAction)valueChanged:(UISwitch *)theSwitch {
if(theSwitch == self.switch1){
//things to be done when the switch1 changes value
} else if (theSwitch == self.switch2) {
//things to be done when the switch2 changes value
}// test all the cases you have
}
The IBAction passes a pointer to the switch that performed the action. You can get any property off of it.
To compare switches:
- (void)valueChanged:(UISwitch *)theSwitch {
if ([theSwitch isEqual:self.switch1]) {
NSLog(#"The first switch was toggled!");
}
else if ([theSwitch isEqual:self.switch2]) {
NSLog(#"The second switch was toggled!");
}
else {
NSLog(#"Some other switch was toggled!");
}
}
I don't thank you can get the name of that switch. You could tag each of the switches, and use that tag to determine the name of the switch.
UISwitch doesn't have a name property. But you can subclass it and add a name property to the subclass. Then create switches from the subclass instead of UISwitch and give them a name when you init them.
#class MySwitch : UISwitch
#property (nonatomic, retain) NSString* name;
#end
Then the event handler can access them name string:
- (IBAction)valueChanged:(MySwitch *)theSwitch {
NSLog(#"switch %# value changed", theSwitch.name);
}
But I think the better answer is to use the tag field already there and use integer tags to identify the switches rather than a string. You can create enumeration constants in your code to name the tag values:
enum { SomeSwitch = 1, AnotherSwitch = 2, MainSwitch = 3 } _SwitchTags;
The best answer is the one #Moxy mentioned to compare the switch's pointer to your controller's properties to figure out which switch changed. That's what I do in my code. Tags and names are too error prone in the long run.