I have implemented facebook native Ads in UITableView, for first 1-2 times it clickable but when I scroll tableview and come again back to the same cell, now Ads are not clicking, I am using swift 3.2
Below is the cell implementation.
let ad = adsManager.nextNativeAd
let cell = self.tableHome.dequeueReusableCell(withIdentifier: "HomeAdsTableViewCell", for: indexPath) as! HomeAdsTableViewCell
cell.message.text = ad?.body
cell.title.text = ad?.title
cell.callToActionButton.setTitle(ad?.callToAction, for: .normal)
if let pic = ad?.coverImage {
cell.postImage.setImageWithIndicator(imageUrl:pic.url.absoluteString)
}
ad?.registerView(forInteraction: cell.postView, with: self)
cell.selectionStyle=UITableViewCellSelectionStyle.none
return cell
//create a new object of nativead in your class//
var previousNativead : FBNativeAd?
let ad = adsManager.nextNativeAd
self.previousNativead?.unregisterView()
let cell = self.tableHome.dequeueReusableCell(withIdentifier: "HomeAdsTableViewCell", for: indexPath) as! HomeAdsTableViewCell
cell.message.text = ad?.body
cell.title.text = ad?.title
cell.callToActionButton.setTitle(ad?.callToAction, for: .normal)
if let pic = ad?.coverImage {
cell.postImage.setImageWithIndicator(imageUrl:pic.url.absoluteString)
}
previousNativead = ad
ad?.registerView(forInteraction: cell.postView, with: self)
cell.selectionStyle=UITableViewCellSelectionStyle.none
return cell
I suggest you follow the steps here. He told me about putting a facebook ad between cells.
https://www.appcoda.com/facebook-ads-integration/
It's like you do not make mistakes in the place you turn the idiot.
You should adapt this part in the link to your own.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if adsCellProvider != nil && adsCellProvider.isAdCellAtIndexPath(indexPath, forStride: UInt(adRowStep)) {
return adsCellProvider.tableView(tableView, cellForRowAtIndexPath: indexPath)
}
else {
let cell = tableView.dequeueReusableCellWithIdentifier("idCellSample", forIndexPath: indexPath) as! SampleCell
cell.lblTitle.text = sampleData[indexPath.row - Int(indexPath.row / adRowStep)]
return cell
} }
You should also use this method on screen refreshes and turns.
func configureAdManagerAndLoadAds() {
if adsManager == nil {
adsManager = FBNativeAdsManager(placementID: "PLACEMENT_ID", forNumAdsRequested: 5)
adsManager.delegate = self
adsManager.loadAds()
}}
override func viewWillAppear(animated: Bool) {
configureAdManagerAndLoadAds()}
Finally you should check the relevant fields, which may not be compatible with content swift 3.
First in your viewDidLoad() check that you add
override func viewDidLoad() {
super.viewDidLoad()
adsManager = FBNativeAdsManager(placementID: "YOUR_PLACEMENT_ID_HERE", forNumAdsRequested: "YourNumAds")
adsManager.delegate = self
adsManager.loadAds()
}
then to comply with the FBNativeAdsManagerDelegate, you will need to add the following method, nativeAdsLoaded().
func nativeAdsLoaded() {
adsCellProvider = FBNativeAdTableViewCellProvider(manager: adsManager, forType: FBNativeAdViewType.genericHeight300)
adsCellProvider.delegate = self
if self.tableview != nil {
self.tableview.reloadData()
}
}
the last thing you should do is to registerView directly to the cell not to postView
ad?.registerView(forInteraction: cell, with: self)
hope that will help you.
Related
I have question about the tableView.
Here is my tableView code
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tierCount
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "InterestRateTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? InterestRateTableViewCell else {
fatalError("The dequed cell is not an instance of InterestRateTableViewCell.")
}
cell.interestRateTextField.delegate = self
cell.rowLabel.text = "\(indexPath.row + 1)."
if let interestText = cell.interestRateTextField.text {
if let interest = Double(interestText){
interestRateArray[indexPath.row] = interest
} else {
interestRateArray[indexPath.row] = nil
}
} else {
interestRateArray[indexPath.row] = nil
}
return cell
}
As you can see, I have the cellForRowAt method to get the value from the textfields in the cell, and assign to my arrays. (I actually have 2 textfields per cell.)
Basically, I let the users input and edit the textfield until they are happy then click this calculate button, which will call the calculation method. In the calculation method I call the "tableView.reloadData()" first to gather data from the textfields before proceed with the actual calculation.
The problem was when I ran the app. I typed values in all the textfields then clicked "calculate", but it showed error like the textfields were still empty. I clicked again, and it worked. It's like I had to reload twice to get things going.
Can anyone help me out?
By the way, please excuse my English. I'm not from the country that speak English.
edited: It may be useful to post the calculate button code here as someone suggested. So, here is the code of calculate button
#IBAction func calculateRepayment(_ sender: UIButton) {
//Reload data to get the lastest interest rate and duration values
DispatchQueue.main.async {
self.interestRateTableView.reloadData()
}
//Get the loan value from the text field
if let loanText = loanTextField.text {
if let loanValue = Double(loanText) {
loan = loanValue
} else {
print("Can not convert loan value to type Double.")
return
}
} else {
print("Loan value is nil")
return
}
tiers = []
var index = 0
var tier: Tier
for _ in 0..<tierCount {
if let interestRateValue = interestRateArray[index] {
if let durationValue = durationArrayInMonth[index] {
tier = Tier(interestRateInYear: interestRateValue, tierInMonth: durationValue)
tiers.append(tier)
index += 1
} else {
print("Duration array contain nil")
return
}
} else {
print("Interest rate array contain nil")
return
}
}
let calculator = Calculator()
repayment = calculator.calculateRepayment(tiers: tiers, loan: loan!)
if let repaymentValue = repayment {
repaymentLabel.text = "\(repaymentValue)"
totalRepaymentLabel.text = "\(repaymentValue * Double(termInYear!) * 12)"
} else {
repaymentLabel.text = "Error Calculating"
totalRepaymentLabel.text = ""
}
}
cellForRowAt is used for initially creating and configuring each cell, so the textfields are empty when this method is called.
UITableView.reloadData() documentation:
// Reloads everything from scratch. Redisplays visible rows. Note that this will cause any existing drop placeholder rows to be removed.
open func reloadData()
As it says in Apple's comment above, UITableView.reloadData() will reload everything from scratch. That includes your text fields.
There are a number of ways to fix your issue, but it's hard to say the best way without more context. Here's an example that would fit the current context of your code fairly closely:
class MyCustomTableViewCell: UITableViewCell {
#IBOutlet weak var interestRateTextField: UITextField!
var interestRateChangedHandler: (() -> ()) = nil
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
interestRateTextField.addTarget(self, action: #selector(interestRateChanged), for: UIControlEvents.editingChanged)
}
#objc
func interestRateChanged() {
interestRateChangedHandler?()
}
}
and cellForRowAtIndex:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "InterestRateTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? InterestRateTableViewCell else {
fatalError("The dequed cell is not an instance of InterestRateTableViewCell.")
}
cell.rowLabel.text = "\(indexPath.row + 1)."
cell.interestRateChangedHandler = { [weak self] in
if let interestText = cell.interestRateTextField.text {
if let interest = Double(interestText){
self?.interestRateArray[indexPath.row] = interest
} else {
self?.interestRateArray[indexPath.row] = nil
}
} else {
self?.interestRateArray[indexPath.row] = nil
}
}
return cell
}
i wanted to make an application with a design that looks like the app store so i followed let's build that app tutorial on how to build it and the result was excellent, when i tested it on my device it seem that when i fast scroll the collection-view the data that's in the first row take place on the last row and when scroll fast up again, the data that supposed to be in the last row i found it in the first row and when i scroll left and right in the row when the cell go off the screen it re-update to the right data but when fast scroll up and down fast again the data between the first row and last row go crazy.
i added image to the case in the end of the code.i spent 5 days trying to fix this but no luck at all i tried alot of solution like reset the data in the cell to nil before reseting it and alot other u will find it in the code but no luck , I really appreciate any help you can provide, Thanks
*update
after #Joe Daniels answer all the data are staple and work fine except the images it still go crazy when fast scrolling but it return after 7/8 sec of stopping scrolling to the right image
first class that contain the vertical collectionview
i made the width of the cell equal the width of the view , i tried the cell.tag == index-path.row solution but it didn't work
class HomePageVC: UIViewController , UICollectionViewDataSource , UICollectionViewDelegate , UICollectionViewDelegateFlowLayout ,SWRevealViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
mainProductsRow.delegate = self
mainProductsRow.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = productCategory?.count {
return count + 1
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.row == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LargeHomeCategoriesCell.identifier, for: indexPath) as! LargeHomeCategoriesCell
cell.categoriesHomePageVC = self
cell.catIndexPath = indexPath.row
cell.productCategories = productCategory
cell.seeMore.addTarget(self, action: #selector(self.seeMoreCat(_:)), for: UIControlEvents.touchUpInside)
return cell
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RowCell", for: indexPath) as! HomeCategoriesCell
cell.categoriesHomePageVC = self
cell.catIndexPath = indexPath.row
cell.seeMore.tag = (indexPath.row - 1 )
cell.seeMore.addTarget(self, action: #selector(self.seeMore(_:)), for: UIControlEvents.touchUpInside)
// cell.tag = indexPath.row - 1
cell.productCategory = nil
//if cell.tag == indexPath.row - 1{
cell.productCategory = productCategory?[indexPath.row - 1 ]
// }
return cell
}
**Second Class that contain the horizontal collectionview that is in the first collectionview cell **
in the cell-for-item-At-index-Path i printed the index-path.row when the view-load it printed 0 , 1 ,2 and didn't print the last index '3' and the same thing happen again when i scroll to the end of the collection view
class HomeCategoriesCell: UICollectionViewCell , UICollectionViewDataSource , UICollectionViewDelegateFlowLayout , UICollectionViewDelegate{
#IBOutlet weak var seeMore: UIButton!
#IBOutlet weak var categorytitle: UILabel!
#IBOutlet weak var productsCollectionView: UICollectionView!
let favFuncsClass = FavItemsFunctionality()
let onCartFuncsClass = OnCartFunctionality()
var productCategory : ProductCategories? {
didSet {
if let categoryTitle = productCategory?.name {
categorytitle.text = categoryTitle
}
}
override func awakeFromNib() {
recivedNotification()
productsCollectionView.delegate = self
productsCollectionView.dataSource = self
productsCollectionView.backgroundColor = UIColor.clear
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = productCategory?.products?.count {
return count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print(indexPath.row)
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HProductCell", for: indexPath) as! HomeProductCell
cell.tag = indexPath.row
cell.productImage.image = #imageLiteral(resourceName: "PlaceHolder")
cell.productTitle.text = nil
cell.productPrice.text = nil
cell.discountLabel.text = nil
cell.preDiscountedPrice.text = nil
cell.favButton.setImage(UIImage(named:"Heart_icon"), for: UIControlState.normal)
cell.addToCart.setImage(UIImage(named:"cart"), for: UIControlState.normal)
cell.configCell(products: nil)
if cell.tag == indexPath.row {
cell.configCell(products: productCategory?.products?[indexPath.item])
}
cell.catNum = indexPath.row
return cell
}
// Thanks to Joe Daniels i added this code and my nightmare become way less scary :P
override func prepareForReuse() {
super.prepareForReuse()
productsCollectionView.reloadData()
}
}
Product Cell
i tried the prepare-For-Reuse() and reseted all the data to nil but still didn't work
class HomeProductCell: UICollectionViewCell {
func configCell(products :productDetails?){
if let title = products?.name {
self.productTitle.text = title
}else { self.productTitle.text = nil}
if let price = products?.price {
self.productPrice.text = "\(price)"
}else { self.productPrice.text = nil }
}
override func prepareForReuse() {
super.prepareForReuse()
self.productImage.image = #imageLiteral(resourceName: "PlaceHolder")
productTitle.text = nil
productPrice.text = nil
discountLabel.text = nil
preDiscountedPrice.text = nil
favButton.setImage(UIImage(named:"Heart_icon"), for: UIControlState.normal)
addToCart.setImage(UIImage(named:"cart"), for: UIControlState.normal)
}
i took a screen shoots of the case you can find it here
i Found the Perfect Solution that worked for me as magic after 15 days of suffering xD i used this library (https://github.com/rs/SDWebImage).
To use it in swift put this line in your bridging Header
#import <SDWebImage/UIImageView+WebCache.h>
And in the collectionViewCell i used this line
self.productImage.sd_setImage(with: myUrl , placeholderImage: UIImage(named:"yourPlaceHolderImage")
The inner collectionView has no way of knowing that it went off screen. do a reload in prepareForReuse
I have a problem about my cell's button.
In my tableView each row is composed by: an image, some labels and a button.
The button has a checkmark image. When it is clicked, the button's image changes.
The problem is that also another button's image changes without reason.
This mistake happens because my cell is reused.
I have tried to use prepareForReuse method in TableViewCell but nothing happens. I've also tried with selectedRowAt but I didn't have any results. Please help me.
Image 1:
Image 2:
This is my func in my custom Cell:
override func prepareForReuse() {
if checkStr == "uncheck"{
self.checkBook.setImage(uncheck, for: .normal)
} else if checkStr == "check"{
self.checkBook.setImage(check, for: .normal)
}
}
func isPressed(){
let uncheck = UIImage(named:"uncheck")
let check = UIImage(named: "check")
if self.checkBook.currentImage == uncheck{
checkStr == "check"
} else self.checkBook.currentImage == check{
checkStr == "uncheck"
}
}
In my tableView:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell: ListPropertyUserCell = tableView.cellForRow(at: indexPath) as! ListPropertyUserCell
let uncheck = UIImage(named:"uncheck")
let check = UIImage(named: "check")
if selectedCell.checkBook.imageView?.image == uncheck{
selectedCell.checkStr = "check"
} else if selectedCell.checkBook.imageView?.image == check{
selectedCell.checkStr = "uncheck"
}
}
From the information in your post, this looks like a cell reuse issue. The problem is that the tableView reuses the cells rather than creating new ones, to maintain performance. If you haven't reset the cell's state, the reused cell will be remain configured in the old state.
For a quick fix, you can implement the prepareForReuse method on UITableViewCell.
However, you'll need to store which cell is 'checked' in your view controller if you want the checkbox to be selected after scrolling the tableView. You can store this yourself, or use the tableView's didSelectRowAtIndexPath method.
Try to do it like this:
var checkBook = UIImageView()
if self.checkBook.image == UIImage(named: "check"){
self.checkBook.image = UIImage(named: "uncheck")
}
else{
self.checkBook.image = UIImage(named: "check")
}
If you're using the click on the entire cell, you can override the setSelected func in your custom cell just like that.
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if selected {
self.checkBook.image = UIImage(named: "check")
} else {
self.checkBook.image = UIImage(named: "uncheck")
}
}
UITableViewCell is reusable. You can't store state of view in cell. You should setup cell in
func tableView(UITableView, cellForRowAt: IndexPath)
method of your data source
The easiest way to achieve that is to implement
func tableView(UITableView, didSelectRowAt: IndexPath)
func tableView(UITableView, didDeselectRowAt: IndexPath)
methods of UITableViewDelegate
Then you can add/remove indexPath to some array in these methods and in cellForRowAtIndexPath setup cell.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("YourTableViewCell") as! YourTableViewCell
if array.contains(indexPath) {
cell.checkBook.image = UIImage(named: "check")
} else {
cell.checkBook.image = UIImage(named: "uncheck")
}
return cell
}
Try my code . here selectindex is use for get selected cell index and selectedindex is NSMutableArray that i store all selected cell value.
var selectindex : Int?
var selectedindex : NSMutableArray = NSMutableArray()
#IBOutlet var tableview: UITableView!
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("LikeCell", forIndexPath: indexPath)
let like: UIButton = (cell.viewWithTag(2) as! UIButton)// like button
let comment: UIButton = (cell.viewWithTag(3) as! UIButton) // comment button
comment.setBackgroundImage(UIImage(named: "chat.png"), forState: UIControlState.Normal) // comment button set
like.addTarget(self, action: #selector(self.CloseMethod(_:event:)), forControlEvents: .TouchDown)
comment.addTarget(self, action: #selector(self.CloseMethod1(_:event:)), forControlEvents: .TouchDown)
return cell
}
// This is my like button action method.
#IBAction func CloseMethod(sender: UIButton, event: AnyObject) {
let touches = event.allTouches()!
let touch = touches.first!
let currentTouchPosition = touch.locationInView(self.tableview)
let indexPath = self.tableview.indexPathForRowAtPoint(currentTouchPosition)!
selectindex = indexPath.row
if selectedindex.containsObject(selectindex!) {
sender.setBackgroundImage(UIImage.init(named: "like (1).png"), forState: .Normal)
selectedindex.removeObject(selectindex!)
}else{
sender.setBackgroundImage(UIImage.init(named: "like.png"), forState: .Normal)
selectedindex.addObject(selectindex!)
}
}
I faced this problem recently, and did not find much about it. What solve, after much searching, was to use:
override func prepareForReuse() {
btnAdd.setImage(nil, for: .normal) //here I use to change to none image
super.prepareForReuse()
}
just put this method inside your custom UITableViewCell, and set which item you want to realese stats.
I want to create a table view that loads its data from two different data sources and chooses a specific nib depending on which one it is. How can I create a feed that mixes elements from two arrays of objects?
I was thinking about creating an array of objects with the objects being sorted by date. Then, I would check what the type of each object is at my cellforrowatindexpath method and use the nib that corresponds.
Is this the most efficient way of doing this?
Thank you
Creating a single array of objects and dealing with them correctly in cellForRowAtIndexPath is a good approach.
You should register each of your nibs with the table view, probably in viewDidLoad using registerNibForCellReuseIdentifier. Make sure you use a different reuseIdentifier for each, then dequeue the correct type in cellForRowAtIndexPath, once you've determined which type of cell it should be.
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerNib(UINib(nibName: "fooCell", bundle: .mainBundle()), forCellReuseIdentifier: "fooCellIdentifier");
tableView.registerNib(UINib(nibName: "barCell", bundle: .mainBundle()), forCellReuseIdentifier: "barCellIdentifier");
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: UITableViewCell?
if (indexPathIsFooCell(indexPath)) {
cell = tableView.dequeueReusableCellWithIdentifier("fooCellIdentifier", forIndexPath: indexPath)
} else {
cell = tableView.dequeueReusableCellWithIdentifier("barCellIdentifier", forIndexPath: indexPath)
}
//customise the cell
return cell!
}
Here is Another Solution which Might help You
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
var cell = tableView.dequeueReusableCellWithIdentifier("relatedTableViewCell") as? relatedTableViewCell
if cell == nil {
let nib:Array = NSBundle.mainBundle().loadNibNamed("relatedTableViewCell", owner: self, options: nil)
cell = nib[0] as? relatedTableViewCell
}
let info = relatedQuizArr.objectAtIndex(indexPath.row) as! relatedInfo
cell?.relatedImage.sd_setImageWithURL(NSURL(string: info.rqImage!))
cell?.relatedLabel.text = (String: info.rqName!)
cell?.rQuizView.layer.shadowOffset = CGSizeMake(-1, 1)
cell?.rQuizView.layer.shadowOpacity = 0.4
return cell!
} else {
var cell = tableView.dequeueReusableCellWithIdentifier("relatedTableViewCell") as? relatedTableViewCell
if cell == nil {
let nib:Array = NSBundle.mainBundle().loadNibNamed("relatedTableViewCell", owner: self, options: nil)
cell = nib[0] as? relatedTableViewCell
}
if indexPath.row == 0 {
let label = UILabel(frame: CGRectMake(22, 10, 200, 40))
label.font = label.font.fontWithSize(25)
label.textAlignment = NSTextAlignment.Center
label.text = "Featured Quiz"
cell!.addSubview(label)
}
else {
}
let info = featuredQuizArray.objectAtIndex(indexPath.row) as! relatedInfo
cell?.relatedImage.sd_setImageWithURL(NSURL(string: info.featuredqImage!))
cell?.relatedLabel.text = (String: info.featuredqName!)
cell?.rQuizView.layer.shadowOffset = CGSizeMake(-1, 1)
cell?.rQuizView.layer.shadowOpacity = 0.4
return cell!
}
Hope It will help.
I have a TableViewController with a custom cell. When I tap the like button inside one of the cells, it is causing at least one other cell to have the like button tapped.
I am using Parse, and it is not affecting the actual like count of the second one which is being ghost tapped, but it is disabling the like button and turning it red.
I have read about cell reuse, and similar topics but am completely lost. I am new to swift, and if someone could help me navigate how to fix this, I can't find a solution on SO about Swift, and Parse.
TableViewController
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:ChinTwoTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! ChinTwoTableViewCell
cell.selectionStyle = .None
// Configure the cell...
let chinTwo:PFObject = self.timelineData.objectAtIndex(indexPath.row) as! PFObject
var myVar:Int = chinTwo.objectForKey("likeCount") as! Int
cell.countLabel.text = String(myVar)
cell.nameLabel.text = chinTwo.objectForKey("name") as? String
cell.bodyText.text = chinTwo.objectForKey("body") as! String
cell.bodyText.font = UIFont(name: "HelveticaNeue-UltraLight", size: 18)
cell.bodyText.textAlignment = .Center
cell.likeButton.tag = indexPath.row;
cell.likeButton.addTarget(self, action: "likeButtonTapped:", forControlEvents: .TouchUpInside)
return cell
}
#IBAction func likeButtonTapped(sender: AnyObject) {
let chinTwo = self.timelineData[sender.tag] as! PFObject
chinTwo["likeCount"] = (chinTwo["likeCount"] as! Int) + 1
sender.setTitleColor(UIColor.redColor(), forState: UIControlState.Normal)
chinTwo.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
println("Worked")
} else {
println("Didn't Work")
}
}
self.tableView.reloadData()
}
TableViewCell
#IBAction func likeTapped(sender: AnyObject) {
likeButton.enabled = false
}
The same problem occurs with the report button.
Due to reuseable cells, the same likeButton will be used on multiple cells depending on if it's shown or not. If you change the color of one instance it will keep that color when it is reused again for another cell. Instead of setting the color in the click method you should determine if the button should be red or not in the cellForRowAtIndexPath method. So something like:
var likedRows: Set<Int> = Set()
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
self.markButtonIfLiked(cell.button, atRow: indexPath.row)
...
}
#IBAction func likeButtonTapped(button: UIButton) {
...
self.likedRows.insert(button.tag)
self.markButtonIfLiked(button, atRow: button.tag)
...
}
func markButtonIfLiked(button: UIButton, atRow row: Int) {
if (self.likedRows.contains(row)) {
button.setTitleColor(.redColor(), forState: .Normal)
}
}
And you shouldn't need the tableView.reloadData() call.