使用自定义模式演示处理通话状态栏
问题
在通话过程中用UIViewControllerAnimatedTransitioning
呈现UINavigationController
(具有根视图控制器,已经自然按下了),我注意到了一些奇怪的行为。
- 如果在显示导航控制器后启用通话状态栏,则导航控制器将按预期方式切换其视图。 但是当通话结束时,控制器不会将其视图移回,在状态栏下留下20p的间隔。
- 如果在呈现控制器之前启用通话状态栏,则控制器根本不占用状态栏,导致44p高的导航栏的4p从40p状态栏下面窥视。 当通话结束时,控制器将其视图向下移动以适应正常的20p状态栏。
*注意:这是在模拟器上testing的,由于启用/禁用通话状态栏非常方便,但是testing人员在实际的手机上观察到了这种现象。
我的(部分)解决方法
如果状态栏是不正常的高度,我会在演示期间通过调整控制器的框架来解决问题:
@interface CustomAnimationController : NSObject <UIViewControllerAnimatedTransitioning> @end @implementation CustomAnimationController - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { UIViewController *toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIView *container = [transitionContext containerView]; CGRect frame = [transitionContext finalFrameForViewController:toController]; if (CGRectEqualToRect(frame, CGRectZero)) { // In my experience, the final frame is always a zero rect, so this is always hit UIEdgeInsets insets = UIEdgeInsetsZero; // My "solution" was to inset the container frame by the difference between the // actual status bar height and the normal status bar height insets.top = CGRectGetHeight([UIApplication sharedApplication].statusBarFrame) - 20; frame = UIEdgeInsetsInsetRect(container.bounds, insets); } toController.view.frame = frame; [container addSubview:toController.view]; // Perform whiz-bang animation here } @end
此解决scheme确保导航栏位于状态栏下方,但导航控制器在通话结束后仍然无法自行备份。 所以这个应用至less可以使用,但是通话结束后,导航栏上方会有一个20p的空白。
有没有更好的办法?
我是否错过了一些关键的步骤来确保导航控制器自己负责通话状态栏? 当使用内置的模式演示风格时,它工作得很好。
在我看来,这是一个UIKit错误 – 毕竟,导航控制器似乎收到UIApplicationWillChangeStatusBarFrameNotification
(请参阅问题的第二点)。 如果其他人遇到这个问题,并find了更好的方法,我将不胜感激解决scheme。
我花了太多的时间来处理状态栏高度问题,并提出了一个适用于我的通用解决scheme,我认为它也适用于您的情况。
首先,一些关于状态栏的事情很奇怪。
-
它通常是20点高,屏幕通常是5 6 8点高
-
在“通话中”,状态栏为40点,屏幕为548点
-
状态栏处于隐藏状态时,状态栏为0点,屏幕高度为5 6 8点
如果状态栏发生变化,但不更新屏幕的高度,那么计算将会closures,这可以在一些相当大的名字(甚至默认)应用程序中看到。
所以,我提出的解决scheme有两个方面:1.创build一个macros来获得调整后的屏幕高度2.注册一个通知来更新状态栏更改时的视图。
这里是macros,我build议把这些放在你的prefix
文件中
#define kScreenWidth [UIScreen mainScreen].bounds.size.width #define kStatusBarHeight (([[UIApplication sharedApplication] statusBarFrame].size.height == 20.0f) ? 20.0f : (([[UIApplication sharedApplication] statusBarFrame].size.height == 40.0f) ? 20.0f : 0.0f)) #define kScreenHeight (([[UIApplication sharedApplication] statusBarFrame].size.height > 20.0f) ? [UIScreen mainScreen].bounds.size.height - 20.0f : [UIScreen mainScreen].bounds.size.height)
此外,这里是通知中心的电话,我发现100%的时间状态栏更改。
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self.view selector:@selector(layoutSubviews) name:UIApplicationWillChangeStatusBarFrameNotification object:nil];
这是一个解决方法,我把我的应用程序委托,解决了我的问题:
- (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame { if (newStatusBarFrame.size.height < 40) { for (UIView *view in self.window.subviews) { view.frame = self.window.bounds; } } }
我有同样的问题,问题是我提出的是由于通话时间自动调整了20pt。 但是,因为我把视图插入容器,我不得不重置框架来匹配容器的边界。
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { UIView* destinationView = [transitionContext viewForKey:UITransitionContextToViewKey]; UIView* container = transitionContext.containerView; // Make sure destination view matches container bounds // This is necessary to fix the issue with in-call bar // which adjusts the view's frame by 20pt. destinationView.frame = container.bounds; // Add destination view to container [container insertSubview:destinationView atIndex:0]; // [reducted] }
只需在layoutSubviews中设置框架,就像在状态栏高度发生变化时所调用的那样。 一般来说,根据经验,只能在layoutSubviews中进行所有框架设置。