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

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.

Related

interact with button in UICollectionViewCell through main view controller

I'm making an app that allows a user to "pin" certain elements in a collection view I have implemented in my home ViewController class. To pin an element, the user must access a button that is part of my WordCell (UICollectionViewCell) class. However, when I try to press the button from my home view controller, nothing happens.
Here is all the relevant code and screenshots:
The star on the right hand side is the button inside the CollectionViewCell that I want the user to be able to push through the home view.
Below is all the relevant code in my ViewController class. I am using a delegate to pass the cell that was pressed into my home class ViewController. I also plan on passing more data back and forth between the cell (UICollectionCellView) class and ViewController in the future.
extension ViewController: UICollectionViewDataSource{
//......
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let wordCell = collectionView.dequeueReusableCell(withReuseIdentifier: wordCellReuseID, for: indexPath) as! WordCell
wordCell.delegate = self
wordCell.configure(word: tempWords[indexPath.item])
return wordCell
}
//......
}
extension ViewController: WordCellDelegate{
func star(wasPressedOnCell: WordCell){
print("touched")
if(wasPressedOnCell.isStarred){ //if the button has already been starred, unstar it
wasPressedOnCell.starButton.setImage(UIImage(named: "unfilled_star.png"), for: .normal)
wasPressedOnCell.isStarred = false
}
else{ //else, star the button
wasPressedOnCell.starButton.setImage(UIImage(named: "filled_star.png"), for: .normal)
wasPressedOnCell.isStarred = true
}
}
}
Here is relevant code in my class that conforms to UICollectionCellView:
//delegate setup for home class
protocol WordCellDelegate: class{
func star(wasPressedOnCell cell: WordCell) //parameter: cell that was pressed
}
//........
//button setup
starButton = UIButton()
starButton.setImage(UIImage(named: "unfilled_star.png"), for: .normal)
starButton.translatesAutoresizingMaskIntoConstraints = false
starButton.addTarget(self, action: #selector(starred), for: .touchUpInside)
contentView.addSubview(starButton)
//......
//button objective function
#objc func starred(){
print("touched")
delegate?.star(wasPressedOnCell: self)
//starredTapAction?() //chained back to main view controller
}
However, when I try to press the star on my home view controller screen, the objc function inside my UICollectionCellView class is not called. I've read from previous posts that this is most likely due to a hierarchy of classes and which view controls which objects, but I haven't been able to find a solution to this issue yet. I'm not sure what needs to be changed so the button inside the collection cell can be pressed through the view of the collection.
Please let me know if you need any more information, and thank you for reading this post!
Please disable cell selection of UIcollection view and your button touch event will fire, basically at a time you can either use collection view did select method or button action method
I had the exact same issue less than 2 weeks ago; I am still not sure about the root cause but I know the fix.
Move this line --
starButton.addTarget(self, action: #selector(starred), for: .touchUpInside)
inside your cell configuration method --
WordCell.configure (word: )
Add --
starButton.isEnabled = true
starButton.isUserInteractionEnabled = true
under your --
//button setup
only if the tap is not registering (ie if you can't see the button getting tapped)
It's really about setting the button target under cellForItemAt(); that'll do the trick.

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

About addTarget for a button inside a UITableViewCell

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.

Change Different Button in same Tableview Cell on Action (Swift)

I have two buttons within each TableView cell. When one button is tapped I want to change its appearance AND the appearance of the other button. I figured out how to change the tapped button using the approach outlined here, but am struggling with adjusting the other button.
Current relevant code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:FeedbackTableViewCell = self.feedbackTableView.dequeueReusableCell(withIdentifier: "cell") as! FeedbackTableViewCell
// Setup YES / NO Buttons
cell.feedbackYesButton.addTarget(self, action: #selector(MainSearchViewController.feedbackYesButtonTapped(sender:)), for: .touchUpInside)
cell.feedbackNoButton.addTarget(self, action: #selector(MainSearchViewController.feedbackNoButtonTapped(sender:)), for: .touchUpInside)
cell.feedbackYesButton.tag = indexPath.row
cell.feedbackNoButton.tag = indexPath.row
return cell
}
func feedbackYesButtonTapped (sender:UIButton) {
let yesButtonTag = sender.tag
switch yesButtonTag {
case 0:
// If YES button was not selected or was NO, then save value as YES and turn button "on", plus turn NO button "off".
turnFeedbackButtonOn(sender)
turnFeedbackButtonOff(NOT SURE HOW TO HANDLE THIS?)
}
// Other cases handled accordingly.
default:
return
}
}
//MARK: - Functions to change the appearances of feedback buttons
func turnFeedbackButtonOn(_ button: UIButton) {
button.setTitleColor(UIColor(red: 157/255, green: 249/255, blue: 88/255, alpha: 1 ), for: UIControlState())
button.titleLabel?.font = UIFont(name: "Avenir-Black", size: 18)
}
func turnFeedbackButtonOff(_ button: UIButton) {
button.setTitleColor(UIColor.black, for: UIControlState())
button.titleLabel?.font = UIFont(name: "Avenir", size: 17)
}
I tried passing the other button through with the target buttons, but I get an error when trying this. It feels like this should work, but I'm no expert at Swift so would appreciate any help!
cell.feedbackYesButton.addTarget(self, action: #selector(MainSearchViewController.feedbackYesButtonTapped(cell.feedbackYesButton, otherButton:cell.feedbackNoButton)), for: .touchUpInside)
func feedbackYesButtonTapped (sender:UIButton, otherButton:UIButton) {
//...
}
It would be a little easier if you handled the button events within the UITableViewCell's class instead, since you'd be able to easily reference the two buttons within there, but it's still possible to do what you want the way you're doing it:
First you'll want to get a reference to the cell after the button is pushed. It looks like you're setting the cell's row to be the button's tag, so I assume you've only got 1 section in that tableView. In that case you can get a reference to the cell by saying let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: button.tag, inSection: 0)). This return an optional, for obvious reasons, so you'll want to make sure to unwrap it safely. Then you can say turnFeedbackButtonOff(cell.feedbackNoButton) in the spot you were not sure how to handle it.

UIButton Action not working in swift

I have two classes UIVIewController and UITableviewcell. In UITableviewcell, i have a button and the button action should be in UIViewController. How can i pass a class and set delegate in button action.
i tried this code,
//UIVIewController
cell.createButton(self)
func buttonClick(object:AnyObject){
println("Button Clicked")
}
//UITableViewCell
func createButton(delegate:AnyObject){
//button created.
button.addTarget(delegate, action: "buttonClick:", forControlEvents: UIControlEvents.TouchUpInside)
}
But the above code is not working. 'buttonClick' function is not calling
I had the same problem a while ago, trying to put self in place of "delegated".
otherwise try to take a look at this question:
link
I hope to be helped.

Resources