Tab Bar covers TableView cells in iOS7 - objective-c

I have a custom tableViewController that I'm adding to a TabBarController with
self.tabBarController.viewControllers = [NSArray arrayWithObjects:someOtherViewController, customTableViewController, nil];
self.tabBarController.selectedIndex = 1;
The issue I'm having is that the last 1.5 tableViewCells are being covered by the tab bar at the bottom of the screen on an iPhone 4 running iOS7. When I use the iOS Simulator - iPhone Retina (4-inch) / iOS 7.0 the issue still exists.
What is the correct way to make the tableView line up with the top of the tabBar at the bottom of the screen without using 'magic numbers'?

Try this for your CustomViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
UIEdgeInsets adjustForTabbarInsets = UIEdgeInsetsMake(0, 0, CGRectGetHeight(self.tabBarController.tabBar.frame), 0);
self.scrollView.contentInset = adjustForTabbarInsets;
self.scrollView.scrollIndicatorInsets = adjustForTabbarInsets;
}

It's an iOS 8 solution but it may work on iOS 7 to: Go to storyboard > select table view controller > uncheck "Under Bottom Bars". That's it!

Setting the contentInset of your table view with a .bottom value of 49 points should correct this.
Under the right configurations, setting YES for the new UIViewController property on iOS 7 called automaticallyAdjustsScrollViewInsets should correct this, but (again) it depends upon a lot of other factors (view hierarchy, parent view controller's settings, et cetera).

The accepted answer doesn't quite work for me--my set up is a little different. I'm programatically creating my view controllers. My app's root is a tab bar controller, one tab is a navigation controller, whose root is a UIViewController with a table view as the main view.
What works for me though is when I manually computed the table view's height and set it in the frame when alloc-initing the table view. The general formula is:
screen height - (status bar height + nav bar height + tab bar height)

CGFloat bottom = self.tabBarController.tabBar.frame.size.height;
NSLog(#"%f",bottom);
[self.tableview setScrollIndicatorInsets:UIEdgeInsetsMake(0, 0, bottom, 0)];
self.tableview.contentInset = UIEdgeInsetsMake(0, 0, bottom, 0);

Embed your table controller in a navigation controller.
1. select the view in story board.
2. On menu bar select Editor -> embed in -> navigation controller.
Hope that helps

I have a similar view hierarchy to Matt Quiros: UITabBarController -> UINavigationController -> UIViewController -> UITableViewController (embedded as a subview of the UIViewController). The other answers didn't work in my case, and I had to set the table view's frame manually in the table view controller's viewWillAppear: method.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Adjust height of tableview (does not resize correctly in iOS 7)
CGRect tableViewFrame = self.tableView.frame;
tableViewFrame.size.height = [self heightForTableView];
self.tableView.frame = tableViewFrame;
}
- (CGFloat)heightForTableView
{
return CGRectGetHeight([[UIScreen mainScreen] bounds]) -
(CGRectGetHeight([[UIApplication sharedApplication] statusBarFrame]) +
CGRectGetHeight(self.navigationController.navigationBar.frame) +
CGRectGetHeight(self.tabBarController.tabBar.frame));
}
If anyone finds a better solution, please share!

I think this would work better for you:
After [super viewDidLoad];
try the following code:
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
self.edgesForExtendedLayout = UIRectEdgeNone;

You can also implement viewDidLayoutSubviews and use bottomLayoutGuide to get the height of the tab bar:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
CGFloat bottomOffset = self.bottomLayoutGuide.length;
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, bottomOffset, 0);
}

Even though changing the contentInset of your table View is a working solution, I find it better to make sure your table view stops before the Tabbar.
As Paul Newman said, using the bottomLayoutGuide is a good thing, specially if you are using autolayout.
In My case adding a constraint to the bottom of the tableview linking to the top of the BottomLayoutGuide was a clean solution, this is an example with Storyboard, but it can be done in code as well.
Hope it helps.

Related

UIStatusBar appear in black in UITabBarViewController ios11

