Strange behavior on scroll UITableView - objective-c

I have a single view with nested scroll and table view with this hierarchical tree:
(The table is loaded with some Huckleberry Finn rows taken from this example.)
On viewWillAppear I programmatically scroll the Table View at the last row doing
-(void)viewWillAppear:(BOOL)animated
{
[self.tableView reloadData];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.tableView numberOfRowsInSection:0]-1 inSection:0];
[self.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionBottom
animated:YES];
}
and everything works as expected, in both orientation. As you can see, the text is rightly truncated.
I've uploaded on Github my project so you can see it running. Download it here.
Now, if you add this line in the cellForRowAtIndexPath method:
cell.textLabel.numberOfLines=0;
the cells automatically will resize to embed and show the entire text label:
BUT, and this is the issue my dear friends, as you can see the table is not scrolled to the last row but 5-6 rows above. And I cannot understand why.
The same thing happens also if NumberOfLines is 2,3 and so on. Only a value of 1 can make the TableView move down to the very last row.
(FYI, I do not use [self.tableView setContentOffset:CGPointMake(0, CGFLOAT_MAX)]; in order to scroll down the table because it throws an exception)
Maybe something is wrong with iOS8 ?

viewDidLayoutSubviews will do the job
I guess you use some autolayout or self sizing cells? In viewWillAppear the calculation is not finished. So you scroll to the bottom, but then thereafter autolayout resizes your cells what makes them expand in height.
For Details: Refer to apple documentation UIViewController
... "Your view controller can override this method to make changes after the view lays out its subviews. The default implementation of this method does nothing."
- (void)viewDidLayoutSubviews {
// your code here
}

I added this event with reloadSection method and now the table scroll to the end. Also, I changed "tableView" to "myTableView" since I read that is better to give a non-default name to the views (as Sapi says in his answer here).
- (void)viewDidLayoutSubviews {
[self.myTableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.myTableView numberOfRowsInSection:0]-1 inSection:0];
[self.myTableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionBottom
animated:YES];
}
IMHO this thing is very Machiavellian, however.

Related

EXC_BAD_ACCESS error at viewwillappear using scrollToRowAtIndexPath

