How can I associate a string property with a UIButton in Swift? I don't want the string to appear as the button text, simply to be assigned to the button as an identifier or a key. Here is what I have so far:
func createAnswerButtons() {
var index:Int
for index = 0; index < self.currentQuestion?.answers.count; index++ {
// Create an answer button view
var answer:AnswerButtonView = AnswerButtonView()
selection.setTranslatesAutoresizingMaskIntoConstraints(false)
// Place into content view
self.scrollViewContentView.addSubview(answer)
// Add a tapped gesture recognizer to the button
let tapGesture:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: Selector("answerTapped:"))
answer.addGestureRecognizer(tapGesture)
// Add constraints etc
// Set the answer button text
let answerText = self.currentQuestion!.answers[index]
answer.setAnswerText(answerText)
// Set the identifier for each answer button
self.identifier = self.currentQuestion!.answerIdentifier[index]
// Add to the selection button array
self.answerButtonArray.append(answer)
}
So I think I need something after
// Set the identifier for each answer
self.identifier = self.currentQuestion!.answerIdentifier[index]
To assign the identifier to the button.
The reason for this is I'm trying to implement a decision tree logic so that I can keep track of each answer button that is tapped to generate a code string that will correspond to a final result.
Using the Objective-C runtime, we can add properties to classes at runtime:
extension UIButton {
private struct AssociatedKeys {
static var DescriptiveName = "nsh_DescriptiveName"
}
#IBInspectable var descriptiveName: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.DescriptiveName,
newValue as NSString?,
UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC)
)
}
}
}
}
Adding #IBInspectable also lets us set the descriptiveName property through Interface Builder.
For more about the Objective-C runtime, I recommend you check out this NSHipster article.
You can use the accessibility identifier (button.accessibilityIdentifier), if the button you want to identify should have a unique identifier (this matters if you're ever writing UI tests).
You can also subclass UIButton and add a variable buttonIdentifier.
class IdentifiedButton: UIButton {
var buttonIdentifier: String?
}
You can use accessibilityIdentifier property of UIButton.
#IBOutlet weak var button: UIButton!
button.accessibilityIdentifier = "Some useful text"
Use
button.accessibilityIdentifier = "some text"
istead of tag.
You can create an array with the strings you want associated with the button. Then set the buttons tag to the index of the string you want associated with the button. Hence:
var myStrings = ["First","Second","Third"]
button.tag = //insert a number corresponding to the string index in myStrings that you want for the button
func buttonPressed(sender: UIButton){
var selectedString = myString[sender.tag]
}
Related
Is there any way of sending string value to UIButton tag? I know tag is Int type but I need to send string value.
let myButton: UIButton = {
let button = UIButton()
button.tag = "123"
return button
}()
I need to send button.tag as a string
Create a custom class of UIButton and create one String type property like customTag
class CustomButton: UIButton {
var customTag:String = ""
}
Assign the custom class to your UIButton and use String type tag by following code.
let myButton: CustomButton = {
let button = CustomButton()
button.customTag = "ABC"
return button
}()
Instead of tag you can do it using accessibilityLabel.
btn.accessibilityLabel = "ABC"
One more answer.
Instead of accessibilityLabel / accessibilityIdentifier or any other related to accessibility , let us go for layer.name
Button.layer.name = "abc123"
#IBAction func wasPressed(_ sender: UIButton) {
print("was Pressed \(sender.layer.name)")
}
Reason:
Incase, we are in need to set accessibility for each and every screen, at that time it might affect.
But, if we are in need to add another layer for UIButton, it will not affect
Unfortunatly, the tag is just an integer value.
What you could do is:
Subclass UIButton and add your own property
Use associated objects, like in https://gist.github.com/mjjimenez/7956352
Use enums for the tag; maybe add an extension to access them
(Mis)use the accessibilityIdentifier
Just create extension of UIButton or NSObject.
extension NSObject {
private struct AssociatedKeys {
static var DescriptiveName = "strCustomTag"
}
var strCustomTag: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.DescriptiveName,
newValue as NSString?,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
}
}
}
Refer below code to set the value of strCustomTag
let myButton: UIButton = {
let button = UIButton()
button.strCustomTag = "ABC"
return button
}()
Get Value
print(view.strCustomTag)
If you create an extension of NSObject
strCustomTag is accessible from any class or any UI Elements such as
UIButton, UITextField, UIView etc.
No need to change in exsting storyboard OR XIB file
There is no way to set the tag to String because its type is Int.
Most probably you will no need to set it anyway because using the tag of UIView is not a common technique.
If you still want to use similar solution (I recommend not to) then you may use the accessibilityIdentifier property which is String.
Also you can create a custom subclass of UIButton with custom String property and to use this custom subclass instead of UIButton
You can create custom class for button then you can pass any kind of values by just define the property in the custom class.
Example: I want to pass the String and Int both values in Button So I will pass Int in to button tag and string will be passed in custom button property.
class CustomButton: UIButton {
var stringToPass: String = ""
}
ViewController Class
class HomeVC: UIViewController {
let customButton = CustomButton()
override func viewDidLoad() {
super.viewDidLoad()
customButton.stringToPass = "Your String"
}
}
Is there any way of sending string value to UIButton tag? I know tag is Int type but I need to send string value.
let myButton: UIButton = {
let button = UIButton()
button.tag = "123"
return button
}()
I need to send button.tag as a string
Create a custom class of UIButton and create one String type property like customTag
class CustomButton: UIButton {
var customTag:String = ""
}
Assign the custom class to your UIButton and use String type tag by following code.
let myButton: CustomButton = {
let button = CustomButton()
button.customTag = "ABC"
return button
}()
Instead of tag you can do it using accessibilityLabel.
btn.accessibilityLabel = "ABC"
One more answer.
Instead of accessibilityLabel / accessibilityIdentifier or any other related to accessibility , let us go for layer.name
Button.layer.name = "abc123"
#IBAction func wasPressed(_ sender: UIButton) {
print("was Pressed \(sender.layer.name)")
}
Reason:
Incase, we are in need to set accessibility for each and every screen, at that time it might affect.
But, if we are in need to add another layer for UIButton, it will not affect
Unfortunatly, the tag is just an integer value.
What you could do is:
Subclass UIButton and add your own property
Use associated objects, like in https://gist.github.com/mjjimenez/7956352
Use enums for the tag; maybe add an extension to access them
(Mis)use the accessibilityIdentifier
Just create extension of UIButton or NSObject.
extension NSObject {
private struct AssociatedKeys {
static var DescriptiveName = "strCustomTag"
}
var strCustomTag: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.DescriptiveName,
newValue as NSString?,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
}
}
}
Refer below code to set the value of strCustomTag
let myButton: UIButton = {
let button = UIButton()
button.strCustomTag = "ABC"
return button
}()
Get Value
print(view.strCustomTag)
If you create an extension of NSObject
strCustomTag is accessible from any class or any UI Elements such as
UIButton, UITextField, UIView etc.
No need to change in exsting storyboard OR XIB file
There is no way to set the tag to String because its type is Int.
Most probably you will no need to set it anyway because using the tag of UIView is not a common technique.
If you still want to use similar solution (I recommend not to) then you may use the accessibilityIdentifier property which is String.
Also you can create a custom subclass of UIButton with custom String property and to use this custom subclass instead of UIButton
You can create custom class for button then you can pass any kind of values by just define the property in the custom class.
Example: I want to pass the String and Int both values in Button So I will pass Int in to button tag and string will be passed in custom button property.
class CustomButton: UIButton {
var stringToPass: String = ""
}
ViewController Class
class HomeVC: UIViewController {
let customButton = CustomButton()
override func viewDidLoad() {
super.viewDidLoad()
customButton.stringToPass = "Your String"
}
}
I have a UIStackView object to which I have added a UILabel as the first subview. Here is what my code looks like:
func createStackView() -> UIStackView? {
var displayLabel = UILabel()
// Set properties of displayLabel
...
let myStackView = UIStackView(arrangedSubviews: [displayLabel, stackViewRow1, stackViewRow2]
return myStackView
}
Now, I wish to obtain a reference to the displayLabel object later on so I can modify it's text field.
The only way I could do this was somewhat of a hack:
var displayPanel = topStackView.arrangedSubviews[0]
if let displayLabel = displayPanel as? UILabel {
displayLabel.text = "Ready"
}
Is there a better way to do this ?
Store a reference to the label in a property:
var displayLabel: UILabel?
// then when you create the stack view
self.displayLabel = displayLabel
// to access it later
if let displayLabel = displayLabel {
displayLabel.text = "Ready"
}
If displayLabel should be not instance variable, then setting a tag value for it might be useful:
An integer that you can use to identify view objects in your
application.
What you can do:
// declaring a global constant for holding the label tag value
let displayLabelTageValue = 101
func createStackView() -> UIStackView? {
var displayLabel = UILabel()
// setting tag
displayLabel.tag = displayLabelTageValue
// Set properties of displayLabel
...
let myStackView = UIStackView(arrangedSubviews: [displayLabel, stackViewRow1, stackViewRow2]
return myStackView
}
And for getting identified displayLabel label:
if let displayLabel = view.viewWithTag(displayLabelTageValue) as? UILabel {
// displayLabel is wanted label!
} else {
// make sure that the tag value is matched
}
It depends on what your goals are.
If you want to set the text of the view in position 0 of your stack view to a specific string, the code you posted is the right way to go.
If you have a label that you want to be able to change regardless of it's position in the stack view then you should save a reference to it in an instance variable of your view controller, and then you can set it's text to a new string at any time.
Im suffering from sending two tags to a function to show UIActivityViewController and share the cell data, well before i used to send one value at a time and within a single UIButton:
cell.sharefb.tag = indexPath.row
cell.sharefb.addTarget(self, action: "showAlert:", forControlEvents:UIControlEvents.TouchUpInside)
But now I've implemented sections in my UITableView so my array is like :
Array[indexPath.section][indexPath.row]
One of them (section or row) is not enough at a time, i need to send both to my function ? how can i do that ?
func showAlert(sender:AnyObject){
// i want to use it like :
Array[sender.sectionvalue][sender.rowvalue]
}
You could subClass UIButton and add properties for the data you need eg
class MyButton : UIButton {
var row : Int?
var section : Int?
}
you can then set those properties, and in your showAlert function you can get them back and use:
func showAlert(sender:AnyObject){
let theButton = sender as! MyButton
let section = theButton.section
let row = theButton.row
}
Edit: Added where to set the button as per comment requested:
In your StoryBoard, make sure that your button is of type MyButton (and not UIButton anymore).
and then where you used to set the tag, don't use the tag but set the properties. So replace this code :
cell.sharefb.tag = indexPath.row
cell.sharefb.addTarget(self, action: "showAlert:", forControlEvents:UIControlEvents.TouchUpInside)
with:
cell.sharefb.row = indexPath.row
cell.sharefb.section = indexPath.section
cell.sharefb.addTarget(self, action: "showAlert:", forControlEvents:UIControlEvents.TouchUpInside)
I have created a custom UISegment Control
#IBDesignable class CardsSegmentedControl: UIControl {
private var labels = [UILabel]()
var thumbView = UIView()
var items: [String] = ["Saved Cards", "Add Card"] {
didSet {
setupLabels()
}
}
var selectedIndex : Int = 0 {
didSet {
displayNewSelectedIndex()
}
}
....
}
Now I wish to change the value of the variable selectedIndex in the viewController where I am adding this custom segment control in.
I guess it is a problem of how to access/change variables from another class.
I tried to create a class func which would set the value of the selectedIndex but I cannot get it to access the selectedIndex variable either.
Still pretty new to Swift so please bear with me.
// Inside your ViewController class, create a new instance of your custom class
var cardSegmentedControl = CardSegmentedControl()
// here, change its property value
cardSegmentedControl.selectedIndex = 1