I have a UITableView with four cells, in single selection mode. When the view is first loaded, I programmatically select one of the cells using -selectRowAtIndexPath:animated:scrollPosition: based on a stored preference. After this, the user can interact with the table.
The methods -tableView:willSelectRowAtIndexPath: and -tableView:didSelectRowAtIndexPath: are called on my table view delegate every time the user taps any cell. However, -tableView:willDeselectRowAtIndexPath: and -tableView:didDeselectRowAtIndexPath: (that's DEselect) don't start being called until the user manually taps that first programmatically selected cell.
Any idea why this might be happening? Are there any workarounds besides manually calling -[UITableView selectRowAtIndexPath:animated:] for every cell except the one that the user tapped?
If you do invoke [UITableView selectRowAtIndexPath:animated:] too soon and you are using a UITableViewController, you probably have conflict with the UIViewController clearsSelectionOnViewWillAppear (defaults to YES). Try setting self.clearsSelectionOnViewWillAppear = NO; in your viewDidLoad
This is too long for comment so I'm posting this as an answer.
Just created test project with tableView. I call [UITableView selectRowAtIndexPath:animated:] on viewDidLoad and I have one cell selected. Then I'm selecting another cell (without deselecting first one).
I have this output:
[SDTVTViewController tableView:willSelectRowAtIndexPath:]
[SDTVTViewController tableView:willDeselectRowAtIndexPath:]
[SDTVTViewController tableView:didDeselectRowAtIndexPath:]
[SDTVTViewController tableView:didSelectRowAtIndexPath:]
And I have first cell deselected and second one selected.
I have no idea, why it's not working in your code. When you're calling [UITableView selectRowAtIndexPath:animated:] and can you post code of UITableViewDelegate methods that you've implemented?
I was wrapping my initial data source updates and -selectRowAtIndexPath:... calls in
[_tableView beginUpdates];
// ...
[_tableView endUpdates];
Apparently, like -reloadData, this clears the current selection (not sure if that's by design). I moved the call to -selectRowAtIndexPath:... to after -endUpdates and now everything works as intended.
Related
The problem I'm having is with triggering code as soon as a cell is displayed on screen.
What I have is grouped cells, much like in the settings application for iOS, with one segueing to a view with a date picker. After closing that view, i save the selected date, but I want to be able to fire my code so I can display the date in the detail text of a cell.
The cell that I want to display the date in has a swift file attached to it, as a UITableViewCell subclass/class.
I need a method similar to the viewDidLoad() on normal UIViewControllers where I can run code as soon as it appears on screen.
Something like this:
when cell comes on screen {
runCode()
}
I've tried with awakeFromNib, but with my understanding it only happens when the view is loaded from a previous view, not when it is transitioned back from a segue.
It looks like you're trying to refresh the contents of the cell, not looking for something similar to viewDidLoad().
What you'll want to do is after a user selects a date, you call the reloadData() on your tableview or to be more efficient, - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; with the updated cell's indexPath.
I have a UICollectionView I use like a tool selection container. You have many tools (the cells), when you select one, the former selected is deselected, and so on... You can only have one tool selected at a time.
The problem is that I can't manage to have the cells selected. When I tap the cell, the collectionView:didSelectItemAtIndexPath: is called. I then reload the selected cell to change it's appearance (I change the alpha of the image) by using the [collectionView reloadItemsAtIndexPaths:#[indexPath]].
Well that reloads the cell ok, except one damn thing: in the collectionView:cellForItemAtIndexPath: the cell never get the selected property set to YES! So I can never change the alphabecause I never know when I must draw a selected cell or not.
Worse: [collectionView indexPathsForSelectedItems] is always empty!!!
I don't even mention the collectionView:didDeselectItemAtIndexPath: that is never called...
Well, if someone can help me understand what is going on, thanks in advance...
When you call reloadItemsAtIndexPaths: the collection view discards those cells and creates new ones, therefor discarding the selected state of the cells.
I'd suggest a couple of different options:
1.) In collectionView:didSelectItemAtIndexPath: call cellForItemAtIndexPath: to get a reference to the selected cell and update it's appearance there.
2.) (My Favorite) Use a custom subclass of UICollectionViewCell if you're not already, and override the method setSelected:. There you'll be notified when the cell is selected and you can update the appearance from within the subclass.
With regards to selecting multiple cell at once, you should try the allowsMultipleSelection property [myCollectionView setAllowsMultipleSelection:YES] The docs for that are here: https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionView_class/Reference/Reference.html#//apple_ref/occ/instp/UICollectionView/allowsMultipleSelection
Otherwise, #daltonclaybrook's answer is sufficient.
I have a problem that is driving me crazy.
I have an UITableView that is always in editing mode (it has to be).
The user can add new rows to it.
The navigationItem.leftbarButton of the tableViewController pushes a new controller just to do it, let's call it "newRowsVC".
Before the push the tableViewController set itself as the delegate of the newRowsVC, the protocol has only a method:
-(void) aNewRowHasBeenCreated
{
[self.tableView reloadData];
}
I start adding rows and everything works fine, each now row is immediately displayed in the tableViewController until the last new row will force the tableView to scroll because there won't be anymore screen real estate for it. Here, I have no idea why, the tableView, only it, is as frozen, with no scrollbar and doesn't respond to input. The app continues to run without a crash and I can even dismiss the tableViewController by tapping the navigationItem.rightbarButtonItem.
I can keep creating new rows, they are added to the array, the number of row in the tableView data source is computed correctly. But the table is like dead.
If I dismiss the tableViewController and then I come back to it, I see that all the rows previously created, also the ones not shown as soon as they were created are there!
I really do not have idea of how I can fix this.
The first thing I tried was to force the scroll after the reload of the table but it didn't fix it.
-(void) aNewRowHasBeenCreated
{
[self.tableView reloadData];
[self.tableView setScrollEnabled:YES];
}
I also tried forcing the tableView to scroll to the last row but it didn't fix it.
-(void) aNewRowHasBeenCreated
{
[self.tableView reloadData];
[self.tableView setScrollEnabled: YES];
[self.tableView scrollToRowAtIndexPosition: [self.tableView indexPathsForVisibleRows]last object] atScrollPosition: UITableViewScrollPositionBottom animated:YES];
}
I check the number of rows each time the table is reloaded. No error, the number is perfect and the new rows data are correctly in the array, also the data for the cells not shown.
I thought it could be because the tableView is always in editing mode so I tried setting it to NO but nothing changes, id din't fix the problem.
Two notes:
1)the tableView has to be the delegate of each one of it's custom cells. They have an UITextField and an UIStepper, handled by the tableViewController. I already tried to not set the tableViewController as the delegate of its custom cells but nothing changes so the problem is not this.
2) self.tableView.bounces = NO but this has nothing to do with the scrolling issue, the scroll is enabled.
Update: After more tests I found that if I remove the dequeue of the reusable custom cell everything works fine, so the problem should be about the reuse.
static NSString *CellIdentifier=#"MyCell"
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier];
Nicola
After hours I got it. As pointed out by Amit the problem was hidden in cellForRowAtIndexPath... It was hard to catch because it didn't happened all the times and the first times I enabled/disabled the cell reusing everything seemed the same so I did not link the problem to it. I got back on it after I tried about all the other options I had been able to think about.
The problem was in the reuse of the cells and the fact that the custom cell has the tableView as its delegate to handle its textView and the stepper without exposing them.
I got rid of all the delagion stuff and exposed the textView and the stepper as public properties of the view. By doing this I was able to set the tableViewController directly to be the delegate of the cell.textView and to add directly a target/action for the stepper.
Everything works flawslessly now.
Thanks to everyone who tried to help me!
Nicola
What's best solution to remove cell from tableview doing this not in editing mode. Like example switching to one view to another , update number of cells (after a previous delete or insert of cell) in -viewWillAppear: method.
You could use NSFetchedResultsController. It will add/remove/update cells as it gets notifications when changes are made to NSManagedObjectContext that it was created with.
If you changed (added/deleted) the values from the dataSource array then you can simply call
[tableView reloadData];
Look at beginUpdates
Call this method if you want subsequent insertions, deletion, and selection operations (for example, cellForRowAtIndexPath: and indexPathsForVisibleRows) to be animated simultaneously.
You should not call reloadData within the group
Maybe I'm missing something obvious, but I'm have a hard time programmatically setting the row selection in a tableView. The goal is to simply have a tableView open with a row already selected. The problem appears to be that I have to wait until the tableView is fully loaded before I can modify the selection.
I've read various strategies such as calling reloadData for the tableView in the viewController's viewWillAppear method, then immediately calling selectRowAtIndexPath for the target row. But when I do that, I get a range exception because the tableView has zero rows at that point. The UITableViewDelegate methods (numberOfRowsInSection, etc.) don't appear to be called immediately in response to reloadData (which makes sense if the table rows are drawn "lazily").
The only way I've been able to get this to work is to call selectRowAtIndexPath after a short delay, but then you can see the tableView scroll the selected row into view.
Surely, there's a better way of doing this?
Well, you can use another strategy. You can create a hidden table view, configure how you want and than show to user. Use the tableview.hidden = YES.