Cell change being applied to multiple cells - uitableview

When a user clicks on a button inside the cell of my tableView, I want it to change multiple properties of the cell. I assign a the indexPath.row value as the tag value to each cell in a different class. Here's my code:
#IBAction func upvote(sender: AnyObject) {
let row = sender.tag
let indexPath = NSIndexPath(forRom: row, in Section: 0)
let cell = tableView.cellForRowAtIndexPath(indexPath) as FeedPageTableViewCell
cell.upVote.setImage("green", forState: .Normal)
}
The problem is that though it works on the cell I click on, it also applies to each cell 5 rows below that.

It isn't proper to update a UITableViewCell by retrieving it from the UITableView like that. I do not think this does what you're expecting:
let cell = tableView.cellForRowAtIndexPath(indexPath) as FeedPageTableViewCell
cell.upVote.setImage("green", forState: .Normal)
Instead, you should have some instance array of data that you keep up-to-date. In this case you may have an array of booleans, where the index is the row and the value is whether not it is "upvoted". Then in your upvote function you would update the boolean and reload your table/cell to render the appropriate data.

Add Target to the UIButton , Use this Method for the action on button
-(void)handeltap:(UIButton*)sender{
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
NSUInteger lastIndex = [indexPath indexAtPosition:[indexPath length] - 1];
}
On lastIndex will give the correct index path , Try and if not happens let me know.

Related

Passing indexpath tag to button inside prototype cell in tableview

I have created a tableview prototype cell in storyboard and I have added a button to cell and set its tag to indexpath.row. When I scroll my cells the scrolled cell on the top of tableview always set tag to zero instead of correct tag.
public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("autoLoadReuseIndentifier", forIndexPath: indexPath)
print("indexpath :\(indexPath.row)")
cell.contentView.viewWithTag(100)?.layer.cornerRadius = 5
tableView.separatorStyle = UITableViewCellSeparatorStyle.None
let tempDict : NSDictionary = savedCardsArray.objectAtIndex(indexPath.row) as! NSDictionary
let bankName = cell.contentView.viewWithTag(102) as! UILabel
deleteButton = cell.contentView.viewWithTag(106) as? UIButton
deleteButton?.tag = indexPath.row
deleteButton?.addTarget(self, action: "deleteCard1:", forControlEvents: UIControlEvents.TouchUpInside)
print("delete button:\(deleteButton)")
// print("indexpath delete tag :\(deleteButton.tag)")
if(self.isSetUpAutoloadSelected){
deleteButton?.hidden = true
}else{
deleteButton?.hidden = false
}
return cell;
}
Whenever I scroll the cells, delete button tag is always set to zero.
If you should go with other way so use follow code and get indexPath.
func deleteCard1(_ sender:deleteCard) {
let buttonPosition:CGPoint = sender.convert(CGPointZero, to:self.tableView)
let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
}
I think you don't need to follow this approach because firstly you set button tag statically in storyboard and again you are change it's tag in cellforrowatindexpath so when you scroll, cell will never find button with tag 106.If you want to follow this approach then you need to create customButton and add Variable of type NSInteger or whatever you want and store indexpath.row into that variable of customButton.
Another Approach is Create CustomTableViewCell Class and create button outlet in this custom Cell class and set indexpath.row into button tag like this
CustomCellClassObject.buttonName.tag = indexPath.row
As Sumit said, it’s better to use a custom cell and create outlet for the buttons and labels, as fetching sub views using tags will be tough to maintain the code in the future.
Additionally, you don’t have to create the variable deleteButton, as I don’t see a valid purpose.
Assign tag to the button and add target in cellForRowAtIndexPath, it should work fine.

How do I pass indexPath (not indexPath.row) into my IBAction for my UITableViewCell

