How to scroll to a row in UITableView on first open - objective-c

I have a UITableView which holds a number of choices and allows user to select one of these choices. I mark the selected row with a checkmark accessory.
On an iPad, I open this in a Pop over on click of a button and the table view shows up with one of the rows checked. But sometimes this checked row is not on screen and one needs to scroll to get to it. I would like to scroll to this checked row when I open the pop over. I have tried doing a scrollToRowAtIndexPath:atScrollPosition:animated: in viewDidAppear but I find that viewDidAppear is called before any call to cellForRowAtIndexPath and the scrolling to selected row doesn't happen
- (void)viewDidAppear:(BOOL)animated
{
NSLog(#"viewDidAppear called");
[super viewDidAppear:animated];
//currentSelectedIndexPath is a correct index path
[self.tableView scrollToRowAtIndexPath:currentSelectedIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:NO];
}

Try calling [self.tableView reloadData] just before scrolling in viewDidAppear.

Related

iOS animate partial cells to show full cell

I've got an UITableViewCell with a UITextField in it. When I click the UITextField I want to make sure the entire cell is visible in the tableView. I did this in textfieldDidBeginEditing, like so:
- (void) textFieldDidBeginEditing:(id)textField {
TTTableViewCell *cell = (TTTableViewCell *)[self.tableView cellForRowAtIndexPath:objc_getAssociatedObject(textField, kIndexPathId)];
[self.tableView setContentOffset:CGPointMake(cell.frame.origin.x, cell.frame.origin.y - self.tableView.sectionHeaderHeight) animated:YES];
}
This works perfectly when the user goes down the list. Expect when to user taps a cell which is partial visible on the top it doesn't reveal the entire cell just moves a bit.
Does someone know a solution to this problem?
There are UITableView methods to solve exactly this. You can use scrollToRowAtIndexPath:atScrollPosition:animated: to ask the table view to bring a cell to the top by passing UITableViewScrollPositionTop.
(You might first want to check if it's already visible to ensure it doesn't scroll unnecessarily.)

UITableViewController TableView wont scroll

Im transferring my project from xibs to using a storyboard. I just added the Settings page, which i made from a UIViewController to a UITableViewController. The table view cells are added using the static method in the storyboard. The table has 5 sections, with 12 cells. I have no other code doing anything to the TableView, and in the storyboard scrolling is on. When running on my iPhone 5 the view wont scroll, all the cells are there, and i can pull and see the cells hidden, but when letting go it scrolls to the top still. Like when pulling up on a page in a UIWebView basically. Xcode gives no warnings, or errors, how can i fix this?
Edit: I found the 2 lines of code causing it not to scroll, But i still don't know how to fix it. The code gets the custom TextFieldTableCell and sets the text for it, because I'm using a storyboard, It doesn't call CellForRow,
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
TextFieldTableCell *cell = (TextFieldTableCell *)[SettingsTable cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
cell.textField.text = [[NSUserDefaults standardUserDefaults] objectForKey:#"Homepage"];
}
The TableView is only disabled from scrolling if the 2 lines under [super viewWillAppear:animated]; are called in the method - (void)viewWillAppear:(BOOL)animated, but if i call the lines in - (void)viewDidAppear:(BOOL)animated it will still scroll, except it takes a second to show that text, which isn't something i want, should be auto-loaded when user see's the TableViewController, are there any workarounds? Any other methods called like viewWillLoad but shouldn't disable the table?

Why does an empty tableView check the number of sections but a non-empty one does not?

I have set up a demo application with a simple UITableViewController with no contents, but an 'Add' button in the toolbar. This launches a modal view controller which is again empty other than a 'cancel' button. The cancel button just tells its delegate (the UITableViewController) to dismiss the modal.
I then added an NSLog statement in the UITableViewController's numberOfSectionsInTableView method.
Ordinarily, when the table view controller loads I see two calls to numberOfSectionsInTableView. When I open and dismiss the modal (which returns to the UITableViewController) I see no further calls to numberOfSectionsInTableView.
However, if I return 0 from numberOfSectionsInTableView, in addition to the two calls on display, I also see an additional numberOfSections call when the modal is dismissed.
This only happens when numberOfSectionsInTableView returns 0, and I have added no additional code to my project besides that mentioned. This is easily verifiable by setting up a couple of controllers as I've described and modifying the result from numberOfSectionsInTableView.
My questions:
Why is the UITableView calling numberOfSectionsInTableView on return from a modal view?
Why is it only doing this if numberOfSectionsInTableView returns 0?
In addition to numberOfSectionsInTableView, the UITableViewController is also calling cellForRowAtIndex: when the modal is dismissed. In fact, it is attempting to display the new contents of its dataSource. How am I meant to manually animate a row insertion if the first row added is going to already be updated automatically? Shouldn't it be left to me to make sure that my UITableView is consistent with its dataSource?
What property is the UITableViewController checking to know that there is one or more sections (and therefore ask my delegate how many sections)? It can't be numberOfSectionsInTableView itself, since I would see it called whenever I return from the modal, not only when numberOfSections = 0.
From UITableViewController docs:
When the table view is about to appear the first time it’s loaded, the
table-view controller reloads the table view’s data... The
UITableViewController class implements this in the superclass method
viewWillAppear:
If you watch in the debugger, the second call upon app launch is from UITableViewController's viewWillAppear: implementation - specifically the part referred to above, where tableView is sent the reloadData message.
Now, the first call to numberOfSectionsInTableView: on launch is also from UITableViewController's implementation of viewWillAppear: but not directly from that implementation's call to -[UITableView reloadData]. I'm not sure what the first call is all about.
But, to your question, the call to numberOfSectionsInTableView: that happens when dismissing the modal has exactly the same call stack as the second call from applicationDidFinishLaunching:withOptions:. My hypothesis then is that UITableView interprets having zero sections as being in a state where it has not loaded at all. That does make some sense actually. I'd consider an "empty" table view to be one without any rows, but one without any sections seems almost "uninitialized" to me. Furthermore the UITableViewDataSource documentation implies UITableView has by default one section. Returning zero from this method would be inconsistent with that assumption of the docs as well.
Now, to your concern about animation - if you give the table an empty section to work with, you will be able to have full control over inserting the first row with whatever animation you'd like, and not be locked in to when you need to reload.
I think the moral of the story is, don't return zero sections unless you really, really need to for some reason. The title of your post refers to this table view being "empty" as well but I think it's clear the framework finds zero sections to not be empty but unloaded.
Hope this helps! And thanks for posting the sample project for me to play around with.
Perhaps the delegate just couldn't believe its eyes. But seriously, since a table view has to have at least one section, passing 0 doesn't make any sense. Why do it? You pass it an invalid argument and it gives you back a weird response. As to why it doesn't ask for number of sections when you pass 1, I think it's because it doesn't need to know at that point (coming back from the modal view controller) -- the table view has already been populated (if there were some data) and you haven't changed anything in the model, so it doesn't need to update.
I added a few lines to your example project to slide in a row each time you return from the modal view controller, which is what I think you're trying to do. I added an int property num for the return value of numberOfRowsInSection, added an array to populate the table, and a call to insertRowsAtIndexPaths from the modal view controller dismissal method.
- (void)viewDidLoad
{
_num = 0;
self.theData = #[#"one",#"two",#"three"];
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:#selector(addRecipe)];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(#"# sections requested");
//when 0, this fires on return from the modal. When 1, it does not.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"in numberOfRows in section");
return _num;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"in cellForRowAtIndexPath");
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
cell.textLabel.text = [self.theData objectAtIndex:indexPath.row];
return cell;
}
- (void)addRecipe
{
//create the modal and suscribe for delegate notifications
AddRecipeViewController *addRecipeController = [[AddRecipeViewController alloc]initWithStyle:UITableViewStyleGrouped];
addRecipeController.delegate = self;
//display the modal in a navigation controller
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:addRecipeController];
[self.navigationController presentModalViewController:navController animated:YES];
}
- (void)addRecipeVC:(AddRecipeViewController *)addRecipeVC didAddRecipe:(NSString *)recipe
{
[self dismissModalViewControllerAnimated:YES];
_num += 1;
[self performSelector:#selector(addRow) withObject:nil afterDelay:.5];
}
-(void)addRow {
[self.tableView insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:_num-1 inSection:0]] withRowAnimation:UITableViewRowAnimationRight];
}
The tableview checks number of sections when it's populating the table view with data!
Since the table can be divided into sections, it has to know specifically how many sections to divide it into.
When you reload the data, the number of sections is also checked.
Because every time the table view has to take action in accessing either the data of the table, like what row you tapped, and in what section, or populating the data table, the number of sections has to be known!
Hope this helped!