I'm getting an EXC_BAD_ACCESS error when using scrollToRowAtIndexPath in the viewWillAppear method. I searched for solutions and saw some old posts recommending to set delegate and table to nil (see code below), however when I set that I simply dont get anything loaded in my tableview.
I should say that this is part of a chat application where I want to show the last message entered first. Many thanks for any assistance with this.
Here's my viewWillAppear:
-(void)viewWillAppear:(BOOL)animated {
[self.table reloadData];
int lastRowNumber = [self.table numberOfRowsInSection:0] - 1;
NSIndexPath* ip = [NSIndexPath indexPathForRow:lastRowNumber inSection:0];
//self.table.delegate = nil;
//self.table = nil;
[self.table scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
I should add that this code seems to work fine in other parts of my program, the only time I get the error is in the viewWillAppear method.
viewWillAppear: is too early to do any animation on view. Per Apple Documentation:
This method is called before the view controller's view is about to
be added to a view hierarchy and before any animations are configured
for showing the view. You can override this method to perform custom
tasks associated with displaying the view. For example, you might use
this method to change the orientation or style of the status bar to
coordinate with the orientation or style of the view being presented.
If you override this method, you must call super at some point in your
implementation.
So, you cannot add animations when even the view hierarchy is not set completely.
It may help you
NSIndexPath * lastIndex =[NSIndexPath indexPathForRow:yourContenetArray.count-1 inSection:0];
[self.yourTableView scrollToRowAtIndexPath:lastIndex atScrollPosition:UITableViewScrollPositionNone animated:YES];
Write your code in viewDidAppear method.As your table view is loaded
then after you should call the method scrollToRowAtIndexPath . Then
your animation will be performed.And if you want to use in
viewWillAppear , then You can try to reload your tableView and then
write code for scrollToRowAtIndexPath
You should probably use the viewDidLoad method for scrolling, and if your array count is 0 (empty array) it will throw an error. It is not possible to scroll to cell at index -1.

Cocoa Touch: How can I update the table view everytime I tap on a tab of tabbar?

I'm using iOS 8 and Objective c.
If I do a change in the values in one view, I can't see the reflection on my other tabviews. But if I re-run the app, I can see the reflection on the other tabs. Sometimes it works, sometimes it doesn't. How can I make sure that when I tap on a tabview, it will be reload according to the database?
I'm not sure what you are trying in your code, but you can try to implement the viewDidAppear method with a reloadData in your tableView:
- (void)viewDidAppear:(BOOL)animated{
[self.tableView reloadData];
}
[tableView reloadData] is the one you are looking for , this UITableView method refreshes the table view by calling its delegate and data source methods again to get fresh data.
I solved it!
Here is my code:
"TheMaster" is the main tabview. Here selectedIndex can be 0,1,2.... according to the placement of the tab. The leftmost tab has the index 0, and after that 1,2, and so on.
UITabBarController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"TheMaster"];
controller.selectedIndex=0;
[self presentViewController:controller animated:YES completion:nil ];

How can I figure out the indexPath of a cell I'm not pushing using didSelectRowAtIndexPath?

I've got a UITableView with custom cells inside, which trigger a detail view when a UIButton in them is tapped. Tapping the cell itself does not trigger the segue, meaning didSelectRowAtIndexPath is never called.
Before implementing this, I was using didSelectRowAtIndexPath to set up the object being passed to the detail view via its indexPath in my NSFetchedResultsController's fetchedObjects array. Since I'm now not using didSelectRowAtIndexPath, I can't access the indexPath there.
I've already tried using the tag of the button, but since it can't store an indexPath, this doesn't work when I call objectAtIndexPath before the segue. I've also tried accessing the button's superview to get its indexPath, but that didn't work either, was always nil.
How else can I know which UIButton was pushed, so that I can pass the correct object to my detail controller?
Edit: I thought of the idea of adding a UIButton category that adds an indexPath property, but I'm having trouble doing this since it expects a method to go along with it. Would this approach work?
Follow the superview of the button (or the superview of superview of the button) to get the Cell and get the indexpath of the cell from tableview
I fixed by adding the following to the method called when I tap the button:
UIView *contentView = (UIView *)[sender superview];
UITableViewCell *cell = (UITableViewCell *)[contentView superview];
NSIndexPath *indexPath = [self.tableView indexPathForCell: cell];
_quote = [self.fetchedResultsController objectAtIndexPath: indexPath];
[self performSegueWithIdentifier: #"ViewDetail" sender: nil];

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?

indexPath null in didSelectRowAtIndexPath in an ipad splitview navigation app

I'm porting a fairly simple iPhone Navigation based app to a Split View iPad app.
I have two nested levels of navigation on the Master view. The user picks a value from the first table and it loads the 2nd table. Selecting a value on the second table loads the Detail item for the detail view. Or it's supposed to. The didSelectRowAtIndexPath on my 2nd controller is firing but indexPath is null.
I'm following the SplitView template fairly closely. I'm only really getting off the beaten track by adding that 2nd TableViewController. My RootViewController loads the 2nd TableViewController on didSelectRowAtIndexPath, and that part's working. In my 2nd TableViewController.m I'm trying to set the detail item for the DetailView. But it's that didSelectRowAtIndexPath method that's not giving me the row. I'm fairly new to this, but it seems odd to me that the method for clicking a row would fire but not have the index for that row.
Here's the code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
TrackerSplitViewAppDelegate *appDelegate = (TrackerSplitViewAppDelegate *)[[UIApplication sharedApplication] delegate];
detailViewController.thisRequest = [appDelegate.requests objectAtIndex:indexPath.row];
NSLog(#"Request Loaded: %#", detailViewController.thisRequest.Title);
NSLog(#"Index Row: %#", indexPath.row);
[appDelegate release];
}
The array of requests is loading properly (appDelegate.requests) but my objectAtIndex is failing because indexPath is null. Or at least indexPath.row is null.
EDIT: Ok, the comments below are correct, I'm not using NSLog properly. The indexPath.row is fine (I thought it was also showing up as null using the mouseover in the debugger, but I just don't know how to use the debugger properly).
The detailViewController property isn't getting set right for some reason. If I substitute with this:
//detailViewController.thisRequest = [appDelegate.requests objectAtIndex:indexPath.row];
Request *aRequest = [appDelegate.requests objectAtIndex:indexPath.row];
The aRequest object loads just fine. So the next question is why setting my property on my detailViewController object isn't working. I'm still quite fuzzy on how things persist in this environment. Any additional input would be great.
The value of indexPath.row is not a string. You can't log it with %#. Try using %i or %li, which are used for integers. Also, you haven't reaLly properly checked for the request to be loaded, afaik. Is the title property only available after a successful load?
NSLog(#"Index Row: %#", indexPath.row);
indexPath is an NSIndexPath object, you could use %# to print it.
But indexPath.row and indexPath.section are NSUInteger. So you have to use %u to print them.
So maybe your indexpath.row is not nil (as in no object) but 0 (as in zero, the number before one)
Select a row that is not the first one and you should get a crash.