UITableViewController weird behavior after popping a view controller - ios

My UITableView has a bunch of reusable cells, and when I tap on one of them, it takes me to another view controller (via push segue) showing the details of that cell (let's say it's an item, so it would show details about an item - name, price, image, etc...). When I pop that view controller (by tapping on the back button), the UITableView has a strange behavior:
a) if it's scrolled all the way to the bottom, it will scroll automatically tad up (around 50 points), leaving the last cell barely visible, so I have to scroll back down again. My cell all have 60 points for height.
b) the scrollbar always shows and then disappears, indicating that something is moving that UITableView (although if not scrolled to the bottom, the content will not move automatically).
This happens in multiple UITableView's I have in my app. I am not forcing a reload of the table view in viewWillAppear, so I don't understand what is happening. My content is static after loading from the server (unless the user changes it, and then the reload is executed). But simply showing details of an item and popping that VC doesn't change anything in the table view.
Edit: Okay, I've figured what the problem is: I'm hiding a UIToolbar when pushing that segue. If I keep it always visible (which I don't want), it still shows the scrollbars animating when popping in my table view but doesn't scroll the table view if on the last few rows.

Add the following to viewDidLoad.
self.automaticallyAdjustsScrollViewInsets = NO;
This solved my problem of table view moving down after navigating back to view controller.

I managed to fix the first issue. It seems like the tableview is not taking into account the 44 points of the UIToolbar.
Save the tableview offset in prepareForSegue: (save it in a CGPoint property)
self.tableViewScrollOffset = self.tableView.contentOffset;
Then, in viewWillAppear:, check if it has been modified. If so, restore it.
if(self.tableView.contentOffset.y != self.tableViewScrollOffset.y) {
[self.tableView setContentOffset:self.tableViewScrollOffset];
self.tableViewScrollOffset = CGPointZero;
}

This behavior is indeed a bug in iOS 8.x.
All answers given so far can not really solve the issue. The issue is, that iOS forgets (or doesn't) consider the previously calculated cell sizes, when a table is being redrawn for instance when the view is being pushed.
One approach to solve this can be found here: UITableView layout messing up on push segue and return. (iOS 8, Xcode beta 5, Swift) (so this question is even a duplicate to this one).
However, the solution provided there is overkill and there are certain situations why this caching will fail (for instance a UIContentSizeCategoryDidChangeNotification is not regarded)
But there is a quite simpler solution even though it is odd:
If you are using a manual performSequeWithIdentifier in didSelectRowAtIndexPath, just add a [self.tableView reloadData] just before.
If you are using a IB seque from the cell, just add [self.tableView reloadData] in your prepareForSeque code.
The reason, why this solves the issue is, that this will force iOS to re-estimate the visible cells and so it no longer scrolls the content to another location. Fortunately, tableView reloadData doesn't cost too much overhead here as only the visible cells will be re-estimated.

Just a hunch, have you got a rogue scrollToRowAtIndexPath:atScrollPosition:animated hanging around?

I was also facing this issue. I managed to find it out. The reason in my case is tableview header height was calculating based text and text height was negative due to which tableview was shifting down even though the contentinset and scrollinset are zero.
This was only occurring for first time. Next time it is calculating correct. One weired thing i found is that when Class A (having tableview) have pushed another Class B from init. When keyboard from Class B is opened viewDidLoad of Class A is called. and before Class B is unloaded from navigation controller. Tableview is reloaded for Class A.

Setting the automaticallyAdjustsScrollViewInsets as suggested above did not work neither did caching and setting the tableViewScrollOffset work.
Hence came up with an workaround which worked like a charm for me.
The workaround was to add an Dummy UIView which has height of 1px and width of 320px and place it between the "Top Layout Guide" and the UITableView. This view's background could be set to clear so that it is invisible.
Now using Autolayouts, fix the Dummy View's top to the Top. Now set the tableview's top constraint with respect to Dummy View. Found that this resolved the issue of the tableview's misplacement.
Screenshot of the Dummy View along with the autolayout constraints have been provided for easy reference. The Dummy View has been set to a larger height and red background colour for illustration purpose only.

Related

UICollectionView moves super view when calling cellForRowAtIndexPath

I have a view that is placed outside the screens bounds (beneath). When a button is pushed this view is animated up, so that it becomes visible. It sort of acts like a keyboard, although it isn´t.
Inside this view I have a container view which is hooked up to a UICollectionViewController.
The problems occurs when I scroll its collectionView. As soon as cellForRowAtIndexPath is called, the entire super view is moved outside the screen again (without animation). If I push the button again it animates back up.
Does anyone know what is causing this behavior? I suspect that the frames of the reused cells are sort of pushing the super view back to where it was initialized. Any suggestions how I should resolve this?
When using autolayout, you should animate using constraints. I had this same issue before. Maybe viewDidLayoutSubviews is called multiple times while you are using the app, and hides your view again.

UITableview with Autolayout and NSFetchedResults controller cell layout bug on insert

I seem to be having a very strange problem with my UITableview cells and autolayout. So my tableview is set up as follows:
I have a UITableView within a UIViewController that I created via Interface builder using Autolayout
My tableview is using UITableViewAutomaticDimension so that the cells can resize based on the text within them.
Each cell has 2 subviews within it. A front view and a back view. The back view contains the buttons for my slide-able cells
I am using NSFetchedResults controller to update the tableview
The problem occurs when I tap on a cell and am taken to the next view. Then I create a core data item within that view and save so that it is inserted into my tableview (now behind the current view controller).
What happens is that the newly inserted cell seems to lose all autolayout constraints and all view elements are on top of each other in the top left of the cell. It also looks like they are not even contained within the front view for the cell (or the front view has a width and height of 0).
Example:
It gets even stranger. If I refresh the tableview by pulling down and the cells are reloaded it corrects itself. However, if I refresh the tableview again then the problem appears again but on a different cell. This bug does not happen consistantly which is rather frustrating.
Any help would be appreciated as I only have a few remaining hairs on my head to pull out at this stage.
Thanks in advance!
So I found out what the issue was. The issue was that I was using size classes in Interface builder but I was only using the iPhone Portrait size class and did all my work in there instead of the "any" size class. Disabling size classes and setting the project to iPhone only fixed all these problems.
Weird I know

Adding a Scrolling Subview to a UITableView (NOT a Cell)

I'm creating my views programmatically. I have a UITableView in my UIViewController subclass that I want to add a scrolling subview to that is not a cell. I want to add some text-based subview to the UITableView that scrolls with the table and starts out above y=0 so the user will only see it if he pushes the table down. That is, it should reside above the first section of my table. If it helps for visualization, I intend to make something similar to those "scroll down to refresh" features and want some indication to the user that scrolling down causes a refresh. Is there any way to do this without something messy like using another UITableViewCell to represent it or abusing the UITableView delegate methods to move a view around whenever the user scrolls?
Simply using [tableView addSubview:] in my viewWillLoad only makes it appear for a split-second then disappear once the table data is loaded. This seems weird to me because UITableView is a subclass of UIScrollView, which is meant to hold other views in it. Using [tableView.backgroundView addSubview] does nothing.
P.S. Why not use a UIRefreshControl for this? I'm still undecided but leaning towards not using one because I don't like how slow that spinning wheel "feels" when the refreshes are usually very very quick. I've been looking at other options like flashing the background subtly and only showing a wheel if it's taking a longer time than usual.
I You can implement pull to refresh with only a table view
To do this using the scroll view delegate, since tableview is a subclass of scroll view.
Set view controller to be the tableview delegate and implement
(void)scrollViewDidScroll:(UIScrollView *)scrollView
When scrollview content offset y value passed a point, add a label to the viewcontroller.view not tableview. When you scroll back or release, remove the view.
You might also be able to add label to the table view and set the frame origin to negative y value, so when you pull the label will move into view (Never tested this do might not work)

UITableView Section Headers not visible when table in visible rect

I have a UIScrollView with 3 UITableViews stacked horizontally. I switch between these tableviews using a tab-controller on top. However, when I switch to the 2nd or 3rd tab and switch back quickly to 1st the section headers don't show. They display when I scroll the tableView. These are custom headers (jfyi). I tried calling setNeedsDisplay when the tableView is visible, but that does not help because as per Apple Docs :
If you simply change the geometry of the view, the view is typically not redrawn. Instead, its existing content is adjusted based on the value in the view’s contentMode property. Redisplaying the existing content improves performance by avoiding the need to redraw content that has not changed.
Since, only the geometry of the view is changing here, it does not help. Also this happens on all versions iOS 5~6.1 and on simulator and device. Thankfully, this does not crash the app, but its a problem nevertheless. Could someone help? I am attaching pictures for reference. First shows the problem, second: after scrolling the "head(er)less" tableview
EDIT:
I am using simple scrollRectToVisible:animated: to switch between tableviews. This does the trick but I just observed that when I set ...animated:NO all is okay. The problem happens when ...animated:YES
It seems the issue of displaying and scrolling taking place simultaneously for the respective tableview. So what you can do here is:
Remove the scroll animation
or
Just scroll the tableview to top on the tab press event
or
simply reload the tableView which is made visible

UITableView will not resize after UITextField becomes first responder

I have two Scenes in my Storyboard that are nearly identical. Both are UITableViewControllers. Both have header and footer views. The header views have a UISearchBar and the footer views have a UIView that contains a UITextField. Each have only one prototype cell. One is prototyped as a "Basic" cell and the other is prototyped as "Right Detail" cell.
Here's the problem. When I click the UITextField in the footer view on the first scene, the table resizes automatically so that the bottom of the table is at the top of the keyboard. This allows me to scroll the table up so the footer view shows and the user is able to see what they're typing. The other scene will not automatically resize the UITableView so the UITableView cannot scroll the footer view to where it can be seen and the UITextField is hidden under the keyboard. I can't even manually scroll the table far enough since the footer is always at the bottom of the UITableView.
A little added info. The scene that works has many rows of data while the one that doesn't work only has a couple. I tried adding a number of rows until the table had enough to enable scrolling and it doesn't fix the problem.
I have checked everything I can think of and I can't see anything that would allow one of the views to let the UITableView to automatically resize to work with the keyboard and the other not. I must have overlooked something but I can't seem to find it.
Any help will be greatly appreciated!
Rob
I thought this problem was caused by copy-and-pasting from one view to another, but I had the same problem once I'd (in theory) fixed it.
The answer for me turned out to be simple: I hadn't called [super viewWillAppear:animated] in my UITableViewController subclass' viewWillAppear: implementation. Make sure you've got a call to the superclass' method and hopefully the problem will go away.
I am guessing the frame of the tableview is not being resized to the smaller size in the second case.
Print out the frame and content sizes in both the cases once its loaded, that should help you see if there is an issue.

Resources