The issue is simple :
The Application is based on UITabBarViewController
3 Tabs in the controllers
TabBar Views are configured in viewDidLoad for the
UITabBarViewController
On launch, the UIStatusBar appear in Black background color
Changing to any tab the UIStatusBar get colored!
What I'm missing?
I had a similar issue some months ago - I solved it by adding the following lines into my AppDelegate. Please find below my swift code, I'm sure you know the objective-c syntax:
// Setup general NavBar appearance
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
UINavigationBar.appearance().shadowImage = UIImage()
UINavigationBar.appearance().backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0)
UINavigationBar.appearance().isTranslucent = true
UINavigationBar.appearance().tintColor = UIColor.white
UIApplication.shared.statusBarStyle = .lightContent // => this would be the solution
I think the last line of code would be interesting for you. It's important to set this changes in your AppDelegate.
I had a similar issue. While not directly related the OP's issue with the tab bar, I too saw that my translucent status bar had appeared to turn opaque and was pushing the view down.
After migrating my project from Swift 3 with Xcode 8 to Swift 4 with Xcode 9, I started experiencing this same issue where the status bar appeared to be pushing content down when it was visible. At first I thought it was an issue with the preferred UIStatusBarStyle I had set (I was using UIStatusBarStyleLightContent), but that was working correctly.
The issue was that in the move from Xcode 8 to 9, a subview in one of my view controllers was misplaced in the Storyboard. A constraint that previously pinned that view to the top of the view controller was pointing incorrectly pointing to the View (with margins) instead of the Top Layout Guide. Switching this constraint to use the Top Layout Guide with a value of 0 solved my problem.
In didFinishLaunchingWithOptions method set status bar style to light content
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[application setStatusBarStyle:UIStatusBarStyleLightContent];
and in every viewController return this :
-(UIStatusBarStyle)preferredStatusBarStyle{
return UIStatusBarStyleLightContent;
}
or if you are using the UINavigationController then also add this line of code in every viewController (or make a baseViewController) viewWillAppear method
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
self.navigationController.navigationBar.barStyle = UIStatusBarStyleLightContent;
For me following approach is working
final class StatusbarStyle {
static let shared = StatusbarStyle()
var style:UIStatusBarStyle = .default
}
and then whenever I need to change status bar color I write following:
StatusbarStyle.shared.style = .default
navigationController?.setNeedsStatusBarAppearanceUpdate()

UISearchController Search Bar Position Drops 64 points

The search bar is appearing exactly 64 points too low:
All of the other frames are exactly correct.
Edit:
- It's the UISearchController's view that is getting the wrong origin.y. It gets set to 64, when it should be 0.
If I add this method:
- (void)didPresentSearchController:(UISearchController *)searchController
{
[super didPresentSearchController:searchController];
searchController.view.frame = CGRectMake(0, 0, searchController.view.frame.size.width, searchController.view.frame.size.height);
}
Then the views align. However, its janky because it jumps. If I modify the frame in willPresentSearchController it does not work, as the controller must be doing some sort of layout after its presented.
If I use SparkInspector, and edit the frame of the UISearchBarContainerView from origin 64 (what it gets set at, to 0), the problem is resolved.
Here is my relevant configuration:
self.searchResultsController = [[GMSearchTableViewController alloc] init];
self.definesPresentationContext = YES;
self.searchController = [[UISearchController alloc] initWithSearchResultsController:self.searchResultsController];
self.searchController.dimsBackgroundDuringPresentation = YES;
self.searchController.hidesNavigationBarDuringPresentation = NO;
self.searchController.searchBar.frame = CGRectMake(0, 0, self.view.frame.size.width, 44);
[self.view addSubview:self.searchController.searchBar];
I'm not using Interface Builder, everything is configured in code.
I'm positive that setting definesPresentationContext is correct.
The VC sits in a regular UINavigationController, which is inside a SplitViewController (but problem exists on iPhone as well).
I feel like I'm missing a simple configuration option in regards to the UINavigationBar
I also have a different controller that uses a custom Container View Controller model, which is more complex, and that one works.
When I set
self.definesPresentationContext = NO;
This happens:
So now the UISearchBar gets positioned correctly, but the presentation context is wrong, causing the UISearchController's table view to occupy the full view.
Well in classic fashion, I've found a solution (https://stackoverflow.com/a/30010473/579217)
This does the trick:
- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar
{
if (bar == self.searchController.searchBar) {
return UIBarPositionTopAttached;
}
else { // Handle other cases
return UIBarPositionAny;
}
}
If your tableView is custom UItableView and not a UItableViewController you should hide top of the tableview under navigation bar, because search bar automatically attached on header of table view and if your table view starts under navigation bar i cause this problem. Just make the table view full screen from top of navigation bar.

How to get inputAccessoryView to anchor above the UITabBar?

