Value changes on scroll UITableView - ios

I have a UITableView with a button in that's toggled depending on whether a user 'favorites' a post or not. Everything works well, except for when the table view is scrolled, the button changes. Here's my code:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let feed = self.feed else {
return 0
}
return feed.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if feed!.count > 9 {
if indexPath.row == feed!.count - 1 {
self.loadMorePosts()
}
}
if hasImageAtIndexPath(indexPath) {
return imageCellAtIndexPath(indexPath)
} else {
return basicCellAtIndexPath(indexPath)
}
}
func hasImageAtIndexPath(indexPath:NSIndexPath) -> Bool {
let post = self.feed?[indexPath.row]
if post?.image?.isEmpty == false {
return true
}
return false
}
func imageCellAtIndexPath(indexPath:NSIndexPath) -> PostCellImage {
let cell:PostCellImage = self.tableView.dequeueReusableCellWithIdentifier("imageCell", forIndexPath: indexPath) as! PostCellImage
if let post = self.feed?[indexPath.row] {
let likedPost = post.hasFavorited
if likedPost == true {
if let favoriteCount = post.favoriteCount {
let count = String(favoriteCount)
cell.likeButton.setTitle(count, forState: .Normal)
cell.likeButton.setImage(UIImage(named: "liked"), forState: .Normal)
cell.likeButton.setTitleColor(UIColorFromRGB("A61224"), forState: .Normal)
cell.likeButton.addTarget(self, action: "unfavoritePost:", forControlEvents: UIControlEvents.TouchUpInside)
cell.likeButton.tag = post.id!
}
} else {
if let favoriteCount = post.favoriteCount {
let count = String(favoriteCount)
cell.likeButton.setTitle(count, forState: .Normal)
cell.likeButton.addTarget(self, action: "favoritePost:", forControlEvents: UIControlEvents.TouchUpInside)
cell.likeButton.tag = post.id!
}
}
}
return cell
}
Favorited Posts Array
var favoritedPosts = [Int]()
Table View
if let likedPost = post.hasFavorited {
if likedPost == true {
self.favoritedPosts.append(indexPath.row)
print(self.favoritedPosts)
}
}
if self.favoritedPosts.contains(indexPath.row) {
let count = String(post.favoriteCount)
cell.likeButton.setTitle(count, forState: .Normal)
cell.likeButton.setImage(UIImage(named: "liked"), forState: .Normal)
cell.likeButton.setTitleColor(UIColorFromRGB("A61224"), forState: .Normal)
cell.likeButton.addTarget(self, action: "unfavoritePost:", forControlEvents: UIControlEvents.TouchUpInside)
cell.likeButton.tag = post.id!
} else {
let count = String(post.favoriteCount)
cell.likeButton.setTitle(count, forState: .Normal)
cell.likeButton.addTarget(self, action: "favoritePost:", forControlEvents: UIControlEvents.TouchUpInside)
cell.likeButton.tag = post.id!
}

In your UITableViewCell PostCellImage subclass you should override prepeareForReuse function - to turn cell to default mode.
Swift:
override func prepareForReuse() {
super.prepareForReuse()
//set cell to initial state here
//set like button to initial state - title, font, color, etc.
}

This might be cased by the table view cell reuse. It turns out that your code set the likeButton.image when the post is a favorited post but did not remove the image when the post is not a favorited one. So when the first time each cell is loaded into tableView every thing works fine. However, when scrolling the tableView, when those cells with favorite image set moves out of the screen area, they will be reused for the cell scrolling in. Thus if this kind of cell is reused by a post that is even not favorited, the image will still be there.
There is a prepareForReuse method for UITableViewCell, it gives you the chance to reset the contents before a cell is being reused.