I am developing a to-do application. In my app, I press a checkbox button to delete a row. I write this code in order to pass the indexPath.row into my checkbox button:
cell.checkbox.tag = indexPath.row
cell.checkbox.addTarget(self, action: "checkAction:", forControlEvents: .TouchUpInside)
The first code allows me access to indexPath.row and the second one allow me to create a function for when my button is pressed. This is the function I use for when the button is pressed:
#IBAction func checkAction(sender: UIButton) {
taskMgr.removeTask(sender.tag)
tblTasks.reloadData()
}
Now, I want to add animation for when it deletes, so it doesn't look so sudden. The code I would use is this:
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
However, I only have access to indexPath.row in my checkAction function. How do I get access to the indexPath (without sacrificing indexPath.row)?
Tags can give you an erroneous row if you move rows around or add or delete rows until you reload the entire table. So, instead of using tags, you can use indexPathForRowAtPoint: in your button method to get the indexPath.
#IBAction func checkAction(sender: UIButton) {
let point = sender.convertPoint(CGPointZero, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(point)
taskMgr.removeTask(indexPath.row)
tblTasks.reloadData()
}
You can save the indexPath in the view controller:
class ViewController: UITableViewController {
var toDeletedIndexPath: NSIndexPath?
#IBAction func checkAction(sender: UIButton) {
var cell = sender
do {
cell = cell.superview
} while cell.isKindOfClass(UITableViewCell)
self.toDeletedIndexPath = self.tableView.indexPathForCell(cell)
tblTasks.reloadData()
}
}
then
tableView.deleteRowsAtIndexPaths([self.toDeletedIndexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
If you have only one section in table view than I would suggest you to assign tag to the instance of UIButton and the tag value should be equal to the indexPath.row
But if you have more than one section and you need to access index path than in the touch event of the button via sender than I think you should subclass UIButton and add NSIndexPath attribute. When you create instance of Custom UIButton then assign index path to the index path attribute of the custom button instance. Now when the button is clicked you can access the index path as it is the attribute of Custom UIButton.

for loop to check on cell.textlabel or cell.tag

can anyone please help me with how to access a specific cell with its tag or text label, i have a table view with a list of buttons above, those buttons filed with the thumbnail of the cell when ever the cell was clicked then also the background of the cell goes red.
when i click on the cell once again - when its red - the background goes white - default - also the thumbnail removed from the button above.
also when i click on the button the photo in it should disappear as well as the background of the cell itself, it should go white. and thats what i couldnt do .. can anyone help me with that, how to access the the cell to change its background when the cell.tag or cell. texlabel.text is given.
thanx in advance
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = myTable.dequeueReusableCellWithIdentifier("intrest") as UITableViewCell
cell.tag = indexPath.row + 1
cell.textLabel?.text = InterstArr[indexPath.row]
var img = UIImageView(frame: CGRect(x: 10, y: 3, width: 40 , height: 40))
img.image = UIImage(named:"\(InterstArr[indexPath.row]).png")
cell.selectionStyle = UITableViewCellSelectionStyle.None
cell.indentationLevel = 1
cell.indentationWidth = 45
cell.addSubview(img)
return cell
}
I did it with the help of #natuslaedo ... i just stored the value of the index path when i clicked on the row .. then in my if statements to check on the 5 buttons above and deselect them, i took the indexpath stored and accessed the cell to change its color ... i initialise the index paths variables like this var firstIP:NSIndexPath!
First of all, add the following line to your didSelectRowAtIndexPath: function:
yourTableView.selectRowAtIndexPath(indexPath, animated: true, scrollPosition: UITableViewScrollPosition.None)
And target selector for your button is like as follows:
func buttonPressed(sender: UIButton!) {
// Code to remove photo from your button
// ......
// To remove background effect of cell
let indexPaths:Array<NSIndexPath> = yourTableView.indexPathsForSelectedRows() as Array<NSIndexPath>
// If you have implemented your own class which inherits from UITableViewCell, use its name while type casting and explicit typing
for indexPath in indexPaths {
let cell:UITableViewCell = yourTableView.cellForRowAtIndexPath(indexPath) as UITableViewCell!
// REMOVE BACKGROUND EFFECT HERE (using cell)
}
}
You shouldn't be trying to match on tag or text content, everything should be based on row or index path so that it correlates back to your data model.
Consider creating a custom class for your button data storage so you have an array with one entry per button and the class holds the index path of the associated cell, the image, etc. Then, when a button is clicked it's easy to update your main data model and the cell (if it's visible).
You can get the indexPath of cell in click event this way,
func buttonClick(sender:UIButton)
{
let position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
if let indexPath = self.tableView.indexPathForRowAtPoint(position)
{
println(indexPath.row)
println(indexPath.section)
}
}
Another way to match buttons in a cell with your data model is to store the indexPath of the cell with the cell. When the button is tapped, your target method which is a method on the cell, will get called and you can use the indexPath and pass it up to your data model using a delegate call.
I'd give you more details but I am not sure if that is what you are asking.

How can I properly recognize what cells have had their subviews modified by the user?

