Reputation: 5111
I am using table view in my Swift project. The problem is with table view cell index path value. When the table gets loaded initially, the index values are ok. But as soon as I scroll my table view the cell index paths change and the ids I get from a data array are wrong. Googling results that it is because of reusable cell like thing. Here's my view controller code:
//
// ProjectsController.swift
// PMUtilityTool
//
// Created by Muhammad Ali on 9/23/16.
// Copyright © 2016 Genetech Solutions. All rights reserved.
//
import UIKit
class ProjectsController: UIViewController {
//MARK: Properties
var ProjectsArray: Array<Project> = []
@IBOutlet weak var ProjectsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.navigationController?.navigationBarHidden = true
// Remove indenting of cell
if self.ProjectsTableView.respondsToSelector(Selector("setSeparatorInset:")) {
self.ProjectsTableView.separatorInset = UIEdgeInsetsZero
}
if self.ProjectsTableView.respondsToSelector(Selector("setLayoutMargins:")) {
self.ProjectsTableView.layoutMargins = UIEdgeInsetsZero
}
self.ProjectsTableView.layoutIfNeeded()
// Get projects
getProjects()
}
override func viewWillAppear(animated: Bool) {
self.navigationController?.navigationBarHidden = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
// MARK: - TableView Datasource
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return self.ProjectsArray.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// print("clicked")
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> ProjectTableViewCell {
print(indexPath.row)
let cell = tableView.dequeueReusableCellWithIdentifier("ProjectViewCell", forIndexPath:indexPath) as! ProjectTableViewCell
// Remove indenting of cell
cell.separatorInset = UIEdgeInsetsZero
cell.layoutMargins = UIEdgeInsetsZero
// Set project name
cell.ProjectName.text = "\((indexPath.row)+1). \(self.ProjectsArray[indexPath.row].ProjectName)"
// Set action button
cell.ActionButton.tag = indexPath.row
cell.ActionButton.addTarget(self, action: #selector(ProjectsController.projectActions(_:)), forControlEvents: .TouchUpInside)
return cell
}
func reloadTableViewAfterDelay()
{
ProjectsTableView.performSelector(#selector(UITableView.reloadData), withObject: nil, afterDelay: 0.1)
}
@IBAction func projectActions(sender: UIButton) {
let index = sender.tag
let optionMenu = UIAlertController(title: nil, message: self.ProjectsArray[index].ProjectName, preferredStyle: .ActionSheet)
// Report Progress
let reportProgressAction = UIAlertAction(title: "Report Progress", style: .Default, handler: {
(alert: UIAlertAction!) -> Void in
self.performSegueWithIdentifier("ShowReportProgress", sender: sender)
})
// Cancel
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: {
(alert: UIAlertAction!) -> Void in
})
optionMenu.addAction(reportProgressAction)
optionMenu.addAction(cancelAction)
self.presentViewController(optionMenu, animated: true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let button = sender where segue.identifier == "ShowReportProgress" {
let upcoming: ReportProgressController = segue.destinationViewController as! ReportProgressController
print(self.ProjectsArray[button.tag].ProjectId)
upcoming.ProjectId = self.ProjectsArray[button.tag].ProjectId
upcoming.ProjectName = self.ProjectsArray[button.tag].ProjectName
}
}
// MARK: Get Projects Function
func getProjects() -> Void
{
var params = Dictionary<String,AnyObject>()
params = ["user_id":CFunctions.getSession("id")]
WebServiceController.getAllProjects(params){ (type, response, message) -> Void in
if (type == ResponseType.kResponseTypeFail)
{
// Show Error
let alert = UIAlertController(title: "Error(s)", message:"Unable to load projects.", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
}
else
{
//debugPrint(response)
if(response.count>0)
{
self.ProjectsArray = response
}
else
{
// Show Error
let alert = UIAlertController(title: "Error(s)", message:"No projects found.", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default) { _ in })
self.presentViewController(alert, animated: true){}
}
self.reloadTableViewAfterDelay()
}
}
}
}
I want each cell's index value to be intact whether I scroll down or up.
Upvotes: 1
Views: 2500
Reputation: 1369
In your iPhone screen, for example you can see 5 cells at a time.
So, first time, when you load tableView cellForRowAtindexPAth method will be called for first 5 cells.
"As you mentioned, first time you are loading and tableView indexes are correct."
Now when you scroll down, cellForRowAtIndexPath method will be called for only 6 and 7.
"Till this time everything works ok, as you mentioned. AS you can see overall indexpath as intact 1,2, 3,4,5,6,7."
*Dark cells are currently visible on your screen.
Now when you scroll up {2 cells}. Now you can see the current visible cell's on your screen are 1,2,3,4,5.
Here, cellForRowAtIndexPath method will be called for ONLY cells numbered 2,1.
Because cell numbers 3,4,5 are already loaded/visible in your screen.
So, your Print log will be 1,2,3,4,5,6,7,2,1.
Upvotes: 1
Reputation: 677
Need to get indexPath of button using tableViewCell's than used below code
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellId: NSString = "Cell"
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellId as String)! as UITableViewCell
let ActionButton : UIButton = cell.viewWithTag(10) as! UIButton
ActionButton.addTarget(self, action: #selector(ViewController.projectActions(_:)), forControlEvents: UIControlEvents.TouchUpInside)
return cell
}
@IBAction func projectActions(sender: UIButton) {
let buttonPosition = sender.convertPoint(CGPointZero, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(buttonPosition)
print(indexPath?.row)
}
Upvotes: 0
Reputation: 8322
You need to get indexPath of button using tableViewCell's hierarchy.
@IBAction func projectActions(sender: UIButton) {
let button = sender as! UIButton
let view = button.superview!
let cell = view.superview as! UITableViewCell
let indexPath = self.reloadTableViewAfterDelay.indexPathForCell(cell)
let index = indexPath.row
}
if let button = sender where segue.identifier == "ShowReportProgress" {
let upcoming: ReportProgressController = segue.destinationViewController as! ReportProgressController
let view = button.superview!
let cell = view.superview as! UITableViewCell
let indexPath = self.reloadTableViewAfterDelay.indexPathForCell(cell)
let index = indexPath.row
print(self.ProjectsArray[index].ProjectId)
upcoming.ProjectId = self.ProjectsArray[index].ProjectId
upcoming.ProjectName = self.ProjectsArray[index].ProjectName
}
Upvotes: 0
Reputation: 1455
You should add UITableViewDelegate
and UITableViewDataSource
to your class. and set it ProjectsTableView.delegate = self
in viewDidLoad
.
Upvotes: 0