You need to completely reset the appearance for normal case here.
Reason is you can't be sure which state of the button you get when
cells are reused.
When you receive a dequeued cell, it might have a
button with 'liked'/'not-liked' state.
Completing the appearance with two commented out lines in the else clause will resolve your problem.
if self.favoritedPosts.contains(indexPath.row) {
let count = String(post.favoriteCount)
cell.likeButton.setTitle(count, forState: .Normal)
cell.likeButton.setImage(UIImage(named: "liked"), forState: .Normal)
cell.likeButton.setTitleColor(UIColorFromRGB("A61224"), forState: .Normal)
cell.likeButton.addTarget(self, action: "unfavoritePost:", forControlEvents: UIControlEvents.TouchUpInside)
cell.likeButton.tag = post.id!
} else {
let count = String(post.favoriteCount)
cell.likeButton.setTitle(count, forState: .Normal)
// Uncomment these two lines and add proper values for image / color to resolve your problem
// cell.likeButton.setImage(UIImage(named: "not-liked-yet"), forState: .Normal)
// cell.likeButton.setTitleColor(UIColorFromRGB("A67832"), forState: .Normal)
cell.likeButton.addTarget(self, action: "favoritePost:", forControlEvents: UIControlEvents.TouchUpInside)
cell.likeButton.tag = post.id!
}
Hope this helps.

You can try prepareForReuse or try to set unlike image in the case likedPost == false

Related

Can not set set image for custom button