How to make a UITextView not take the focus when initializing?

I'm using code to create a detailed view pushed when you press a row of an UITableView, but theres a problem.
The detailed view contain an UITextView and when a detailedView is called (only first time) this make the UITableView row pressed to lose its pressed state. It shouldn't ! It should lose the pressed state only when returning from the detailed view to the list view.
As soon as I remove the UITextView from my code, no problem !
I think it's something like UITextView taking focus?
Is there any way to avoid this ? By subclassing or such?
Hmmm not seeing this in the sandbox I just wrote.
Created a simple navigation-based project.
Added a view controller to the project with XIB; added a UITextField to the XIB.
Made following code changes to the root view controller:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
in cellForRowAtIndexPath:
cell.text = #"Push me";
in didSelectRowAtIndexPath:
SimpleViewController *detailViewController = [[SimpleViewController alloc] initWithNibName:#"SimpleView" bundle:nil];
in viewDidLoad:
self.navigationItem.title = #"Home";
Selecting the "Push me" row highlights the row and pushes the SimpleViewController onto the stack. Selecting the "Home" back button pops the view off the stack, returning to the table view and deselecting/un-highlighting the selected row. This is true whether or not the textfield in the SimpleViewController is the first responder at the time of the back navigation.

Using a UITable in a UIViewController

I've got a UIViewController with a xib view that has a button and a table.
Everything is wired up, the data has rows in it etc. But if i click a row and navigate away from this initial screen, then go back, the cell in the table still has the highlighted state on it.
Do i need to implement methods other than numberOfSectionsInTableView, tableView:cellForRowAtIndexPath and tableView:didSelectRowAtIndexPath?
Implement -viewWillAppear: on your view controller and deselect the table's currently-selected row, something like this:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:animated];
}