I'm trying to figure out the best way to have a custom inputAccessoryView rest on top of a tab bar. Currently, I have an inputAccessoryView that rests at the very bottom of the screen, but it covers the tab bar. Any one know the best practice for shifting that inputAccessoryView up?
Currently I have a view defined in a storyboard with a tab bar. Its corresponding view controller takes the view and calls becomeFirstResponder. I've overwritten both:
- (UIView *)inputAccessoryView and -(BOOL)canBecomeFirstResponder
within the view's .m
Found a workaround by shifting toolbar frame by bottomSpacing = tabbar height:
- (void) layoutSubviews {
[super layoutSubviews];
CGRect origFrame = self.frame;
origFrame.origin.y = _keyboardIsVisible ? 0 : -self.bottomSpacing;
self.frame = origFrame;
}
Strangely it works well in JSQMessagesInputToolbar, but it's lost after animations if I do this in UIView that wraps toolbar, or maybe I'm missing something..

iOS 8 - Modal in Popover

I have a popover with TabBarController in it. In one tab there is a TableViewController with a list of names. And there is a plus button, that has a modal segue to AddCharacterVC for adding new names.
In iOS 7 I do it like this:
AddCharacterViewController *acvc = (AddCharacterViewController *)segue.destinationViewController;
acvc.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
acvc.modalPresentationStyle = UIModalPresentationCurrentContext;// so it does not take full screen in popover
And in AddCharacterVC I set content size like this:
- (void)viewWillAppear:(BOOL)animated {
CGSize size = CGSizeMake(320, 480); // size of view in popover
if (IDIOM == IPAD && [self respondsToSelector:#selector(preferredContentSize)]){
self.preferredContentSize = size;
}
And it works perfectly.
However, in iOS 8 modal view does not cover the whole popover, leaving TabBar visible. The user can tap on it or not, anyway modal view won't unwind properly.
I've tried:
setting acvc.modalPresentationStyle to UIModalPresentationOverCurrentContext
tried to set TabBar hidden
checked in storyboard that edges of TableVC extend under Bottom Bar and Bottom Bar in Modal View (AddCharacterVC) is set to none
All with no results.
Now the only thing I can think of is to try making modalPresentationStyleCustom and use UIPresentationController (I'm trying to do it now, but I haven't done it before). Am I missing something? Could there be other way to do it? Sorry, I cannot post images here yet. Many thanks in advance!
Ok, so I've set the modalPresentationStile to UIModalPresentationCustom, and used UIPresentationController - I've just copied code from WWDC-14's LookInside project and modified it a bit.
I'm not sure if it was the best solution, but it worked in my case.

UINavigationBar/Status Bar issue in IOS7

Final EDIT
(Rather than having an overly long question with edits making a final edit for clarification, please see other edits if needed).
Controller Setup
I have an application that is setup as follows:
InitialViewController (subclass of ECSlidingViewController)
Main Navigation Controller (subclass of UINavigationController)
Main Home View Controller (subclass of UIViewController)
In the viewDidLoad of the initialViewController I load the main navigation controller in with the Home View Controller as its root.
self.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"MainNavVC"];
The Issue
On the first load of the application the status bar and navigation bar are seperated.
This is the desired effect.
However, I then load a modal view controller and close it, using the standard methods:
[self performSegueWithIdentifier:#"LoadSelectOpponentVC" sender:self];
Then close with:
[self dismissViewControllerAnimated:YES completion:nil];
This in turn causes the main navigation controller (holding the home view controller) to display the status bar incorrectly and overlapping:
Testing
The plist setting is set to YES - View controller-based status bar appearance
I have tried setting the edgesForExtendedLayout to the relevant none, but no change.
Logging
I have tried to log out some frames to see where the issue occurs:
On first Load:
Main Nav VC - View Frame - {{0, 0}, {320, 480}}
Main Nav VC - Nav Bar Frame - {{0, 0}, {320, 44}}
Initial VC - View Frame - {{0, 0}, {320, 480}}
Home VC - View Frame - {{0, 0}, {320, 480}} -- viewDidLoad Home VC
Home VC - View Frame - {{0, 64}, {320, 416}} -- viewWillAppear Home VC
--- After Modal is opened/closed ----
Home VC - View Frame - {{0, 64}, {320, 416}} -- viewWillAppear Home VC
Main Nav VC - View Frame - {{0, 0}, {320, 480}} -- viewWillAppear Main Nav
Main Nav VC - Nav Bar Frame - {{0, 20}, {320, 44}} -- viewWillAppear Main Nav
Home VC - View Frame - {{0, 44}, {320, 436}} -- viewDidAppear Home VC
Did you try Apple recommendation about "Preventing the Status Bar from Covering Your Views": https://developer.apple.com/library/content/qa/qa1797/_index.html
And did you have a look at "UIBarPositioningDelegate": https://developer.apple.com/documentation/uikit/uibarpositioningdelegate
In iOS 7.0 UIViewController works by default this way. View will be full screen if you are using UIViewController inside UINavigationController and the navigationBar is visible.
If navigationBar is visible do following.
==>
self.edgesForExtendedLayout = UIRectEdgeNone
if navigationBar is hidden do following .
==>
Adjust all the UIView elements by shifting 20 points
If you use Interface builder, you can use iOS6/7 deltas: First, "view as iOS 6.0", then set a delta of "20" to achieve the +20 offset in iOS 7
I'm surprised no one has hit on the right answer yet. UIBarPositioningDelegate works like a charm! Just make your view controller a UIBarPositioningDelegate and assign it as the bar's delegate. Position the bar 20 pixels from the top of your view. Then add this method to your view controller (only available in iOS7+):
- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar
{
return UIBarPositionTopAttached;
}
I had a similar issue with a "hamburger" menu button that slide the main view controller over and had a menu view controller on the side. I found that the menu view controller's navigation bar was not aware if the status bar was shown or not. I fixed it by posting a notification when the status bar was shown and hiddden then doing
[self.navigationController setNavigationBarHidden:YES/NO animated:NO];
in the menu's view controller.
I fixed such a problem using answer from this post: iOS 7 | Navigation bar / Toolbar buttons very close to status bar
Using Autolayout you should ignore setting a new Frame. You should add a Top Space Constraint equal to 20 for the TopBar for iOS 7.
Did you try to add the following code to your viewDidLoad method :
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
self.edgesForExtendedLayout = UIRectEdgeNone;
It is quickly explained in Apple migrating to iOS 7 doumentation.
I have answered this problem at length in this answer to a similar question. The short answer is this: there is no way to get the automatic status bar layout behavior you're used to from iOS 6 and earlier. You'll have to design around it, or find a way to simulate the old style (I cover both approaches).
I strongly advise you not to make manual adjustments to the navigation bar frame. Let UINavigationController handle that yourself. Most likely, your problem is that that your navigation controller's view's frame isn't equal to the UIScreen's bounds.
I know that you have ViewController as Main VC. But if someone is using UITableviewController and having the same problem, this code solves the issue:
self.tableView.contentInset = UIEdgeInsetsMake(20.0f, 0.0f, 0.0f, 0.0f);
The solution to this is actually very simple. It involves manipulating UINavigationBar's center.y value, which is what UIKit natively uses in order to adjust UINavigationBar to the status bar's height. For simplicity's sake, I subclassed UINavigationBar and did the following:
#implementation MyNavigationBar
- (void) setCenter:(CGPoint)center {
// Anything less than or equal to 22 is something we don't want (below SB height)
if(center.y > 22) [super setCenter:center];
}
#end
Yes had same problem. followed all the step but no change.
Got it to work by making sure AutoLayout set up correctly for the whole screen not just the top view/toolbar as specified in
"Preventing the Status Bar from Covering Your Views" : https://developer.apple.com/library/ios/qa/qa1797/_index.html
At least for all Views just below the main Viewcontroller.view.
There is a built-in way to do this.
Same as Joel Cave's answer, but elaborated:
Make your navigation bar have a Y origin of 20 points.
Then in the .h file:
#interface XYZViewController : UIViewController <UIBarPositioningDelegate>
And in the .m file:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationBar.delegate = self;
}
- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar {
return UIBarPositionTopAttached;
}
For me solution was be shifting Navigation Bar to 20 point
I'm a little too late to the party, but since I faced the same issue and this was the first result that showed up in the search, I guess my answer could help other people :)
I fixed the problem by implementing
- (BOOL)shouldAutorotate
{
return NO;
}
in the view controller that presents the modal.
Try it, all navigationBars should be translucent is disable.
[self.navigationController.navigationBar setTranslucent:NO];
If you designed your view with a storyboard then you can solve the problem using XCode. Select the NavigationBar widget and uncheck "Translucent".