Configure in TableView is not being reached - swift

So I am trying to have a TableView displayed, but I'm currently only getting an empty tableview. Upon further inspection, I see that the configure block is not being run. Why is this?
Code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: teamCellIdentifier, for: indexPath)
print("reached")
func configure(cell: UITableViewCell,
for indexPath: IndexPath) {
print("not reached")
guard let cell = cell as? TeamCell else {
return
}
let team = fetchedResultsController.object(at: indexPath)
cell.teamLabel.text = team.teamName
cell.scoreLabel.text = "Wins: \(team.wins)"
if let imageName = team.imageName {
cell.flagImageView.image = UIImage(named: imageName)
} else {
cell.flagImageView.image = nil
}
}
return cell
}
}
TeamCell
class TeamCell: UITableViewCell {
// MARK: - IBOutlets
#IBOutlet weak var teamLabel: UILabel!
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet weak var flagImageView: UIImageView!
// MARK: - View Life Cycle
override func prepareForReuse() {
super.prepareForReuse()
teamLabel.text = nil
scoreLabel.text = nil
flagImageView.image = nil
}
}

The reason why your tableView is empty or it looks like empty and your tableView methods are not being called are two reasons.
1)- Table view heigh or width is equal to 0 and there is no need to load and display tableView, respectively tableView cell.
2)- You did not connect tableView data source and delegates to your view controller.
2.1) You can add TableView delegates and data sources through storyboard (see image down below)
2.2) You can add TableView delegates and data sources thrugh code in your view controller, like:
2.2.1) In viewDidLoad() method:
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// TableView Delegates & DataSource
tableView.delegate = self
tableView.dataSource = self
}
2.2.2) Or view tableView outlet didSet (I prefer this way, if I don't do it via storyboard):
#IBOutlet weak var tableView: UITableView! {
didSet {
tableView.delegate = self
tableView.dataSource = self
}
}

Your code has a structure like this:
// This next line defines a function called a
func a() {
print("a")
// This next line only defines a function called b:
// that is scoped inside the 'a' function
func b() {
print("b")
}
// Defining a function does not invoke the function
// That would need an actual call here like this:
// b()
}
// I can't call b here because it's nested inside the function 'a'

First you have a nested func inside function?
Take it out....
then after this line :
let cell = tableView.dequeueReusableCell(withIdentifier: teamCellIdentifier, for: indexPath)
do this:
configure(cell: cell,for indexPath: indexPath)

Related

Problem using an extension to load image into a custom cell

My last question for today. I'm too newby with swift to know what's happening but my problem is as follows:
I've use the next extension to load an image from a URL and it works when I use it in a imageView in a normal view, but when the imageView is inside of a custom cell It doesn't recognize de method so I can`t use the extension. What am I doing wrong? Thank you all in advances.
My code for the extesion is:
import UIKit
extension UIImageView {
func loadFrom(URLAddress: String) {
guard let url = URL(string: URLAddress) else {
return
}
DispatchQueue.main.async { [weak self] in
if let imageData = try? Data(contentsOf: url) {
if let loadedImage = UIImage(data: imageData) {
self?.image = loadedImage
}
}
}
}
}
And the code for the table where I try to use it into a custom cell:
import UIKit
class EventosCustomCellController: UITableViewCell {
#IBOutlet weak var imEvento: UIView!
#IBOutlet weak var txtNombreEvento: UILabel!
#IBOutlet weak var txtFechaEvento: UILabel!
#IBOutlet weak var txtEstadoEvento: UILabel!
}
class ListaEventosTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Eventos"
}
// MARK: - Table view data source
override func viewWillAppear(_ animated: Bool) {
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return eventos.contarEventos()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "prototipoCeldaEvento", for: indexPath) as! EventosCustomCellController
let evento = eventos.buscarEventoPorID(id: indexPath.row)
cell.txtNombreEvento?.text = evento?.nombre
cell.txtFechaEvento?.text = evento?.fecha
cell.txtEstadoEvento?.text = evento?.tipo
if evento?.tipo == "deportivo"{
cell.backgroundColor = .blue}
else if evento?.tipo == "cultural"{
cell.aplicarFondoDegradado()
cell.backgroundColor = .green}
else{
cell.backgroundColor = .red}
cell.layer.masksToBounds = true
cell.layer.cornerRadius = 10
//There is no method loadFrom when I try to use as follows
cell.imEvento?.loadFrom(URLAddress: (evento?.imagenes![0])!)
return cell
}
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
}
After updating code my problem is that the cell isn`t growthn appropiately
I think, you better use UIImageView instead of UIView because your extension for UIImageView and you can access to this.
#IBOutlet weak var imEvento: UIImageView!
and then it is good to override the prepareForReuse method like this:
override func prepareForReuse() {
super.prepareForReuse()
imEvento.image = nil
}
you can read more about the prepareForReuse method from apple documentation

