使用自定义模式演示处理通话状态栏

Posted

技术标签:

【中文标题】使用自定义模式演示处理通话状态栏【英文标题】:Handling In-Call Status Bar with Custom Modal Presentation 【发布时间】:2014-02-26 20:37:23 【问题描述】:

问题

我注意到在通话期间使用UIViewControllerAnimatedTransitioning 呈现UINavigationController(带有根视图控制器,自然已经推送)时出现了一些奇怪的行为。

如果在显示导航控制器之后启用通话状态栏,则导航控制器会按预期向下移动其视图。但是当通话结束时,控制器不会将其视图重新向上移动,在状态栏下方留下 20p 的间隙。 如果在显示控制器之前启用通话状态栏,控制器根本不考虑状态栏,留下 44p 高导航栏的 4p 从下方窥视40p 状态栏。当通话结束时,控制器会向下移动其视图以适应正常的 20p 状态栏。

*注意:这是在模拟器上测试的,因为启用/禁用通话状态栏很容易,但测试人员在实际手机上观察到了这种现象。

我的(部分)解决方法

如果状态栏的高度异常,我通过在演示期间调整控制器的框架解决了这个问题:

@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

此解决方案确保导航栏位于状态栏下方,但导航控制器在通话结束时仍无法自行返回。所以应用至少可以使用,但是通话结束后导航栏上方有一个难看的 20p 间隙。

有更好的方法吗?

我是否遗漏了一些关键步骤来确保导航控制器自己负责通话状态栏?当使用内置的模态演示样式呈现时,它工作得很好。

在我看来,这有点像 UIKit 错误——毕竟,导航控制器似乎收到了UIApplicationWillChangeStatusBarFrameNotification(参见问题的第二点)。如果其他人遇到此问题并找到了更好的方法,我将不胜感激。

【问题讨论】:

你找到解决方法了吗?我的问题是我的应用程序的主屏幕没有导航栏,带有一个有导航栏的滑入式面板。滑入式面板全屏时,通话中的绿色条显示,面板变短,但滑入式面板部分屏幕时,通话进度条消失。因此,当我打开和关闭面板时,面板会变得越来越短。 @MusiGenesis 我还没有找到解决办法,但 Apple 确实开始查看我的错误报告。如果他们确认这确实是一个错误,我会在这里发布。 @Austin 你找到解决办法了吗? @tikhop 我从未找到解决方案,Apple 也从未回复我的错误报告。它可能ios 8 中被修复;应用程序的设计发生了变化,因此不再需要自定义演示控制器,因此我没有对其进行测试。 @Austin,感谢您的回复。所以在 iOS8 中没有任何修复,我遇到了同样的问题,只找到了一个看起来像你的解决方案。 【参考方案1】:

我花了太多时间来解决状态栏高度问题,并提出了一个适合我的通用解决方案,我认为也适用于您的情况。

首先,关于状态栏的一些奇怪的事情。

    通常高 20 点,屏幕通常高 568 点

    “通话中”时,状态栏高40点,屏幕高548点

    隐藏状态栏时,状态栏高度为 0 点,屏幕高度为 568 点

如果状态栏发生变化但您不更新屏幕高度,则计算将关闭,这可以在一些非常知名(甚至默认)的应用程序中看到。

所以,我想出的解决方案有两个:1. 创建一个宏来获取调整后的屏幕高度 2. 注册一个通知以在状态栏更改时更新视图。

这是宏,我建议将它们放入您的 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];

【讨论】:

updateViews 到底是做什么的?您要将通知观察添加到哪个类? @George 我本可以更好地解释这一点。 updateViews 是通用的,用于更新您拥有的任何视图的布局。 layoutSubviewsUIView 上工作,但在视图控制器上没有任何可比性,因此在视图控制器的视图上调用 layoutSubviews 会起作用。【参考方案2】:

我遇到了同样的问题,问题在于我展示的视图由于通话栏而自动调整了 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]

【讨论】:

【参考方案3】:

只需在 layoutSubviews 中设置框架,因为它在状态栏高度发生变化时被调用。一般来说,根据经验,仅在 layoutSubviews 中进行所有框架设置。

【讨论】:

【参考方案4】:

这是我在我的应用程序委托中放入的一种解决方法,它为我解决了问题:

- (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame

    if (newStatusBarFrame.size.height < 40) 
        for (UIView *view in self.window.subviews) 
            view.frame = self.window.bounds;
        
    

【讨论】:

【参考方案5】:

我遇到了同样的问题,我通过调用更新框架视图修复了它,所以我得到了高度状态栏。我的问题解决了。

 UIView *toViewSnapshot = [toView resizableSnapshotViewFromRect:toView.frame afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];

【讨论】:

以上是关于使用自定义模式演示处理通话状态栏的主要内容,如果未能解决你的问题,请参考以下文章

使用自定义演示控制器时保留原始状态栏外观

状态栏高度变化处理

通话状态栏更改时查看调整大小[重复]

UIViewController 通话状态栏问题

jQuery - 检测 iPhone 通话状态栏

当用户在通话状态栏中时,导航栏布局出现异常