I'm going to implement a check box like this:
by
class CheckBox: UIButton {
let checkedImage = UIImage(named: "checked")
let uncheckedImage = UIImage(named: "unchecked")
var checked : Bool = false{
didSet{
if checked == false{
self.setImage(uncheckedImage, forState: .Normal)
}else {
self.setImage(checkedImage, forState: .Normal)
}
}
}
override func awakeFromNib() {
self.addTarget(self, action: "buttonClicked:", forControlEvents: .TouchUpInside)
checked = false
}
func buttonClicked(sender: UIButton){
if (sender == self){
checked = !checked
}
}
}
but everything I got is:
Could you explain what happened?
i tried your code it's works fine i only changed this part self.addTarget... to:
self.addTarget(self, action: #selector(CheckBox.buttonClicked(_:)), forControlEvents: .TouchUpInside)
and when you create the button change it class to: CheckBox
Call self.setNeedsLayout() at the end of your didSet statement.
If you change any of subviews you need to ask it to update it's layout and children. Let me know if that helped.

Swift button press copy text

I have a button inside a cell inside a collection view, everything works fine except for the copy function I'm trying to create. When I click on the button the text is not copied or in my test case the text is not printed to console.
cell.buttonViewLink.addTarget(self, action: "buttonViewLinkAction:", forControlEvents: UIControlEvents.TouchUpInside)
func buttonViewLinkAction(sender:UIButton!) {
print("Button tapped")
}
In my storyboard I have a touch up inside action linked from the button to the view (I created this with ctrl-drag form the button to view). Everything looks OK but yet when I press the button nothing happens, doesn't crash either as things look OK.
What am I missing?
do like
Copy
func buttonViewLinkAction(sender:UIButton!) {
print("Button tapped")
UIPasteboard.generalPasteboard().string = yourstring!.text() // or use sender.titleLabel.text
}
paste or Retrieve
func GetCopiedText(sender: UIButton!) {
if let myString = UIPasteboard.generalPasteboard().string {
print(myString)
}
}
Update
func buttonViewLinkAction(sender:UIButton!) {
print(sender.currentTitle)
print(sender.titleLabel.text)
}
update-2
you written the buttonViewLinkAction code in inside the func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) its never call. so remove the buttonViewLinkAction and add outside the method of data source
//fontawesome link
cell.buttonViewLink.setTitle(String.fontAwesomeIconWithName(.Link), forState: .Normal)
cell.buttonViewLink.titleLabel?.font = UIFont.fontAwesomeOfSize(20)
cell.buttonViewLink.addTarget(self, action: "buttonViewLinkAction:", forControlEvents: UIControlEvents.TouchUpInside)
func buttonViewLinkAction(sender:UIButton!) {
print("Button tapped")
UIPasteboard.generalPasteboard().string = "Label text"
}
Final Answer
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CellClass
let face = self.faces[indexPath.item]
//set image and align center
if let imageURL = NSURL(string:face.image) {
cell.imageView.sd_setImageWithURL(imageURL)
} else {
cell.imageView.image = self.placeholderImage
}
//set name
if let imageNAME: String = String(face.name){
cell.labelView.text = (imageNAME .uppercaseString)
} else {
cell.labelView.text = "oops name"
}
//set border
cell.layer.shadowOffset = CGSizeMake(0, 1)
cell.layer.shadowColor = UIColor.blackColor().CGColor
cell.layer.shadowRadius = 1
cell.layer.shadowOpacity = 0.1
cell.clipsToBounds = false
let shadowFrame: CGRect = (cell.layer.bounds)
let shadowPath: CGPathRef = UIBezierPath(rect: shadowFrame).CGPath
cell.layer.shadowPath = shadowPath
//square background button
cell.buttonViewSquare.backgroundColor = UIColor(red: 249/255, green: 249/255, blue: 249/255, alpha: 1)
cell.buttonViewSquare.enabled = false
//fontawesome link
cell.buttonViewLink.setTitle(String.fontAwesomeIconWithName(.Link), forState: .Normal)
cell.buttonViewLink.titleLabel?.font = UIFont.fontAwesomeOfSize(20)
cell.buttonViewLink.addTarget(self, action: "buttonViewLink:", forControlEvents: UIControlEvents.TouchUpInside)
// or use like cell.buttonViewLink.addTarget(self, action: "buttonViewLinkAction:", forControlEvents: UIControlEvents.TouchUpInside)
//fontawesome heart
cell.buttonViewHeart.setTitle(String.fontAwesomeIconWithName(.Heart), forState: .Normal)
cell.buttonViewHeart.titleLabel?.font = UIFont.fontAwesomeOfSize(20)
cell.buttonViewHeart.setTitleColor(UIColor.redColor(), forState: UIControlState.Normal)
//fontawesome share
cell.buttonViewShare.setTitle(String.fontAwesomeIconWithName(.Share), forState: .Normal)
cell.buttonViewShare.titleLabel?.font = UIFont.fontAwesomeOfSize(20)
return cell
}
call method like
func buttonViewLink(sender:UIButton!) {
print("Button tapped")
UIPasteboard.generalPasteboard().string = sender.titleLabel.text
}
or use directly already you have a function
#IBAction func buttonViewLinkAction(sender: UIButton) {
print("Button tapped")
UIPasteboard.generalPasteboard().string = sender.titleLabel.text
}
modified Answer
add tag in here cell.buttonViewLink.tag = indexPath.item
cell.buttonViewLink.setTitle(String.fontAwesomeIconWithName(.Link), forState: .Normal)
cell.buttonViewLink.titleLabel?.font = UIFont.fontAwesomeOfSize(20)
cell.buttonViewLink.addTarget(self, action: "buttonViewLinkAction:", forControlEvents: UIControlEvents.TouchUpInside)
cell.buttonViewLink.tag = indexPath.item
and call the methof like
#IBAction func buttonViewLinkAction(sender: UIButton) {
print("Button tapped")
let face = self.faces[sender.tag]
if let imageNAME: String = String(face.name){
print(imageNAME .uppercaseString)
}
if let imageURL = NSURL(string:face.image) {
print(imageURL)
}
UIPasteboard.generalPasteboard().string = sender.titleLabel.text
}
Update Swift 4.2
override func viewDidLoad() {
super.viewDidLoad()
label.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(copyValuePressed)))
}
#objc func copyValuePressed() {
UIPasteboard.general.string = label.text
}
You just posted your code. The problem is you are doing it inside function. Please take it out of it and it is working well.
Why you are not using it like this:
cell.buttonViewLink.setTitle(String.fontAwesomeIconWithName(.Link), forState: .Normal)
cell.buttonViewLink.titleLabel?.font = UIFont.fontAwesomeOfSize(20)
cell.buttonViewLink.addTarget(self, action: "buttonViewLinkAction:", forControlEvents: UIControlEvents.TouchUpInside)
func buttonViewLinkAction(sender:UIButton!) {
print("Button tapped")
UIPasteboard.generalPasteboard().string = "Label text"
}
You have two duplicated buttonViewLinkAction functions!
When you press the button, this function will be called:
#IBAction func buttonViewLinkAction(sender: UIButton) {
}
instead of this
func buttonViewLinkAction(sender:UIButton!) {
print("Button tapped")
}
Remove the second function and write your code in the first function.
Also, you don't need to write this line:
cell.buttonViewLink.addTarget(self, action: "buttonViewLinkAction:", forControlEvents: UIControlEvents.TouchUpInside)
if you have already linked the button to the function (In the storyboard).