Connect UITableviewCell with UITableview using Combine repeat values

I'm learning combine and I want to use combine instead a delegate between cell and tableview. I have managed to connect and receive the information, but the problem is when the cell is reused, every time I generate the same event, I receive it as many times as it has been used previously in that reused cell.
I have declared cancelables in the view controller as
var cancellables: Set<AnyCancellable> = []
And this is the cellForRow method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: MyCell.celdaReuseIdentifier, for: indexPath)
as? MyCell else {
return MyCell()
}
cell.index = indexPath
cell.lbTitle.text = String("Cell \(indexPath.row)")
cell.tapButton.compactMap{$0}
.sink { index in
print("tap button in cell \(index.row)")
}.store(in: &cancellables)
return cell
}
and the cell is
class MyCell: UITableViewCell {
static let cellNibName = "MyCell"
static let celdaReuseIdentifier = "MyCellReuseIdentifier"
#IBOutlet weak var lbTitle: UILabel!
#IBOutlet weak var button: UIButton!
var index: IndexPath?
let tapButton = PassthroughSubject<IndexPath?, Never>()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
#IBAction func tapButton(_ sender: Any) {
self.tapButton.send(index)
}
}
Thanks for your help
To solve your problem with reused cells you must add the Set<AnyCancellable> to the cell.
If you are only going to use an event inside cells you can use a single AnyCancellable:
Single Event (AnyCancellable)
Declares a variable in the cell of AnyCancellable Type. Every time the cell is reused a new publisher will be added replacing the previous one and you will not receive the event multiple times.
Cell
class MyCell: UITableViewCell {
static let cellNibName = "MyCell"
static let celdaReuseIdentifier = "MyCellReuseIdentifier"
#IBOutlet weak var lbTitle: UILabel!
#IBOutlet weak var button: UIButton!
var index: IndexPath?
// store publisher here
var cancellable: AnyCancellable?
// Single Publisher per cell
let tapButton = PassthroughSubject<IndexPath?, Never>()
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func tapButton(_ sender: Any) {
self.tapButton.send(index)
}
}
ViewController
In the Viewcontroller you just have to add the publisher to the cancellable.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: MyCell.celdaReuseIdentifier, for: indexPath)
as? MyCell else {
return MyCell()
}
cell.index = indexPath
cell.lbTitle.text = String("Cell \(indexPath.row)")
// Add your publisher to your cancellable and remove store function.
cell.cancellable = cell.tapButton.compactMap{$0} .sink { index in
print("tap button in cell \(index.row)")
}
return cell
}
Multiples events (Set<AnyCancellable>)
Here it is the same but using a collection in case you want to have more events than just one.
Cell
Create a variable Set<AnyCancellable> to store the publishers.
In this case, before reusing the cell, we will have to remove the cancellables before creating new ones.
class MyCell: UITableViewCell {
static let cellNibName = "MyCell"
static let celdaReuseIdentifier = "MyCellReuseIdentifier"
#IBOutlet weak var lbTitle: UILabel!
#IBOutlet weak var button: UIButton!
var cancellables: Set<AnyCancellable>?
var index: IndexPath?
// Multiple Publishers per cell
let tapButton = PassthroughSubject<IndexPath?, Never>()
let tapView = PassthroughSubject<UIImage, Never>()
// Remove all suscriptions before reuse cell
override func prepareForReuse() {
super.prepareForReuse()
cancellables.removeAll()
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
#IBAction func tapButton(_ sender: Any) {
self.tapButton.send(index)
}
}
ViewController
In the Viewcontroller you just have to store the publishers.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: MyCell.celdaReuseIdentifier, for: indexPath)
as? MyCell else {
return MyCell()
}
cell.index = indexPath
cell.lbTitle.text = String("Cell \(indexPath.row)")
// Add your publisher to your cell´s collection of AnyCancellable
cell.tapButton.compactMap{$0}
.sink { index in
print("tap button in cell \(index.row)")
}.store(in: &cell.cancellables)
return cell
}
Good Luck!! 😉
You have analyzed and described the problem perfectly. And so the cause is clear. Look at your cellForRow implementation and think about what it does: You are creating and adding a new pipeline to your cancellables every time your cellForRow runs, regardless of whether you've already added a pipeline for this instantiation of the cell.
So you need a way not to do that. Can you think of a way? Hint: attach the pipeline to the cell and vend it from there, so there is only one per cell. Your Set won't add the same pipeline twice, because it is a Set.

