I am working with a MasterDetail View using SplitViewController in iPad, My Master View has a tableView where I add rows dynamically. So the newly added row sits at zero postion. So what I want to achieve is that, I want to select the newly added row as soon as it is added to the Master table. Later when the user adds a new row, that row should be selected and the previously added row should be deselected.
For this I have written the code below
- (void)selectNewlyAddedRow
{
NSIndexPath* selectedCellIndexPath= [NSIndexPath indexPathForRow:0 inSection:0];
[m_tableView selectRowAtIndexPath:selectedCellIndexPath animated:TRUE scrollPosition:UITableViewScrollPositionNone];
[self tableView:m_tableView didSelectRowAtIndexPath:selectedCellIndexPath];
}
This code works if I write it in function ViewDidAppear, but if I write it in cellForRowAtIndexPath it doesnt work..
Please provide me correct understanding where I am going wrong.
Regards
Ranjit
viewDidAppear wont get call when you add new row so you just register for table reloaded notification and in method for that notification call select first row I hope it will help you....
- (void)selectFirstRow{
if ([self.tableView numberOfSections] > 0 && [self.tableView numberOfRowsInSection:0] > 0) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionTop];
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
}
}
Implement this delegate -
-(void)tableView:(UITableView )tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath)indexPath{
[NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:#"Table Reloaded" object:nil];
}
In your view controller add this line in viewdidload
-(void)viewDidLoad{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(tableReloaded:) name:#"Table Reloaded" object:nil];
}
-(void) tableReloaded:(NSNotification*)notification{
[self selectFirstRow];
}
Related
I have a class that allows a user to add entries to a server-side table. Everything works correctly until I attempt to refresh the UITableView with the new data. I make a server call to get the new dataset, use it to refresh the NSArray that is the data source for the table, and then attempt to reload the table. Here is the method that is called when the data comes back from the server:
- (void) logEntriesRefreshed : (NSNotification *) notification {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:#"log_entries_refreshed"
object:nil];
NSLog(#"returned from log entries fetch");
_logEntriesArray = [LogEntriesDataFetcher getLogEntriesArray];
[_tableView reloadData];
_activityIndicator.hidden = YES;
[_activityIndicator stopAnimating];
NSLog(#"log entries array count: %lu", [_logEntriesArray count]);
[_tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
animated:NO
scrollPosition:UITableViewScrollPositionNone];
}
It's this last line that is the problem. I want to programmatically select the first row in the table (there has to be at least one, since I just added a row). But it appears that this line never executes. Note this method, which should go next:
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"here");
UITableViewCell *previousCell = (UITableViewCell *)[_tableView cellForRowAtIndexPath:_previousIndexPath];
previousCell.backgroundColor = [UIColor clearColor];
previousCell.textLabel.textColor = [SharedVisualElements primaryFontColor];
UITableViewCell *cell = (UITableViewCell *)[_tableView cellForRowAtIndexPath:indexPath];
cell.contentView.backgroundColor = [SharedVisualElements secondaryFontColor];
cell.textLabel.textColor = [SharedVisualElements primaryFontColor];
_previousIndexPath = indexPath;
// get the file attributes for the cell just selected
_currentEntry = (LogEntry *)[_logEntriesArray objectAtIndex:[indexPath row]];
NSLog(#"array count: %lu", (unsigned long)[_logEntriesArray count]);
NSLog(#"current entry: %ld", (long)[indexPath row]);
_isExistingEntry = YES;
_arrayPositionOfEntryBeingEdited = [indexPath row];
[self initializeValues];
[self initializeObjects];
[self captureStartingValuesForStateMachine];
}
I have break points set on the selectRowAtIndexPath line and also on the first NSLog(#"here") line in didSelectRow.... I get to the selectRowAtIndexPath line but never to the didSelectRow method. My console output is consistent with that:
returned from log entries fetch
log entries array count: 7
and that is the end of it. Nothing from the didSelectRow... method. There are no errors thrown, either.
What am I missing. Seems pretty straightforward, but nothing I do seems to work.
As per Apple's documentation, calling selectRowAtIndexPath will NOT invoke the didSelectRowAtIndexPath. Take a look here.
Calling this method does not cause the delegate to receive a
tableView:willSelectRowAtIndexPath: or
tableView:didSelectRowAtIndexPath: message, nor does it send
UITableViewSelectionDidChangeNotification notifications to observers.
To specifically invoke the didSelectRowAtIndexPath delegate method, use the following code:
[[tableView delegate] tableView:tableView didSelectRowAtIndexPath:indexPath];
Hope this helps.
I am using the following code to deselect a selected table view cell when returning back to the table view in -viewWillAppear:animated.
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
I also need to reload the table view's data in this case but when you do that it clears the selected state of the selected cell so you don't see any fade animation.
Is there a way to reload the table data and also preserve the selected state to create the deselect animation?
After several attempts, I've found something that works. You need to set the deselection to occur after a "delay" (of 0 seconds) in order to make sure it happens on the next draw cycle and gets animated properly.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSIndexPath *indexPath = self.tableView.indexPathForSelectedRow;
[self.tableView reloadData];
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[self performSelector:#selector(deselectRow) withObject:nil afterDelay:0];
}
- (void)deselectRow
{
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
}
Try this in your viewDidLoad:
[self setClearsSelectionOnViewWillAppear:NO];
The obvious solution suggested by #user2970476 seems to work fine on iOS 7. For iOS 8 I slightly modified #Stonz2's answer to use blocks
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
dispatch_async(dispatch_get_main_queue(), ^(void) { // necessary for iOS 8
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
});
}
I also had to set self.clearsSelectionOnViewWillAppear = NO; in viewDidLoad because the IB setting got ignored.
You will need to reload your table view first, select the row you want to indicate then perform the deselect animation. The issue you are having is your order of operation is incorrect.
You can save the current selection with
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
[self.tableView reloadData];
[self.tableView selectRowAtIndexPath:selectedRow animated:NO scrollPosition:UITableViewScrollPositionNone];
I have a feature in my app where the user can change the color scheme of the app. The app uses a Split View Controller, with a MainTable and DetailView table. Everything works fine except for the MainTable. What is failing is that the MainTable reloadData method is not causing the cells to be redrawn.
It should be noted that I am changing globalHighContrast and sending the notification from a UIModalPresentationFormSheet viewController, so the tables are kind of visible on the screen while the viewController is active.
I am triggering the screen update from a notification, like this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reloadAllTables)
name:#"contrastModeChanged"
object:nil];
Then, to make sure that I call reloadData on the main thread, I am handling the notification like this:
-(void)reloadAllTables{
[self performSelectorOnMainThread:#selector(doReloadAllTables) withObject:nil waitUntilDone:NO];
}
-(void)doReloadAllTables{
[self showIcon];
if( globalHighContrast ){
theTable.backgroundColor = [Colors lightBkgColor];
self.view.backgroundColor = [Colors lightBkgColor];
} else {
theTable.backgroundColor = [Colors darkBkgColor];
self.view.backgroundColor = [Colors darkBkgColor];
}
[detailViewController configureView:currentMainMenu];
[detailViewController.subTable reloadData];
[theTable reloadData];
// desperate try to force it to work
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:currentMainMenu inSection:0];
[self tableView:theTable didSelectRowAtIndexPath:indexPath];
}
Both reloadAllTables and doReloadAllTables are being called, but
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
is not being called.
As soon as I tap a cell on the MainTable it does update correctly to the new color scheme.
Also, there is a desperate attempt to workaround this by trying to simulate the MainTable touch, but that doesn't work either.
You can try to put code for updating you scheme in -(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath method...
I am trying to scroll my table view to show my last cell when keyboard is active.
This is what i am doing in tableView:cellForRowAtIndexPath:...
if (keyboard) {
CGFloat calculatedPosY = 70 * ([Array count]-1);
MyTable.contentOffset = CGPointMake(0.0, calculatedPosY);
}
its working correctly for first time but for second time when its reloading table its not scrolling. For third time its again scrolling and showing last cell of table. Alternatively the code is working and the log gives the same content offset (0,280).
Please tell were I am doing wrong.
Thanks in advance.
You need to do two things - 1) make sure the table view fits on the visible part of the screen not covered by the keyboard and 2) scroll the table to the last row.
To resize the view, I'd register to detect the keyboard appearing:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector (keyBoardWillShow:)
name: UIKeyboardWillShowNotification object: nil];
The keyBoardWillShow: method would be called when the keyboard appears and you can resize the tableview:
- (void)keyBoardWillShow:(NSNotification *)aNotification
{
NSValue *value = [[aNotification userInfo] objectForKey: UIKeyboardFrameEndUserInfoKey];
CGRect keyboardRect = [value CGRectValue];
CGFrame tableFrame = self.tableView.frame;
tableFrame.height -= keyboardRect.size.height
self.tableView.frame = tableFrame;
}
Lastly, scroll to the last cell (you could do this in keyBoardWillShow: if you like):
[tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:lastRowIndex inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
Don't do it in tableView:cellForRowAtIndexPath:.. this method iterates through all array elements and creates cell. It means this calculation runs array[N] times.
Instead run it once after [self.tableView reloadData];
Or try this:
NSInteger *cells_count // You can have it from - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section.
//Run this after [tableView reloadData]
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:cells_count-1 inSection:0];
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated: NO];
I would suggest you use – scrollToRowAtIndexPath:atScrollPosition:animated:. Its a method in the UITableView.
I am using this script for slide menu "SASlideMenu" and I am trying to select the first item by default, but with no success. Does anybody know how to do it?
I tried this:
(void) viewDidLoad{
[super viewDidLoad];
...
self.tableView.delegate = self;
NSString *CellIdentifier = #"homeCell";
UITableViewCell *homeCell = [self.tableViewdequeueReusableCellWithIdentifier:CellIdentifier];
[homeCell setSelected:YES];
}
UPDATE (improved explanation)
I am doing this in "SASlideMenuViewController.m"
UPDATE 2
Further to main question I would like to ask when user enters applicationWillEnterForeground how to select then first cell and switch to home page?
P.S. I dont want to open another question for that, because it could be treated like a "duplicate".
UPDATE 3
I don't know how to switch to home page when user is on another view. For example we switch user to home page when he came back to app(foreground), but this doesn't work in next situation; in SASlideMenu I have list of categories and when I go to one of this categories and then exit from app and back again into app, it switch me to home page and that's ok. But when I click on one of categories in SASlideMenu and then click inside of category view on one of articles and exit app and enter again then it does not switch me to home page and I don't know why. It's navigation based app.
Question 1: To select the first item by default on launch of app
Assuming that you are using the SASlideMenu code mentioned in the question. In your ExampleMenuViewController class you need to add the following,
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
if (indexPath) {
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
__________________________________________________________________________________
Question 2: To select the first item by default and to set home screen in applicationWillEnterForeground
Add the following method declaration in your ExampleMenuViewController.h file,
#interface ExampleMenuViewController :SASlideMenuViewController<SASlideMenuDataSource>
- (void)selectFirstRowAndSetHomePage;
#end
Add the following method in your ExampleMenuViewController.m file,
- (void)selectFirstRowAndSetHomePage {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
if (indexPath) {
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
NSString *identifier= [self.slideMenuDataSource initialSegueId];
if (identifier) {
[self performSegueWithIdentifier:identifier sender:self];
}
}
Implement the following in your SASlideMenuAppDelegate.h class,
#import "ExampleMenuViewController.h"
And,
- (void)applicationWillEnterForeground:(UIApplication *)application {
ExampleMenuViewController *exampleMenuViewController = (ExampleMenuViewController *)self.window.rootViewController;
[exampleMenuViewController selectFirstRowAndSetHomePage];
}
That should help.
__________________________________________________________________________________
This should work for you:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
Check with this code:
NSIndexPath *indexPath = [[NSIndexPath alloc] initWithIndex:0];
UITableViewCell *homeCell = [[self.tableView cellForRowAtIndexPath:indexPath] setSelected:YES];
When viewDidLoad is called your table is still empty. You are not even taking a cell which is inside the table.
Try this code:
NSIndexPath firstCellIndex = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView selectRowAtIndexPath:firstCellIndex
animated:NO
scrollPosition:UITableViewScrollPositionNone];
And put it into viewDidAppear.