How to toggle a Button Inside a table View

I have a custom cell in my table view which contain a button and name of audio file. when i am tapping in button the relevant audio is playing and when tapping again it audio stops. My problem is when i am tapping the same button it is ok but when i am tapping in any button different then current cell then my process is not working. Can any one show me the right direction to do this . My code is below . Thanks in advance..
var isAudioPlaying = false
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return designCellAtIndexPath(indexPath)
}
private func designCellAtIndexPath(indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = NSBundle.mainBundle().loadNibNamed("ExtendedDesign", owner: self, options: nil)[4] as! UITableViewCell
cell.frame = CGRectMake(0.0, 0.0, UIScreen.mainScreen().bounds.size.width, cell.bounds.height)
cell.contentView.frame = cell.bounds
cell.selectionStyle = UITableViewCellSelectionStyle.None
btnPlayPause = cell.viewWithTag(1) as! subclassedUIButton
btnPlayPause.addTarget(self, action: "btnPlayPauseTapped:", forControlEvents: .TouchUpInside)
btnPlayPause.urlString = arrAttractionList[indexPath.row].audio.url
let lblAttractionName = cell.viewWithTag(2) as! UILabel
lblAttractionName.text = arrAttractionList![indexPath.row].name
return cell
}
My Button Methode is:
func btnPlayPauseTapped(sender: subclassedUIButton) {
if !isAudioPlaying {
isAudioPlaying = true
sender.setImage(UIImage(named: "Button_Pause"), forState: .Normal)
AudioHandler.sharedManager().playAudio(sender.urlString)
} else {
isAudioPlaying = false
sender.setImage(UIImage(named: "Button_Play_Menu.png"), forState: .Normal)
AudioHandler.sharedManager().audioPlayer?.pause()
}
}
// Custome class for UIButton
class subclassedUIButton: UIButton {
var indexPath: Int?
var urlString: String?
}
You have to manage other flag for your current cell's audio you have to play. Set current cell index and manage that.
let cell: UITableViewCell = NSBundle.mainBundle().loadNibNamed("ExtendedDesign", owner: self, options: nil)[4] as! UITableViewCell
This is not the correct way to add a cell to a table view. You should call registerNib(_:forCellReuseIdentifier:) (usually in the viewDidLoad() method) and then dequeueReusableCellWithIdentifier(_:forIndexPath:) in your cellForRowAtIndexPath(_:) delegate method.
The registerNib(_:forCellReuseIdentifier:) isn't needed if you design your cell with a template in the storyboard.
But you really need to use table views the way they were designed to be used. What you have now might seem to work, but it's going to have problems and make your life miserable if you don't use table views the way they were intended to be used.
Also, you don't want to be referencing your subviews with viewWithTag(_:). You want to wire up proper IBOutlets in Interface Builder. Or at least creating properties in your UITableView subclass to access the subviews.
Finally I did it here is my code to do this. First Declare Two variables in class:
var currentCellIndex :int = -1
var previousCellIndex : int = -1
func btnPlayPauseTapped(sender: subclassedUIButton) {
currentCellIndex = sender.indexPath!
if currentCellIndex == previousCellIndex {
if !isAudioPlaying {
isAudioPlaying = true
AudioHandler.sharedManager().stopAudioPlayer()
sender.setImage(UIImage(named: "Button_Play_Menu.png"), forState: .Normal)
} else {
isAudioPlaying = false
AudioHandler.sharedManager().playAudio(sender.urlString)
sender.setImage(UIImage(named: "Button_Pause"), forState: .Normal)
}
} else {
let indexPath = NSIndexPath(forRow: previousCellIndex, inSection: 0)
tblAttractinList.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
isAudioPlaying = false
sender.setImage(UIImage(named: "Button_Pause"), forState: .Normal)
AudioHandler.sharedManager().playAudio(sender.urlString)
previousCellIndex = sender.indexPath!
}
}
Thank You all of you for your help! :)