Swift - Increment Label with Stepper in TableView Cell

Another Swift beginner here. I simply want a Stepper in each of my TableView cells that increments a label in the same cell.
I have found a couple of questions on this topic, but they include other elements and I haven't been able to extract the basic concept.
Swift Stepper Action that changes UITextField and UILabel within same cell
Stepper on tableview cell (swift)
So far I have connected IBOutlets for my Label and Stepper, as well as an IBAction for my Stepper in my cell class.
class BuyStatsCell: UITableViewCell{
//these are working fine
#IBOutlet weak var category: UILabel!
#IBOutlet weak var average: UILabel!
#IBOutlet weak var price: UILabel!
//Outlet for Label and Stepper - How do I make these work?
#IBOutlet weak var purchaseAmount: UILabel!
#IBOutlet weak var addSubtract: UIStepper!
//Action for Stepper - And this?
#IBAction func stepperAction(_ sender: UIStepper) {
self.purchaseAmount.text = Int(sender.value).description
}
}
And I understand the concept of reusing the cell in the cellForRowAt indexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BuyStatsTabCell", for: indexPath) as! BuyStatsCell
cell.isUserInteractionEnabled = false
//these are working
cell.category.text = categories[indexPath.row]
cell.price.text = String(prices[indexPath.row])
cell.average.text = String(averages[indexPath.row])
//but is there something I need to add here to keep the correct Stepper and Label for each class?
return cell
}
One of the already asked questions includes a protocol and another function in the ViewController like this
protocol ReviewCellDelegate{
func stepperButton(sender: ReviewTableViewCell)
}
func stepperButton(sender: ReviewTableViewCell) {
if let indexPath = tableView.indexPathForCell(sender){
print(indexPath)
}
}
I don't know if this is the approach I should be trying to take. I am looking for the simplest solution, but I am having trouble putting the pieces together.
Any help is appreciated.
Thanks.
Easiest solution (simplyfied):
Create a model BuyStat with a property purchaseAmount (it's crucial to be a class).
You are strongly discouraged from using multiple arrays as data source
class BuyStat {
var purchaseAmount = 0.0
init(purchaseAmount : Double) {
self.purchaseAmount = purchaseAmount
}
}
In the view controller create a data source array
var stats = [BuyStat]()
In viewDidLoad create a few instances and reload the table view
stats = [BuyStat(purchaseAmount: 12.0), BuyStat(purchaseAmount: 20.0)]
tableView.reloadData()
In the custom cell create a property buyStat to hold the current data source item with an observer to update stepper and label when buyStat is set
class BuyStatsCell: UITableViewCell {
#IBOutlet weak var purchaseAmount: UILabel!
#IBOutlet weak var addSubtract: UIStepper!
var buyStat : BuyStat! {
didSet {
addSubtract.value = buyStat.purchaseAmount
purchaseAmount.text = String(buyStat.purchaseAmount)
}
}
#IBAction func stepperAction(_ sender: UIStepper) {
buyStat.purchaseAmount = sender.value
self.purchaseAmount.text = String(sender.value)
}
}
In cellForRowAtIndexPath get the data source item and pass it to the cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BuyStatsTabCell", for: indexPath) as! BuyStatsCell
cell.buyStat = stats[indexPath.row]
return cell
}
The magic is: When you are tapping the stepper the label as well as the data source array will be updated. So even after scrolling the cell will get always the actual data.
With this way you don't need protocols or callback closures. It's only important that the model is a class to have reference type semantics.
NOTE: MY Cell class is just normal..All changes are in viewcontroller class
class cell: UITableViewCell {
#IBOutlet weak var ibAddButton: UIButton!
#IBOutlet weak var ibStepper: UIStepper!
#IBOutlet weak var ibCount: UILabel!
#IBOutlet weak var ibLbl: UILabel!
}
1.define empty int array [Int]()
var countArray = [Int]()
2.append countArray with all zeros with the number of data u want to populate in tableview
for arr in self.responseArray{
self.countArray.append(0)
}
3.in cell for row at
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! cell
let dict = responseArray[indexPath.row] as? NSDictionary ?? NSDictionary()
cell.ibLbl.text = dict["name"] as? String ?? String()
if countArray[indexPath.row] == 0{
cell.ibAddButton.tag = indexPath.row
cell.ibStepper.isHidden = true
cell.ibAddButton.isHidden = false
cell.ibCount.isHidden = true
cell.ibAddButton.addTarget(self, action: #selector(addPressed(sender:)), for: .touchUpInside)
}else{
cell.ibAddButton.isHidden = true
cell.ibStepper.isHidden = false
cell.ibStepper.tag = indexPath.row
cell.ibCount.isHidden = false
cell.ibCount.text = "\(countArray[indexPath.row])"
cell.ibStepper.addTarget(self, action: #selector(stepperValueChanged(sender:)), for: .valueChanged)}
return cell
}
4.objc functions
#objc func stepperValueChanged(sender : UIStepper){
if sender.stepValue != 0{
countArray[sender.tag] = Int(sender.value)
}
ibTableView.reloadData()
}
#objc func addPressed(sender : UIButton){
countArray[sender.tag] = 1//countArray[sender.tag] + 1
ibTableView.reloadData()
}

