UISearchBar on UITableView strange offset issue - ios

I have a UITableView which has a UISearchBar subview. This is all on the view of a UIViewController along with a few other subviews (labels, text fields and such).
The search bar and content offset of the table are acting quite strangely, but it seems dependent on the order in which these views are added to the main view in the xib. I created a sample project with just my table/search and a label in order to test, and the result is the same. When the table is added after the label, everything works fine:
Setup:
Correct and Expected Result:
However, if I simply change the order in which my 2 subviews sit on the main view (aka table added before the label) then weird things start happening.
Apparently bad setup:
Weird offset of Search Bar:
I'm not changing anything else whatsoever, so why does Xcode seem to care which order these subviews are added to the main view?? If I scroll up on the "bad" table setup, the search bar disappears immediately at its top edge, but the table's content will continue to scroll up until it reaches the top of the frame that was set in the xib. Scroll back down and the search bar doesn't reappear until the strange lowered location. This is in Xcode 5.1.1, not the new beta. The result is the same with or without Autolayout turned on.
Any idea why this is happening? Is this a bug, or am I missing something? (I didn't post any code because all I'm doing is setting the number of sections, rows, and setting the text on the cell. Not messing with content insets, offset, anything. I load the view from the app delegate as the root of a nav controller)

This happens because a UIViewController's property called automaticallyAdjustsScrollViewInsets
With iOS 7, UIViewControllers have a property called
automaticallyAdjustsScrollViewInsets, and it defaults to YES. If you
have a scroll view that is either the root view of your view
controller (such as with a UITableViewController) or the subview at
index 0, then that property will adjust both the contentInset and the
scrollIndicatorInsets. This will allow your scroll view to start its
content and scroll indicators below the navigation bar (if your view
controller is in a navigation controller).
From Big Nerd Ranch
If you are using storyboards, you can change it by selecting the view controller and in the attributes inspector deselect Adjust scroll view insets.
Here is its description from apple documentation:
Default value is YES, which allows the view controller to adjust its
scroll view insets in response to the screen areas consumed by the
status bar, navigation bar, and toolbar or tab bar. Set to NO if you
want to manage scroll view inset adjustments yourself, such as when
there is more than one scroll view in the view hierarchy.

I have same problem before about position of tableview and searchbar. i tried the following and it works for me.
If you do not write code for that and if it is only problem of xib or storyboard then try all outlet's autosizing and origin setting to fix its position and see the difference. it may be work for you.

Update : automaticallyAdjustsScrollViewInsets has been deprecated in ios 11 and a new field contentInsetAdjustmentBehavior has been introduced.
if #available(iOS 11.0, *) {
tableview.contentInsetAdjustmentBehavior = .never
} else {
automaticallyAdjustsScrollViewInsets = false
}

UITableView header can contains only one UIView, so if you need UISearchBar plus UILabel, you need to wrap they into UIView and add this view as UITableView header.

Related

Embedding a Collection View inside a Container View - extra white-space at top

I have an app that uses a Nav Contoller as it's initial VC, which then has a root UIViewContoller that contains a UIView at the top half, and a UIContainerView at the bottom. In the UIContanerView, I'm embedding a working UICollectionView that contains image buttons that segue to detail views.
The problem is that white space now shows up at the top of the UICollectionView. Given this is around 64 pixels high, it appears to be a ghosting of a Nav Bar 44px + Status Bar 20px = 64.
And if I scroll up everything looks fine and works as expected, and it also allows me to show you what I expected the layout to look like upon launch:
A snippet of my storyboard is below if that helps:
yes, that could be because child view controller embedded in container view gets the impression, that it is a direct child of UINavigationController, which in turn make collectionView leave top 64 pt insets.
TO solve this problem,In your child view controller interface builder, unmark adjust scrollView insets
This should solve your problem
UPDATE
As Dan suggested, we can also fix it programatically, by calling
automaticallyAdjustsScrollViewInsets = false
in viewDidLoad() of your UIViewController

Table view top space to navigation bar increased after pressing back

When I click one of the cells in the table view, it opens a new view with the following code:
let fullResView : FullResponseViewController = self.storyboard?.instantiateViewControllerWithIdentifier("FullResponseView") as! FullResponseViewController
fullResView.receivedPost = post
self.navigationController?.pushViewController(fullResView, animated: false)
When I press back, it increases the distance between the table view and the Top Layout Guide. Representation:
Hierarchy:
I have a tab bar controller, that is embedded in a navigation controller.
The table view is drag & dropped after creating an normal view. So the table view is inside a View.
Table view does contain an header view. When setting a background color for this, it moves together with it, so it should not be anything with those constraints.
constraints for the table view are:
equal with to superview
Align Center X to superview
top space to Top Layout Guide
bottom space to Bottom Layout Guide
I've tried the following:
set this in viewWillAppear:
self.responsesTableView.contentOffset = CGPointMake(0, 0)
self.automaticallyAdjustsScrollViewInsets = false
This did work when I pressed back, then switch to another view in the tab bar, and then switch back again. Without contentOffset it will stay like this forever.
As i seen the OP images that seems like Navigation translucent property Issue. Because after push a viewcontroller there is same 44px white space. so it means if your Navigation translucent property is true then your UITableview start from 0th Y position. And if your Navigation translucent property is false then UITableview start from 44px Y position.
So i guess in between push and back some where UINavigation's translucent become a true and false. make following one line add in your appdelegate class in DidFinish method:
UINavigationBar.appearance().translucent = false
This is appearance of UINavigationBar for make this false globley in your project. Hope that will be fix your issue.
I'm not sure if this is related to your particular issue, but it's worth checking whether your UITableView is the first control on your screen:
Why is there extra padding at the top of my UITableView
Even in the latest version of Xcode, this is a bug. But it's easy enough to fix, if you know how to get around it.

