About addTarget for a button inside a UITableViewCell - ios

I'm not able to understand why the addTarget in my UIButton is not working in a specific case.
So, there is a UITableViewCell where I create a button programmatically, like:
let myClickButton : UIButton = {
let button = UIButton()
button.setTitle("Hit Test", for: .normal)
button.tintColor = UIColor.white
button.addTarget(self, action: #selector(printMessage), for: .touchUpInside)
button.isUserInteractionEnabled = true
return button
}()
And, there is also the function in my UITableViewCell class that the button was supposed to be calling:
func printMessage(){
print("button was clicked")
}
However, the printMessage function is never called and there is no error in the console. Could you help me understand what is the problem on this case? It seems to be the problem of being in a UITableViewCell as I definitely tested it on a regular viewController and it worked fine.
Thanks a ton!

Depending on where the closure for the button is, I would try setting the target AFTER the UITableViewCell has been instantiated. Other than that, I'm not sure what the problem is without seeing more code.

Related

"addTarget" action function of UIButton not called (strikethrough)? [duplicate]

This question already has an answer here:
Why does Xcode line-out autocomplete methods for selector?
(1 answer)
Closed 2 years ago.
I have a UIButton within a UITableViewCell. When I call addTarget on the button and attempt to put a function in, the autocomplete has the function crossed out with a white line (see first image). Xcode still allows me to put the function in and run the app; however, when the button is tapped the function isn't called.
Button initialization:
private let infoButton: UIButton = {
let button = UIButton()
button.backgroundColor = .clear
button.adjustsImageWhenHighlighted = false
button.setImage(Images.sizeGuide, for: .normal)
button.addTarget(self, action: #selector(infoButtonPressed(_:)), for: .touchUpInside)
return button
}()
Function (infoButtonPressed):
#objc private func infoButtonPressed(_ sender: UIButton) {
print("Button Pressed")
}
Because I reuse this cell multiple times and only one of these cells needs to have this button, I have a variable that dictates whether or not to show the button:
var hasInfoButton: Bool = false {
didSet {
if hasInfoButton {
setupInfoButton()
}
}
}
The function that is called above simply sets up the button using autoLayout. Something to mention: when I tried calling addTarget in this function, the app crashed with Unrecognized selector sent to instance...
The tableView in which this is embedded in is only static and displays data. Therefore, allowSelection and allowsMultipleSelection are both set to false.
Does anyone have a solution to this?
You shouldn't need (_ sender: UIButton) the method should just be:
#objc func infoButtonPressed() {
print("button pressed")
}
EDIT:
The button initializer is also a little strange. The way I generally go about this kind of thing is like this:
let button = UIButton()
private func configureButton() {
button.backgroundColor = .clear
button.adjustsImageWhenHighlighted = false
button.setImage(Images.sizeGuide, for: .normal)
button.addTarget(self, action: #selector(infoButtonPressed(_:)), for: .touchUpInside)
}
Then call configureButton() in viewDidLoad()

Swift - Push to ViewController from UIButton in CollectionViewCell

I am trying to make my button, when tapped, to push to a new View Controller. I've tried many different ways but it won't trigger the function that I have it linked to. I also checked the 3D stack view of my layers and the button is on top and clickable, even when I check the background color, it's not being covered by anything else.
Does anyone have any ideas to what I am doing wrong?
For now I am trying to make the button print out the sentence in the console, however whenever I press it, the string doesn't pop up, so I haven't bothered to connect it to the view controller yet.
Also, I am coding this app without storyboards.
Here is my code below.
It is under the MainPageCell class declared as a UICollectionViewCell
private let playButton: UIButton = {
let button = UIButton()
button.setTitle("", for: .normal)
button.backgroundColor = .clear
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(MainPageCell.buttonTapped), for: .touchUpInside)
return button
}()
#objc func buttonTapped() {
print("I PRESSED THE BUTTON")
}
This line is wrong:
button.addTarget(self, action: #selector(MainPageCell.buttonTapped), for: .touchUpInside)
You cannot assign self as the action target in a property declaration initializer, because the instance designated by self does not exist yet. There is no error or warning (I regard that as a bug), but the action method is never called.
Move that assignment elsewhere and rewrite it, like this:
self.playButton.addTarget(self, action: #selector(MainPageCell.buttonTapped), for: .touchUpInside)
Maybe try defining your button action under the UIView Class, I've had a problem like that before, only worked when i linked it to the View Class, Good luck

UIButton change background when tapped (programmatically)

I have a custom UIButton but I am not able to make changing background or text color based on a quick tap. If something works, it's only on long press:
buton.isUserInteractionEnabled = true
buton.setTitle("text_normal", for: .normal)
buton.setTitle("text_highlighted", for: .highlighted)
buton.setTitle("text_selected", for: .selected)
buton.setTitle("text_focused", for: .focused)
The only text I can get is "text_highlighted" after holding the button ~1 second. But nothing happens on short tap.
But the action is triggered correctly:
let tap2 = UITapGestureRecognizer(target: self, action: #selector(handleTap))
buton.addGestureRecognizer(tap2)
#objc func handleTap(sender: UITapGestureRecognizer) {
print("click")
}
What I tried:
Adding custom
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
...
which is also triggered later. And combining with tocuhesEnded didn't change the color temporarily as well (maybe it was too quick).
I can't use UIButton(type: .system) as well - it's not system styled button.
Similar as with changing text this is not working as well:
self.setTitleColor(UIColor.gray, for: .normal)
self.setTitleColor(UIColor.yellow, for: .selected)
self.setTitleColor(UIColor.yellow, for: .highlighted)
self.setTitleColor(UIColor.yellow, for: .focused)
I can see yellow only when long pressed. I am looking for something like this, but it needs to work on quick tap as well.
Custom button is modifying layoutSubviews(), but not colors. Custom button contains default image and label. Whole button has rounded corners. But overall nothing special is in there.
I am not using any storyboard or XIB - everything is in Swift 4 programatically.
The button is supposed to lead to another ViewController, but I want to see the immediate feedback on click. Something like when created from storyboard: https://youtu.be/lksW12megQg?t=3m25s - not even simple alpha change works for me right now.
check isselected property of uibutton if button is selected then change the background color of button
Jen Jose was right - there was a problem with my parent class which was 'eating up' my touches. I confirmed this when moving it to different ViewController + I had the same issue with table, which couldn't handle touch events (https://stackoverflow.com/a/9248827/1317362)
EDIT:
To be precise - this button was in a UIScrollView.
Adding scrollView.delaysContentTouches = NO; solved the issue completely.
Details: https://stackoverflow.com/a/16650610/1317362

How do you fix the error "cannot assign value"?

I am just learning the swift basics and thought it would be a good idea that I try using my skills and a problem appeared. I have tried everything I know can someone help. My image below.
First of all please post text, not images
You have to use the (IBOutlet) instance number rather than the type UIButton and you have to use the proper API
number.setTitle(String(score), for: .normal)
But in an IBAction I'd declare the method with the static sender type (rather than unspecified Any) and use that
#IBAction func touched(_ sender : UIButton) {
score += 1
sender.setTitle(String(score), for: .normal)
}
If you want to change the title of a button, you need to do this:
button_Outlet_Name.setTitle(title: String?, for: UIControlState)
or
button_Outlet_Name.title.text = "New Title"
Remember to do this on your button OUTLET, not on the UIButton class
You cannot change the your button's title that way, what you have done there by writing
UIButton.title = String(score)
This means you are calling a static method of UIButton class and the name of the method is title.
If you want to change the button's tite you can do that the below way:
Step 1: Take a reference of your button by ctrl+drag.
Step 2: Inside your IBAction you need to write:
yourButton.setTitle("\(score)", for: .normal)
Here you are accessing the static method of the UIButton class. If you want to set the title, you need to do so on the instance.
Based on what you have, within the IBAction, you can cast the sender as a UIButton and then set the title. You’d also do that for a specific state since the titles are closely related to the the state for a UIButton.
if let btn = sender as? UIButton {
btn.setTitle(“\(score)”, forState: .normal)
}
You could have also used the IBOutlet reference instead.
number.setTitle(“\(score)”, forState: .normal)
Whenever you use \(variable) within a string, it uses the string value of the variable to be displayed in the string.
I'm not sure what are u trying to do but if you want to change the title of the clicked button u can do like this:
#IBAction func touched(_ sender: Any) {
score += 1
// check if the sender is UIButton
if let button = sender as? UIButton {
//change your button title
button.setTitle("\(scroe)", for: UIControlState.normal)
}
}

