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()
Related
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..
I'm using Xcode 6 Beta 3, iOS 8 SDK. Build Target iOS 7.0 using Swift. Please refer to my problem step by step with screenshots below.
I have a UICollectionView in Storyboard. 1 Prototype UICollectionViewCell which contains 1 label in the centre (no autoresizing rule). Purple background was to mark a contentView that is generated in runtime by the Cell I guess. That view will be resized properly base on my UICollectionViewLayoutDelegate eventually, but not on iOS 7. Notice that I'm using Xcode 6 and the problem only happens on iOS 7.
When I build the app on iOS 8. Everything is okay.
Note: Purple is the contentView, Blue is my UIButton with rounded corner.
However, on iOS 7, all the subViews inside the Cell suddenly shrink to the frame of (0,0,50,50) and never conforms to my Autoresizing rule anymore.
I assume this is a bug in iOS 8 SDK or Swift or maybe Xcode?
Update 1: This problem still exists in the official Xcode 6.0.1 ! The best work around is like what KoCMoHaBTa suggested below by setting the frame in cellForItem of the cell (You have to subclass your cell though). It turned out that this is a incompatibility between iOS 8 SDK and iOS 7 (check ecotax's answer below quoted from Apple).
Update 2:
Paste this code at the beginning of your cellForItem and things should be okay:
/** Xcode 6 on iOS 7 hot fix **/
cell.contentView.frame = cell.bounds;
cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
/** End of Xcode 6 on iOS 7 hot fix **/
contentView is broken. It can be also fixed in awakeFromNib
ObjC:
- (void)awakeFromNib {
[super awakeFromNib];
self.contentView.frame = self.bounds;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
Swift3:
override func awakeFromNib() {
super.awakeFromNib()
self.contentView.frame = self.bounds
self.contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
I encountered the same problem and asked Apple DTS for help. Their reply was:
In iOS 7, cells’ content views sized themselves via autoresizing
masks. In iOS 8, this was changed, cells stopped using the
autoresizing masks and started sizing the content view in
layoutSubviews. If a nib is encoded in iOS 8 and then decode it on iOS
7, you’ll have a content view without an autoresizing mask and no
other means by which to size itself. So if you ever change the frame
of the cell, the content view won’t follow.
Apps being deploying back to iOS 7 will have to work around this by
sizing the content view itself, adding autoresizing masks, or adding
constraints.
I guess this means that it's not a bug in XCode 6, but an incompatibility between the iOS 8 SDK and iOS 7 SDK, which will hit you if you upgrade to Xcode 6, because it will automatically start using the iOS 8 SDK.
As I commented before, the workaround Daniel Plamann described works for me. The ones described by Igor Palaguta and KoCMoHaBTa look simpler though, and appear to make sense giving Apple DTS' answer, so I'll try those later.
I encountered the same issue and hope that Apple will fix this with the next Xcode version. Meanwhile I use a workaround. In my UICollectionViewCell subclass I've just overridden layoutSubviews and resize the contentView manually in case the size differs from collectionViewCell size.
- (void)layoutSubviews
{
[super layoutSubviews];
BOOL contentViewIsAutoresized = CGSizeEqualToSize(self.frame.size, self.contentView.frame.size);
if( !contentViewIsAutoresized) {
CGRect contentViewFrame = self.contentView.frame;
contentViewFrame.size = self.frame.size;
self.contentView.frame = contentViewFrame;
}
}
Another solution is to set the contentView's size and autoresizing masks in -collectionView:cellForItemAtIndexPath: like the following:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID = #"CellID";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
// Set contentView's frame and autoresizingMask
cell.contentView.frame = cell.bounds;
cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
// Your custom code goes here
return cell;
}
This works with Auto Layout too, since auto resizing masks are translated to constraints.
In Xcode 6.0.1 contentView for UICollectionViewCell is broken for iOS7 devices.
It can be also fixed by adding proper constraints to UICollectionViewCell and its contentView in awakeFromNib or init methods.
UIView *cellContentView = self.contentView;
cellContentView.translatesAutoresizingMaskIntoConstraints = NO;
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[cellContentView]|"
options:0
metrics:0
views:NSDictionaryOfVariableBindings(cellContentView)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[cellContentView]|"
options:0
metrics:0
views:NSDictionaryOfVariableBindings(cellContentView)]];
This will not work correctly without any of the other mentioned workarounds because of a bug in Xcode 6 GM with how Xcode compiles xib files into the nib format. While I cannot say for 100% certainty it is Xcode related and not having to do with runtime, I'm very confident - here's how I can show it:
Build+Run the application in Xcode 5.1.
Go to the simulator application's directory and copy the compiled .nib file for the xib you are having issues with.
Build+Run the application in Xcode 6 GM.
Stop the application.
Replace the .nib file in the newly built application's simulator folder with the .nib file created using Xcode 5.1
Relaunch the app from the simulator, NOT from Xcode.
Your cell loaded from that .nib should work as expected.
I hope everyone who reads this question will file a Radar with Apple. This is a HUGE issue and needs addressing before the final Xcode release.
Edit: In light of ecotax's post, I just wanted to update this to say it is now confirmed behavior differences between building in iOS 8 vs iOS 7, but not a bug. My hack fixed the issue because building on iOS 7 added the autoresizing mask to the content view needed to make this work, which Apple no longer adds.
The answers in this post work, what I never understood is why it works.
First, there are two "rules":
For views created programmatically (Ex. [UIView new]), the property translatesAutoresizingMaskIntoConstraints is set to YES
Views created in interface builder, with AutoLayout enabled, will have the property translatesAutoresizingMaskIntoConstraints set to NO
The second rule does not seem to apply to top-level views for which you do not define constraints for. (Ex. the content view)
When looking at a Storyboard cell, notice that the cell does not have its contentView exposed. We are not "controlling" the contentView, Apple is.
Deep dive into storyboard source code and see how contentView cell is defined:
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
Now the cell's subviews (notice the translatesAutoresizingMaskIntoConstraints="NO"):
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NaT-qJ-npL" userLabel="myCustomLabel">
The contentView does not have it's translatesAutoresizingMaskIntoConstraints set to NO. Plus it lacks layout definition, maybe because of what #ecotax said.
If we look into the contentView, it does have an autoresizing mask, but no definition for it:
<autoresizingMask key="autoresizingMask"/>
So there are two conclusions:
contentView translatesAutoresizingMaskIntoConstraints is set to YES.
contentView lacks definition of a layout.
This leads us to two solutions which have been talked about.
You can set the autoresizing masks manually in awakeFromNib:
self.contentView.frame = cell.bounds;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Or you can set the contentView translatesAutoresizingMaskIntoConstraints to NO in awakeFromNib and define constraints in - (void)updateConstraints.
This is the Swift version of #Igor's answer which is accepted and thanks for your nice answer mate.
First Goto your UICollectionViewCell Subclass and paste the following code as it is inside the class.
override func awakeFromNib() {
super.awakeFromNib()
self.contentView.frame = self.bounds
self.contentView.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
}
By the way I am using Xcode 7.3.1 and Swift 2.3. Solution is tested on iOS 9.3 which is working flawlessly.
Thanks, Hope this helped.
In swift, place the following code in the collection view cell subclass:
override var bounds: CGRect {
didSet {
// Fix autolayout constraints broken in Xcode 6 GM + iOS 7.1
self.contentView.frame = bounds
}
}
I have found that there are also issues with contentView sizing in iOS 8. It tends to get laid out very late in the cycle, which can cause temporary constraint conflicts. To solve this, I added the following method in a category of UICollectionViewCell:
- (void)fixupContentView
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 80100
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) {
self.contentView.frame = self.bounds;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
} else {
[self layoutIfNeeded];
}
#endif
#endif
}
This method should be called after dequeuing the cell.
I fixed doing this:
override func layoutSubviews() {
contentView.superview?.frame = bounds
super.layoutSubviews()
}
see: here
Just make sure you check the check box "Autoresize subviews" in the nib for that collection view cell. It will work fine on both iOS 8 and iOS 7.
I am getting a really strange animation behaviour when pushing another view controller that has the bottom bar hidden with hidesBottomBarWhenPushed. The first thread I found was that: Strange animation on iOS 7 when using hidesBottomBarWhenPushed in app built targeting <= iOS 6 but as my application is only build and run on iOS7 it is not the case for my problem.
Please see the following video that shows the problem (look in the top right corner):
https://dl.dropboxusercontent.com/u/66066789/ios7.mov
This strange animation shadow only occurs when hidesBottomBarWhenPushed is true.
How can I fix that?
Solved my problem:
self.tabBarController.tabBar.hidden=YES;
In the second view controller is the way to go.
Leo Natan is correct. The reason for this blur effect is because the entire Tab Bar Controller is being animated underneath the navigation controller, and behind that view is a black UIWindow by default. I changed the UIWindow background color to white and that fixed the issue.
hidesBottomBarWhenPushed seems to work great with UITabBars (iOS 7/8).
Turn off the Translucent property of Navigation Bar in Storyboard.
In My Case, I had TabBarViewController with UINavigationController in each tabs & faced similar issue. I used,
nextScreen.hidesBottomBarWhenPushed = true
pushViewToCentralNavigationController(nextScreen)
It works fine when nextScreen is UITableViewController subclass & applied auto layout. But, It does not work fine when nextScreen is UIViewController. I found it depends on nextScreen auto layout constraints.
So I just updated my currentScreen with this code -
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.tabBarController?.tabBar.hidden = true
}
For more details - https://stackoverflow.com/a/39145355/2564720
An elegant way of doing this, while keeping transparency, is to add this to the root UIViewController:
- (void)viewWillAppear:(BOOL)animated {
[UIView animateWithDuration:0.35f animations:^{
self.tabBarController.tabBar.alpha = 1.0f;
}];
}
- (void)viewWillDisappear:(BOOL)animated {
[UIView animateWithDuration:0.35f animations:^{
self.tabBarController.tabBar.alpha = 0.0f;
}];
}
This way you'll get a nice fade-in/fade-out animation of the tab bar.
What if in the second view controller in viewWillAppear you put
[self.navigationController setToolbarHidden:YES animated:NO];
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.
The app I'm working on has a custom nab bar but supports iOS 4.2-iOS 5, so I need to set the UINavigationBar background and tint in this old school way in my app delegate.
#implementation UINavigationBar (UINavigationBarCategory)
- (void)drawRect:(CGRect)rect {
self.tintColor = [UIColor colorWithRed:42.0/255.0
green:164.0/255.0
blue:182.0/255.0
alpha:1.0];
UIImage *img = [UIImage imageNamed:#"navbar_bg.png"];
[img drawInRect:CGRectMake(0.0, 0.0,
self.frame.size.width,
self.frame.size.height)];
}
#end
This works for the most part, but I noticed when the app is first starting, the UIBarButtonItems flash the default navigation bar color for a second before they correct themselves and change color to match the navigation bar. Interestingly, the navigation bar itself uses the background image correctly from the get-go.
To be clear, I'm using setBackgroundImage for UINavigationBar on iOS 5 devices which works as expected so the flash is only in iOS 4.
Anyone have any insight on why this would happen and/or how to fix it?
The bar button items are the wrong color? You can manually set their tint color in viewDidLoad: to the tint color
navigationBar.rightBarButtonItem.tintColor = [UIColor ...]
if you're using a nib file. Otherwise you can do the same thing in loadView: . Either way this code will get executed as part of the initial draw loop so you'll have the proper color without any flashing.
Also for future reference, it's technically incorrect to override a method inside a category. (The latest version of Xcode, 4.3, will give you a warning about this). You should either properly subclass UINavigationBar or do "method swizzling". But that's pretty tough so don't worry about it right now :)
If you call the class with the code referenced in viewDidLoad try moving it to awakeFromNib