UIButton not changing image

I'm trying to create a "like" button for my app. When I press the button, I want it to change to a second image, and when I press it again I went it to change back to the first image.
Here is my code:
#IBAction func sendLike(sender: UIButton) {
if let _ = UIImage(named:"pinkLike.png") {
sender.setImage(UIImage(named:"like.png"), forState: .Normal)
}
if let _ = UIImage(named:"like.png") {
sender.setImage(UIImage(named:"pinkLike.png"), forState: .Normal)
}
}
For some reason this line of code works how I want it to:
if let _ = UIImage(named:"like.png") {
sender.setImage(UIImage(named:"pinkLike.png"), forState: .Normal)
}
But when I press the button again, this line doesn't change it back:
if let _ = UIImage(named:"pinkLike.png") {
sender.setImage(UIImage(named:"like.png"), forState: .Normal)
}
You set both of those image in Normal state, so your "like.png" will always override "pinkLike.png" image.
You need to put "pinkLike.png" in different state.
if let _ = UIImage(named:"pinkLike.png") {
sender.setImage(UIImage(named:"like.png"), forState: .Selected)
}
and change your selected state before setting the image. So your code will be like this.
#IBAction func sendLike(sender: UIButton) {
sender.selected = !sender.selected
if let _ = UIImage(named:"pinkLike.png") {
sender.setImage(UIImage(named:"like.png"), forState: .Normal)
}
if let _ = UIImage(named:"like.png") {
sender.setImage(UIImage(named:"pinkLike.png"), forState: .Selected)
}
}

Crash with Thread 1: signal SIGABRT

I am creating a class to manage the checkboxes.
This is what I did:
import UIKit
class CheckboxButton: UIButton {
//let checked = ""
//let unchecked = ""
let checked = "bubu"
let unchecked = "baba"
var isChecked:Bool = false{
didSet{
if isChecked == true {
self.setTitle(checked, forState: UIControlState.Normal)
}else{
self.setTitle(unchecked, forState: UIControlState.Normal)
}
}
}
override func awakeFromNib() {
self.titleLabel?.font = UIFont(name: "FontAwesome", size: 20)
self.addTarget(self, action: "buttonClicked", forControlEvents: UIControlEvents.TouchUpInside)
self.isChecked = false
}
func buttonClicked(sender:UIButton){
if(sender == self){
if isChecked == true {
isChecked = false
}else{
isChecked = true
}
}
}
}
Everything is fine, but when I click on the button, the app just crashes with error Thread 1: signal SIGABRT
Is there something wrong?
Thanks for any help!
Since your action function takes parameters func buttonClicked(sender:UIButton) the action of your target should always contain : at the end, so all you have to do is to replace add target line with this
self.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
Also your buttonClicked function can be simplified like this:
func buttonClicked(sender:UIButton){
if(sender == self){
isChecked = !isChecked
}
}

Resources