UITableView Content inset unexplainable behaviour

I have a View Controller with UITableView declared in storyboard. The View Controllers has "Under top bars" option selected (Edge Rect). Normally the view looks like the below image.
There is no gap between the cells of the tableview and navigation bar.
The filter in the right bar button item presents filter view controller modally and
when returning back (dismiss), there is content inset (additional) added (by I don't know who). The final looks like this.
I have tried to set content inset in viewWillAppear, viewDidLayoutSUbviews etc. But nothing seems to work. Has anyone ever come across similar kind of problems? How can I remove this contentInset.

Can you set the scroll view insets for container controllers?

I have a container controller that displays a bar of data right below the lop layout guide.
I want the child controller of the this container controller to be able to scroll behind this bar as well as any navigation bar and status bar. However it seems that when UIKit automatically adjusts the scroll view insets of a controller it only takes into account the length of the top layout guide.
Is there any way to be say that the scroll view insets should be the top layout guide + the height of my bar? I know people are going to suggest I set automaticallyAdjustsScrollViewInsets = NO but I have already tried this. The problem is that I cannot seem to replicate exactly when UIKit sets the content insets and so I get all sorts of weird edge cases that break the insets. The most prominent example being UITableViewController where, because I set the content insets manually, the refresh control is sometimes not in the correct position and when I end refreshing the insets of the scroll view get set to { 0, 0 } so content is hidden behind my bar and the navigation bar.
UPDATE: All the magic seems to be inside the methods _setNavigationControllerContentInsetAdjustment on UIViewController and _computeAndApplyScrollContentInsetDeltaForViewController on UINavigationController.
It appears that only the navigation controller attempts to adjust the scroll view insets and it calculates the insets based on the top and bottom layout guides. It passes these insets onto the view controller which then adds/subtracts the insets from the scroll view and takes into consideration what navigation content insets have been set previously. This ensures that if anyone else modified the content insets between now and when the navigation controller last modified the insets they will not override the intermediary changes.
My solution was to set automaticallyAdjustsScrollViewInsets = NO and handle the setting of the content insets myself.
I created a category on UIViewController that tracked any existing insets that were set so whenever I adjusted the insets to have the scroll view appear below my custom UIView I would not be losing any information, such as the content inset for the UITableViewController with a active refresh control inside it.
After that the last challenge was figuring out where to set the custom insets and after much experimenting I found that - (void)viewDidLayoutSubviews was the best place to do it.
FDScrollingTabBarController is the container controller that I implemented this solution in.

In iOS 7, why UITableView's contentInset has bottom value despite the UITabBarController is hidden?

I don't know why UITableView has bottom inset automatically despite I make UITabBarController be hidden by calling [setHidden:YES] before.
The view controller who has UITableView is a child of UITabBarController. I already know that automaticallyAdjustsScrollViewInsets helps any UIScrollView get proper 'contentInset' depending on status of it's container view controller.
So, I expected that UITableView's bottom contentInset will be 0 if UITabBar is hidden. But, doesn't do that.
Although automaticallyAdjustsScrollViewInsets is YES, should I manually adjust that value when UITabBar is hidden?
Tab bars have never been meant to be hidden - after all why have a UITabBarController if you want to hide the tab bar. In the documentation, you are warned not to modify the tab bar object directly:
You should never attempt to manipulate the UITabBar object itself
stored in this property.
This is exactly what you are doing when setting it to hidden.
In iOS6 this has worked, but now in iOS7, it doesn't. And it seems very error prone to hide it. When you finally manage to hide it, if the app goes to the background and returns, Apple's layout logic overrides your changes. There are many such triggers.
My suggestion is to change your design. Perhaps display the data modally.
Putting this here for anyone who gets this problem for nested view controllers.
My view controller containment hierarchy is:
UINavigationController
|--UIViewController
|--UITabBarController
|--UIViewController
The last view controller has a UITableView whose scrollIndicatorInsets keep getting offset by the tab bar controller's UITabBar height even if it is hidden.
Solution: Set automaticallyAdjustsScrollViewInsets to false in the view controller that contains the tab bar controller (and is inside the UINavigationController). No need to set additional properties in the tab bar controller itself and the second view controller where the UITableView is.

Resources