UIButton | setTitle taking huge amount of time even in main thread

I’m reopening this question because my last one wast flagged as duplicate even if it’s actually not ! It’s the same problem but the solutions are not working with my code. I’m using swift 2.
So my problem is, as the title says : I have a UIButton in a tableViewCell and when I use the method « setTitle », it takes from 10 to 60 seconds to update the title. At the same time I’m using « addTarget » and it works instantly. So the title should also update. My button is set as « custom » in my storyboard.
When the view is loading I’m running the following code :
/* viewDidLoad */
override func viewDidLoad() {
super.viewDidLoad()
boolAlready = false
findParticipation()
}
/* findParticipation */
func findParticipation() {
// After server request response :
boolAlready = true
self.tableView.reloadData()
}
/* cellForRowAtIndexPath */
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellActions = tableView.dequeueReusableCellWithIdentifier(informationsCellArray[indexPath.row], forIndexPath: indexPath) as ! eventDetailsAction
if boolAlready {
cellActions.foundParticipation
} else {
cellActions.btnParticipate.setTitle("...", forState: UIControlState.Normal)
cellActions.btnParticipate.addTarget(self, action: « deleteParticion", forControlEvents: .TouchUpInside)
}
/* In my custom cell */
func foundParticipation () {
self.btnParticipate.setTitle("Annuler", forState: UIControlState.Normal)
self.btnParticipate.addTarget(self, action: "deleteParticipation", forControlEvents: .TouchUpInside)
}
Different things I found on forums that didn’t worked :
Putting my settitle action around
dispatch_async(dispatch_get_main_queue()) {}
Setting title for all differents UIControlStates
Using setAttributedTitle()
Using self.btnParticipate.setNeedsLayout() and self.btnParticipate.layoutIfNeeded() after the setTitle
Disabling the button before and enable it after the setTitle
self.addSubview(self.btnParticipate)
Changing the title in titleLabel.text
Doing everything said previously in the parent viewController using cellActions.btnParticipate
UIView.performWithoutAnimation {
self.btnParticipate.setTitle("Annuler", forState: .Normal)
}
I’m now stuck and can’t find a solution for that.
You can try to refer to this answer https://stackoverflow.com/a/29633647/4478037
Make sure your button is a "custom" button and not a "system" button.
Try to wrap your setTitle call into performWithoutAnimation:
UIView.performWithoutAnimation {
self.btnParticipate.setTitle("Annuler", forState: .Normal)
}
Your issue is when you are creating the cell the btnParticipate function won't run properly you have to code all those conditions in func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) method
Try this
let cellActions = tableView.dequeueReusableCellWithIdentifier(informationsCellArray[indexPath.row], forIndexPath: indexPath) as ! eventDetailsAction
cellActions.removeTarget(nil, action: nil, forControlEvents: .AllEvents)
if boolAlready {
cellActions.btnParticipate.setTitle("Annuler", forState: UIControlState.Normal)
cellActions.btnParticipate.addTarget(self, action: "deleteParticipation", forControlEvents: .TouchUpInside)
} else {
cellActions.btnParticipate.setTitle("...", forState: UIControlState.Normal)
cellActions.btnParticipate.addTarget(self, action: « deleteParticion", forControlEvents: .TouchUpInside)
}
You can use
reloadRowsAtIndexPaths(_:withRowAnimation:)
to force a reload of the cell.
When the title changes that means that your code seems to be correct and its about rendering cycles. This would be one way to to reload the cell.
For reference: Apple Docs - UITableView reloadRowsAtIndexPaths
EDIT:
You have to call this method when some event gets fired, an requests finished etc. to update the cell. I had a second look at your code and it seems that your if-else in cell for row at index path is not doing what it should do. You should NOT call there any "action-performing-methods". Its just for initialization.
Your problem seems to be that your boolean boolAlready is the same for all cells. That will result in a never executing else-block. An therefore the title is not set, or its just set when the bool is set to false. You should probably print the bool there. And/or update your post with more information.
And its not good-readable code when you use nearly identical names like findParticipation and foundParticipation. You should probably refactor that.
So after 1 week of tries, I decided to stop searching...
I'm just calling findParticipation() synchronously and I'm not instantiating my tableView until I get foundParticipation() response.
I know it's way worse for the UX but it's still less time to wait.

Resources