I'm having an issue with UITableView's didSelectRowAtIndexPath.
My table is setup so that when I select row it initializes a new view controller and pushes it.
The first time I tap any row in the table, the method does not get called. Once I select another row, it begins to work as normal.
I have verified this by setting a breakpoint on didSelectRowAtIndexPath. When adding an NSLog to the method I see that when I select the second row that finally pushes the new view controller, I see two log statements appear in the console at the same time.
Any suggestions?
Any chance you accidentally typed didDeselectRowAtIndexPath?
Also check the selection property of your table view in xib file. Use 'Single Selection' or 'Multiple Selection' as required.
I experienced the following issue:
first tap in row -> no effect, no selection, never
second tap and following -> correct selection behavior, always
In my case, my error was checking Show Selection on Touch in Interface Builder. You can uncheck it in IB here:
Hope that helps someone
Check If you have set any Gesture recognisers in your class. Removing gesture worked for me.
I debated even posting this answer because I think the stars kind of aligned in order for this to manifest itself.
I am having a variation of this problem and have checked the other solutions. On my table view it isn't processing the very last row of my table on the first tap. It highlights it, but didSelectRowAtIndexPath isn't being called. All the other rows work fine. But if I turn the tableview bounce on then it seems to solve the problem (but then you have to deal with a tableview bounce).
UITableViewCellSelectionStyleNone was set for the cell displaying that problem
(ios9).
I have ended up calling
[tableView deselectRowAtIndexPath:indexPath animated:NO];
first thing in
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
which is not the same, but is good enough.
makes me wonder what kind of table breakage ios10 brings.
this issue happens also when you are working with gesture recogniser within a tableView in this case you don't need to remove them, you need only to make sure that your gesture property cancelsTouchesInView = false this is a boolean value affecting whether touches are delivered to a view when a gesture is recognised.
SWIFT 2
Make sure you have this set to true:
self.tableView.allowsSelection = true
Put this above your right after your viewDidLoad() and before the super.viewDidLoad()
SWIFT 3
If you are working with Swift 3 in a class which isn't a UITableViewController and you are using UITableViewDelegate, then you may need to use the method title:
#objc(tableView:didSelectRowAtIndexPath:) func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){...}
This worked for me, although I have just migrated my project to Swift 3. It's fairly new so this may be fixed later.
SWIFT 3
None of the other answers worked in my case, what fixed it for me was:
tableView.delaysContentTouches = false
If you have set any UITapGestureRecognizer in your class, you can add this line in your didSelectRowAtIndexPath:
[tableView deselectRowAtIndexPath:indexPath animated:NO];
This worked for me.
Swift 4.2
Disabling "Delay Touch Down" in the attributes inspector solved the issue.
After that the clicks are smooth and didSelectRowAt fires immediately.
didSelectRowAt function was not called in my app in first or second tap...
I was trying to solve the problem could not find any solution. But suddenly I was recognise, I was using view.animation color change... Delegate method was not called while animation persist
In my case I had a UITableView section header with a gesture recognizer. When the header is tapped, it should insert few rows into that section and animate the insertion (like expand/collapse section).
First time when expanded and tapped, the didSelectRow delegate method was not fired. For further taps and expand/collapse actions, it was working as expected.
Per answer by #Ayoub Nouri I set cancelsTouchesInView to false, and this resolved the issue.
tapGestureRecognizer?.cancelsTouchesInView = false
What worked for me was to start typing "didSelect..." and then let autocorrect fill in the rest. Apparently some detail of my syntax was wonky. As others have said, Use the IDE!!
I had the same issue on my tableView(swift 4.2), it could be fixed with allowsMultipleSelection property on tableView.
If allowsMultipleSelection is set to true the table view selection mechanism will change in a way that by selecting each cell the tableView didSelectRowAtIndexPath: is called for the first time and by selecting the same cell for the second time the tableView didDeselectRowAtIndexPath: is called.
It means that if the number of times a cell tapped are odd (1, 3, 5, ...) then always tableView didSelectRowAtIndexPath: will be called and if the number of times a cell tapped are even (2, 4, 6, ...) then always tableView didDeselectRowAtIndexPath: will be called.
This makes tableView didSelectRowAtIndexPath: function to be called on the third selection for the same cell and so the result is double tap for calling didSelectRowAtIndexPath:!!!
If you want the tableView didSelectRowAtIndexPath: to be called on each selection for a cell then the tableView multiple selection has to be set false, tableView.allowsMultipleSelection = false.
By doing this, every time the cell is tapped tableView didSelectRowAtIndexPath: will be called on table view and by selecting another cell tableView didDeselectRowAtIndexPath: will be called for the cell was selected before and then the tableView didSelectRowAtIndexPath: will be called for the newly selected cell.
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.allowsMultipleSelection = false
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("This will be called for each cell tap")
}
Related
I have all of my view stylings in the method
override func viewWillAppear(_ animated: Bool) {
displayDataForIndexPath()
}
Which is called when I need to reload data in my ViewController. My ViewController has a CollectionView that I need to refresh.
I've added collectionview.reloadData() to my displayDataForIndexPath() method. However, all the previous cells remain, instead of disappearing, so the new cells stack on top of the old ones. What is the proper method to remove those old cells so the CollectionView reloads properly?
you dont need to delete your datasource
in your cellForItemAtIndexPath you should not add more controls to the cell. it should be only done one time on creation of the cell.
as the name states when you normaly get your cell: reuse and that means it reuses from the last state it displayed in the collection.
Hey I have a UITableView. There are 4 large cells (so they are not all displayed on the screen). At various points through the app I want to disable user interaction for all of them however I am getting a nil unwrap error when I run this code:
for row in 0...3 {
let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: row, inSection: 2)) as! AnswerTextChoiceCell
cell.userInteractionEnabled = reEnable ? true : false
}
I'm guessing it's because it can't fetch the cell because it's not displayed on the screen. How Would I go about disabling all user interaction for all the cells?
I don't want to disable the user interaction on the tableView as it will prevent the user from scrolling.
Any pointers on this would be really appreciated!
Try once.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//Configure the cell
if indexPath.row == yourcell {
cell.userInteractionEnabled = false
}
return cell!
}
Hope this helps.
Without seeing the rest of your code and your TableView the only two things I can see that would cause that crash would be:
When you are casting the cell from 'cellForRowAtIndexPath' to your 'AnswerTextChoiceCell' type
You have the wrong section passed in to your 'cellForRowAtIndexPath' function
To try and see which of the two it is I would first try omitting the 'AnswerTextChoiceCell' cast completely and see if the problem goes away, or at least that it stops crashing. The function will return a UITableViewCell anyway and the userInteractionEnabled property is available on that object anyway so this will still allow you to test out what you're after.
If that doesn't work then we'll need a bit more information about your table view such as, how many sections do you have, is it storyboard based, etc.
I am currently developing an app that features a UITableView with custom cells that contain a UITextField. The problem I am having is that, after the user inputs a number to the textfield, upon scrolling, the tableView reuses that cell and the user's previous input is initialized in the new cell. For example, if I put the number 7 in the top cell's textfield, when I scroll, the newest cell already has a 7 in it. I am wondering how I can fix this problem? Thank you for any help.
Edit: Since my problem is unclear, I basically need a way for my UITableViewCell to "talk" to the model in my UITableViewController so that when the user is done editing the UITextField in the cell, I can save the input to an array in my view controller. Thanks again
Override -prepareForReuse in your cell subclass.
In that method, set your text to nil and then call super.prepareForReuse
The quick fix is to create an array of integers to represent numbers in the table.
Then, in cellForRowAtIndexPath method, just do something like
cell.textField.text = numberArray[indexPath.row]
You need to actually save the text fields data into this array now though, in the same method add
cell.textField.addTarget(self, action: "onTextChanged:", forControlEvents: UIControlEvents.EditingChanged)
Create the ,,onTextChanged'' method like
func onTextChanged(sender: UITextField) {
let cell = sender.superview! as! UITableViewCell
let indexPath = self.tableView.indexPathForCell(cell)!
numberArray[indexPath.row] = sender.text.toInt()!
}
I let user to reorder rows in tableView. Because this event affects content - some numeric values in cells should be updated - in all the other rows, I call a reloadData in moveRowAtIndexPath. And then strange effects occur.
I.e. cells seems overlapping when touching dragger, and some cell starts to move up and down. Its important to know, that cells height are varying.
The strange, that if I remove reloadData from moveRowAtIndexPath, then all these phenomenons disappear. Only the content is invalid.
So how should I reload data after reordering?
UPDATE: What I have done meantime reconfiguring cells in viewDidLayoutSubviews instead of call reloadData end of the moveRowAtIndexPath. And it works 90% like I expect, but still rows are sometimes somewhat higher they should.
override func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
//..
reorderOccured = true
}
override func viewDidLayoutSubviews() {
if reorderOccured {
for cell in tableView.visibleCells() as! [UITableViewCell] {
let ip = tableView.indexPathForCell(cell)
if ip != nil {
self.configureCell(cell, indexPath: ip!)
}
}
reorderOccured = false
}
}
You shouldn't call reloadData after reorder.
You have to make the same changes to your data, as the changes that has been made on the screen.
For example: if you moved cell nr 2 to position 6, you have to remove your object that populate cell nr.2 and insert it again at position 6.
You didn't provide enough details, but usually you would keep your data in an array of objects. This array you have to make the changes to, so your backing datasource is valid.
Here's a link to details from Apple.
I just read the updated after I posted my answer. It seems like you really need to reloadData. In this case I advise to reload after a small delay with a dispatch_async block on the main thread. Say after 0.1.
I found here the answer: https://stackoverflow.com/a/4692563/239219
[tableView beginUpdates];
[tableView endUpdates];
This code forces UITableView to reload cell sizes only, but not cell contents.
I can't yet comment on pteofil's answer, but he is correct: if you have a numbered set of rows in a table, and you move one calling moveRow(...), your animation for the move will be cancelled by a tableView.reloadData().
As such I delayed the data reload (which renumbers all the visible cells based on the updated data source (don't forget to do that when you move stuff around!)) a couple hundred milliseconds and it works perfectly now and looks great too.
So i have a UITableview with 3 different tabs setup.
I want to scroll the table to a specific item.
If user added a new item, table to scroll all the way to the bottom because that's where the item will be added.
If user picks one of the items and edits it, it should come back to the same position on the table. Not scrolled to the top or bottom.
This is what I have:
This is a class variable.
NSIndexpath lastScrollY;
In my ViewWillDisappear :
lastScrollY = this.tableView.IndexPathForSelectedRow() ;
In my ViewWillAppear:
if(lastScrollY != null) {
this.tableView.ScrollToRow(lastScrollY, UITableViewScrollPosition.None, true);
}
this does not seem to work.
And that is only for when user selects a saved item.
What about when then add a new item/row to the table?
Please explain in detail as I am new to this.
Thank you for your time!
For after you add a new cell to your tableView, use the same method you did before, but change the scroll position (this code is in Objective-C, but does the same thing as yours. I'll try to help with a translation if you need one):
[tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionBottom
animated:true];
You might also want to set lastScrollY in an override of the method
(void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath
using the automatically passed in value indexPath to save to lastScrollY.
Lastly, I'm not sure if this will help, but in your current scrollToRow call, you have it scrolling to "none", try having it scroll to UITableViewScrollPositionTop instead. If that still doesn't work, try calling [tableView reloadData] at the end of your viewWillAppear method, which is probably something along the lines of tableView.reloadData() in your language.
Hope this helps!