Uiviewcontroller In-Call Status Bar Issue

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



Leave a reply



Submit