Putting Tableview inside UITableViewCell Swift

I'm having trouble with putting a tableview inside a uitableview cell. Right now, it doesn't even display the nested tableview. I set the tableview's delegates and datasource to the custom cell, and have all the required methods. I am also using a storyboard for my tableviews, and have connected all outlets properly.
The problem is that it does not go into the cellForRow function. It does, however, go into the numberOfRowsInSection function, and also the heightForRow function. Below is the code for the custom cell that holds the nested UITableviewcell.
import UIKit
class EventSurveyCell: UITableViewCell, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var cellBox: UIView!
#IBOutlet weak var announcementLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var authorLabel: UILabel!
#IBOutlet weak var numVotesLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
var surveyArray: [SurveyClass] = []
override func awakeFromNib() {
super.awakeFromNib()
tableView.delegate = self
tableView.dataSource = self
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
tableView.delegate = self
tableView.dataSource = self
// Configure the view for the selected state
}
func do_table_refresh()
{
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
return
})
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print("cellforrow")
let cell: SurveyItemCell = self.tableView.dequeueReusableCellWithIdentifier("surveyItemCell") as! SurveyItemCell!
cell.itemLabel.text = surveyArray[indexPath.row].itemName
//go through firebase to check which one user voted on
cell.voteButton.imageView!.image = UIImage(named: "circle")
let percent = surveyArray[indexPath.row].numOfVotes/surveyArray[indexPath.row].totalNumOfVotes
cell.precentLabel.text = String(percent)
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
print("heightforrow")
return 44
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("numrowsinsection")
return 3
//should be return self.surveyArray.count.
}
}
Below is a snippet of code for in the the viewcontroller that is holding the custom cells (containing nested tableviews). This code is part of the cellforrowatindexpath
case PostType.survey:
let cell: EventSurveyCell = self.tableView.dequeueReusableCellWithIdentifier("EventSurveyCell") as! EventSurveyCell!
//cut out other code as it is irrelevant
cell.surveyArray = eventPost.surveyClassArray
cell.do_table_refresh()
print(cell.tableView.rowHeight)
return cell
Another problem I have noticed is that when printing cell.tableview.rowheight, it prints -1. This could be the reason why it does not enter cellforrowatindexpath, but I clearly return 44 in the heightforrow function. Right now, it doesnt even display the nested tableview.
1) Don't need do_table_refresh, you can call cell.tableView.reloadData() directly.
2) Override the systemLayoutSizeFitting:withHorizontalFittingPriority:verticalFittingPriority function:
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
tableView.reloadData()
// if the table view is the last UI element, you might need to adjust the height
let size = CGSize(width: targetSize.width,
height: tableView.frame.origin.y + tableView.contentSize.height)
return size
}
3) In your view controller, after you set the surveyArray, remove this line cell.do_table_refresh(), replace with cell.setNeedsLayout() to auto adjust the layout (you might also need to call a method to update the other UIs such as announcementLabel etc. call it before setNeedsLayout too.)
I had the same issue, cellForRow was not being called for the TableView Inside Custom Cell. And the reason was that the tableview had no height set for it to show the content inside it. After I set some height for the child TableView, cellForRow started to begin calling.

