Custom container does not adjust correctly layout to in-call status bar
After playing around I was able to determine that there was a problem with the frame being set - the frame of the container view controller seemed to be offseted but not resized when the initialTransition(to:)
was called (in container's viewDidLoad
), thus causing the child the get a frame that overlapped the bottom of the screen by the offset - 20 points.
My first approach was to add setting the frame once again in container's viewDidAppear
, which in the end solved the problem, but cause a glitch - for a moment the bottom seemed cropped, and then viewDidAppear
was called and the layout was adjusted correctly. This glitch looked bad.
I finally achieved what I wanted by overriding container's viewDidLayoutSubviews
and setting the frame of the child there (thus when the container gets notified to adjust its frame to the status bar, the information about a new frame gets passed to the child).
override func viewDidLayoutSubviews() {
self.selectedViewController?.view.frame = self.view.frame
super.viewDidLayoutSubviews()
}
In-call status bar pushes UIPageViewController content view down
I ended up resolving this by ensuring the right constraints were on my view controller's main view. Here is an example where I am adding a view controller's view as a subview to a root view controller and then using Masonry (https://github.com/SnapKit/Masonry) to establish constraints that will snap all the child view controller's main view's edges to the root view controller (which itself already has autoresizing masks in place from Interface Builder). This resolved my issue of the status bar leaving that black space because it was pushing this view down (rather than re-sizing):
[self.view addSubview:viewController.view];
[viewController.view mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
In your case, and I don't have your full code so I don't know for sure but something like:
[self.pageController.view mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.pageController.view.superview);
}];
.. ought to do the trick.
Detecting if user has in call status bar
UIApplicationDelegate
has these two methods.
// ObjC
- (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame; // in screen coordinates
- (void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame;
// Swift
func application(_ application: UIApplication, willChangeStatusBarFrame newStatusBarFrame: CGRect)
func application(_ application: UIApplication, didChangeStatusBarFrame oldStatusBarFrame: CGRect)
And there are Notifications
too.
//ObjC
UIApplicationWillChangeStatusBarFrameNotification
UIApplicationDidChangeStatusBarFrameNotification
// Swift
Notification.Name.UIApplicationWillChangeStatusBarFrame
Notification.Name.UIApplicationDidChangeStatusBarFrame
but they're not posted on the launch of the app, so I wouldn't recommend it.
The simulator has an useful tool to test that.
Hardware->Toggle In-Call Status Bar (⌘Y)
I would suggest you to implement those methods on your AppDelegate
file. They will be called when the status bar change it's height. One of them it's called before and the other after the change.
Assuming you want that your ViewController
to be notified when the change occurs, one option, is to send notifications. Like this
First, add this property/variable on AppDelegate
// ObjC
@property (assign, nonatomic) CGRect currentStatusBarFrame;
// Swift
var currentStatusBarFrame: CGRect = .zero
then, implement willChangeStatusBarFrame
// ObjC
- (void) application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame
{
self.currentStatusBarFrame = newStatusBarFrame;
[[NSNotificationCenter defaultCenter] postNotificationName:@"Status Bar Frame Change"
object:self
userInfo:@{@"current status bar frame": [NSValue valueWithCGRect:newStatusBarFrame]}];
}
// Swift
func application(_ application: UIApplication, willChangeStatusBarFrame newStatusBarFrame: CGRect) {
currentStatusBarFrame = newStatusBarFrame
NotificationCenter.default.post(
name: NSNotification.Name(rawValue: "Status Bar Frame Change"),
object: self,
userInfo: ["current status bar frame": newStatusBarFrame])
}
And we're done with the base of our Status Bar Frame Checker
. The next part you implement on any ViewController
that needs to know the status bar frame.
Any time you want to get the status bar frame, you do like so
// ObjC
[(AppDelegate*)[[UIApplication sharedApplication] delegate] currentStatusBarFrame]
// Swift
(UIApplication.shared.delegate as! AppDelegate).currentStatusBarFrame
And to be notified when it changes, add this to ViewDidLoad
method.
In ObjC
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(statusBarFrameChanged:)
name:@"Status Bar Frame Change"
object:[[UIApplication sharedApplication] delegate]];
And implement this method
- (void) statusBarFrameChanged:(NSNotification*)notification
{
CGRect newFrame = [[notification.userInfo objectForKey:@"current status bar frame"] CGRectValue];
NSLog(@"new height %f", CGRectGetHeight(newFrame));
}
In Swift
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "Status Bar Frame Change"),
object: nil,
queue: nil) { (note) in
guard let currentStatusBarFrame = note.userInfo?["current status bar frame"] as? CGRect else {return}
print("New Height", currentStatusBarFrame.height)
}
Prevent In-Call Status Bar from Affecting View
Answering my own question, the best solution I was able to find for this is to manually change the main view's frame to start at the origin and have the size of the entire screen (both width and height). That way, you receive the same effect as you would if you were to use a navigation controller for example, where the view remains in place and the in-call status bar appears to be on top of it (on a different layer, without affecting the app view's display).
Status bar sized incorrectly with a contained UINavigationController
You are adding a view (the navigation controller's view) as subview to another view (the parent view controller's view), without giving it a frame! This is something you should never do.
Just add these lines to your code:
aNav.view.frame = aParent.view.bounds;
aNav.view.autoresizingMask =
UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
Now the navigation controller's view has a frame, and maintains it in relation to its superview.
Related Topics
How Programmatically Restart an iPhone App in iOS
Set Custom Images to the Uibarbuttonitem But It Doesn't Show Any Image
Missing Marketing Icon Xcode Bug
What Is Build Object File Extension in iOS
Bounding Box from Vndetectrectanglerequest Is Not Correct Size When Used as Child Vc
Playing Back Audio Using Avaudioplayer iOS 7
Why Is This Code Not Recognising the Nsstring as Being Equal
Xcode UI Testing Error Keyboard
Datefromstring Returns Nil for Some Values
Correct Way to Setting a Tag to All Cells in Tableview
Tableview Cell How Do We Resize Cell in Swift Along with Image and Label
Is It Okay to Leave Out Unnecessary Launch Images
As3 for iOS:How to Serialize an Array and Then Save It