I have a tableView and inside of it is custom cells. The whole thing syncs fine with NSUserDefaults, however there is a problem. These custom cells have textfields inside of them among other interactive subviews. When the user modifies the textfield of a cell, how can I determine the index of the cell so that I can properly match the data model to the tableView? I've tried assigning an index variable to each cell in the cellForIndexPath(...) method but this causes alot of potential bugs.
To get a sense for what my tableView looks like, just look at the Apple reminders app, which has custom cells which also contain interactive subviews.
Anyway, any alternative methods of determining this sort of data? There are tons of reminders apps that contain interactive subviews so there must be a better way!
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as GoalTableViewCell
cell.goalTextField.text = goalsArray[indexPath.row].title
cell.checkmarkImageView.visible = goalsArray[indexPath.row].checkmarked
cell.blackLineView.visible = goalsArray[indexPath.row].checkmarked
cell.enabled = goalsArray[indexPath.row].enabled
cell.isLastCell = goalsArray[indexPath.row].isLastCell
cell.priority = goalsArray[indexPath.row].priority as Priority
cell.indexInTable = indexPath.row //this is how I assign the index of a cell. Problem is that cellForRowAtIndexPath is inconsistent.
return cell
}
Then to retrieve that data
func finishCreatingGoal(notification : NSNotification) { //this executes after a textfield resigns first responder status
if (notification.name == "FinishCreatingGoal") {
var userInfo = notification.userInfo!
var text = userInfo["text"]! as String
var index = userInfo["index"]! as Int //this index is pulled from the cell.indexInTable variable which is assigned in CellForIndexPath(...)
goalsArray[index].title = text //this line crashes often after various manipulation of the tableView such as deleting rows and reordering them.
saveGoals(goalsArray)
let indexPath = NSIndexPath(forRow:index,inSection:0)
self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.None)
}
}
UITableView has a method indexPathForRowAtPoint: which you can use.
// textField is a subview of a cell
let point = tableView.convertPoint(CGPointZero, fromView: textField)
if let indexPath = tableView.indexPathForRowAtPoint(point) {
let yourObject = dataSource[indexPath.row]
// do something with it
}
Should be pretty self-explanatory.
A better option would be to have a Goal class (which is what I'm guessing you already have in goalsArray) that would contain all the information that the user stores. Then instead of setting all of the UI from the table view's delegate, just pass it a Goal object. I.e.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as GoalTableViewCell
cell.goal = goalsArray[indexPath.row]
return cell
}
Then in your GoalTableViewCell class you keep a reference to this goal as well as update the UI. Then when you want to check the index of the cell's Goal you can just search for it.
You could get the index of the cell via TouchEvents i.e. getting the point coordinates in the UITableview. Then extracting the index path from point location and then via which you could get the cell index. Here is the method that i used in my code to do the same.
- (void)checkButtonTapped:(id)sender event:(id)event
{
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];
if (indexPath != nil)
{
NSLog(#"the cell index : %d", (int)indexPath.row);
}
}
Update ---
This is how its done in Objective-C. I haven't still read about swift so i cannot translate it in swift.

Get the row of UITextField inside UITableViewCell

I have two UITextFields inside a custom cell of a UITableView.
I need to edit and store values of the textFields.
When I click inside a UITextField I have to know the row it belongs to in order to save the value to the correct record of a local array.
How can I get the row index of the textField?
I tried :
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
currentRow = [self.tableView indexPathForSelectedRow].row;
}
But the currentRow does not change when I click inside the UITextFieldRow.It changes only when I click (select) the entire row...
The text field did not send touch event to the table view so indexPathForSelectedRow is not working. You can use:
CGPoint textFieldOrigin = [self.tableView convertPoint:textField.bounds.origin fromView:textField];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:textFieldOrigin];
In iOS 8 I found that the simulator and device had different number of superviews, so this is a little more generic and should work across all versions of iOS:
UIView *superview = textField.superview;
while (![superview isMemberOfClass:[UITableViewCell class]]) { // If you have a custom class change it here
superview = superview.superview;
}
UITableViewCell *cell =(UITableViewCell *) superview;
NSIndexPath *indexPath = [self.table indexPathForCell:cell];
Try this
//For ios 7
UITableViewCell *cell =(UITableViewCell *) textField.superview.superview.superview;
NSIndexPath *indexPath = [tblView indexPathForCell:cell];
//For ios 6
UITableViewCell *cell =(UITableViewCell *) textField.superview.superview;
NSIndexPath *indexPath = [tblView indexPathForCell:cell];
1>You can achieve it by programmatically creating textfields in your CellForRowAtIndexPath and setting text field's tag as indexpath.row.
then textFieldDidBeginEditing you can just fetch textField.tag and achieve what you want.
2>another way is to have 2 custom cells in one table view. in that way you cam place text feild individually and set their tags from your utility panel.
What I do is create a custom cell, and put whatever custom UI elements I need inside and create a property indexPath which gets set when the cell is dequeued. Then I pass the indexPath along to the custom elements in didSet.
class EditableTableViewCell: UITableViewCell {
#IBOutlet weak var textField: TableViewTextField!
var indexPath: IndexPath? {
didSet {
//pass it along to the custom textField
textField.indexPath = indexPath
}
}
}
class TableViewTextField: UITextField {
var indexPath: IndexPath?
}
In TableView:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "EditableCell") as! EditableTableViewCell
cell.indexPath = indexPath
return cell
}
Then I implement the UITextFieldDelegate protocol, and since the textField has its indexPath you will always know where it came from.
Not sure where the best place to set the delegate is. The easiest is to set it when the cell is dequeued.
override func textFieldDidEndEditing(_ textField: UITextField) {
guard let myTextField = textField as? TableViewTextField else { fatalError() }
guard let indexPath = myTextField.indexPath else { fatalError() }
}

Resources