How can I get indexPath.row in cell.swift

I have 2 files.
myTableViewController.swift
myTableCell.swift
Can I get the indexPath.row in myTabelCell.swift function?
Here is myTableCell.swift
import UIKit
import Parse
import ActiveLabel
class myTableCell : UITableViewCell {
//Button
#IBOutlet weak var commentBtn: UIButton!
#IBOutlet weak var likeBtn: UIButton!
#IBOutlet weak var moreBtn: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func likeBtnTapped(_ sender: AnyObject) {
//declare title of button
let title = sender.title(for: UIControlState())
//I want get indexPath.row in here!
}
Here is myTableViewController.swift
class myTableViewController: UITableViewController {
//Default func
override func viewDidLoad() {
super.viewDidLoad()
//automatic row height
tableView.estimatedRowHeight = 450
tableView.rowHeight = UITableViewAutomaticDimension
}
// cell config
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//define cell
let cell = tableView.dequeueReusableCell(withIdentifier: "myTableCell", for: indexPath) as! myTableCell
}
As you can see... I'm trying to get indexPath.row in myTableCell, liktBtnTapped function.
Could you let me know how can I access or get IndexPath.row?
I have created a UIResponder extension with a recursive method that you can use in any UIView (which inherits from UIResponder) to find a parent view of a specific type.
import UIKit
extension UIResponder {
/**
* Returns the next responder in the responder chain cast to the given type, or
* if nil, recurses the chain until the next responder is nil or castable.
*/
func next<U: UIResponder>(of type: U.Type = U.self) -> U? {
return self.next.flatMap({ $0 as? U ?? $0.next() })
}
}
Using this, we can extend UITableViewCell with some convenient read-only computed properties for the table view and index path of the cell.
extension UITableViewCell {
var tableView: UITableView? {
return self.next(of: UITableView.self)
}
var indexPath: IndexPath? {
return self.tableView?.indexPath(for: self)
}
}
Here is how you could use it in your example:
#IBAction func likeBtnTapped(_ sender: AnyObject) {
//declare title of button
let title = sender.title(for: UIControlState())
//I want get indexPath.row in here!
self.indexPath.flatMap { print($0) }
}
Swift 4+
Try this inside your cell.
func getIndexPath() -> IndexPath? {
guard let superView = self.superview as? UITableView else {
print("superview is not a UITableView - getIndexPath")
return nil
}
indexPath = superView.indexPath(for: self)
return indexPath
}
Easy.. You can do like this inside button action:
let section = 0
let row = sender.tag
let indexPath = IndexPath(row: row, section: section)
let cell: myTableCell = self.feedTableView.cellForRow(at: indexPath) as! myTableCell
And afterwards in cellForRowAtIndexPath:
// add the row as the tag
cell.button.tag = indexPath.row
Another Approach for Swift 4.2 and not assuming Superview will be always a tableview
extension UITableViewCell{
var tableView:UITableView?{
return superview as? UITableView
}
var indexPath:IndexPath?{
return tableView?.indexPath(for: self)
}
}
Usage example
#IBAction func checkBoxAction(_ sender: UIButton) {
guard let indexPath = indexPath else { return }
sender.isSelected = !sender.isSelected
myCustomCellDelegate?.checkBoxTableViewCell(didSelectCheckBox: sender.isSelected, for: indexPath)
}
Swift 4.1. Here I created function to get IndexPath. Just pass your UIView(UIButton,UITextField etc) and UITableView object to get IndexPath.
func getIndexPathFor(view: UIView, tableView: UITableView) -> IndexPath? {
let point = tableView.convert(view.bounds.origin, from: view)
let indexPath = tableView.indexPathForRow(at: point)
return indexPath
}
Create a property indexPath in the cell class and set it in cellForRowAtIndexPath when the cell is reused.
But there is a caveat: Some table view methods to rearrange the cells don't call cellForRowAtIndexPath. You have to consider this case.
But if you use always only reloadData() it's safe and pretty easy.
Another way is to put the code regarding controlling things back in the controller class and run it via callback closures capturing the index path.
Heres another way of doing it
import UIKit
import Parse
import ActiveLabel
class myTableCell : UITableViewCell {
//Button
#IBOutlet weak var commentBtn: UIButton!
#IBOutlet weak var likeBtn: UIButton!
#IBOutlet weak var moreBtn: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
}
}
class myTableViewController: UITableViewController {
//Default func
//assuming you have an array for your table data source
var arrayOfTitles = [String]()
override func viewDidLoad() {
super.viewDidLoad()
//automatic row height
tableView.estimatedRowHeight = 450
tableView.rowHeight = UITableViewAutomaticDimension
}
// cell config
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//define cell
let cell = tableView.dequeueReusableCell(withIdentifier: "myTableCell", for: indexPath) as! myTableCell
cell.commentBtn.tag = indexPath.row
cell.commentBtn.addTarget(self, action: #selector(likeBtnTapped(_:), forControlEvents:.TouchUpInside)
//cell config end
#IBAction func likeBtnTapped(sender: UIButton) {
let btn = sender
let indexP = NSIndexPath(forItem: btn.tag, inSection: 0)
let cell = tableView.dequeueReusableCell(withIdentifier: "myTableCell", for: indexP) as! myTableCell
//I want get indexPath.row in here!
let title = arrayOfTitles[indexP.row]
//declare title of button
cell.commentBtn.setTitle(title, forState: UIControlState.Normal)
}
}
My solution was subclassing UITableViewCell, so can add IndexPath property. assign custom class for table view cell in storyboard. assign IndexPath value when rowAtIndexPath called.
class MyTableViewCell: UITableViewCell {
var indexPath: IndexPath?
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cellid1", for: indexPath)
(cell as? MyTableViewCell)?.indexPath = indexPath
return cell
}
Swift 5:
if
let collectionView = superview as? UICollectionView,
let index = collectionView.indexPath(for: self)
{
// stuff
}