Обработка строки состояния во время разговора с настраиваемым модальным представлением

Эта проблема

Я заметил странное поведение при представлении UINavigationController (с уже выдвинутым корневым контроллером представления) с UIViewControllerAnimatedTransitioning во время телефонного звонка.

  • Если строка состояния при вызове включена после отображения контроллера навигации, контроллер навигации сдвигает свой вид вниз, как ожидалось. Но когда вызов завершен, контроллер не переключает свой обзор обратно вверх, оставляя зазор 20p под строкой состояния.
  • Если строка состояния при вызове включена до представления контроллера, контроллер вообще не учитывает строку состояния, оставляя 4p из 44p-высокой панели навигации выглядывающими из-под строки состояния 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

Это решение гарантирует, что панель навигации находится под строкой состояния, но контроллер навигации по-прежнему не может сдвинуться обратно после завершения вызова. Таким образом, приложение, по крайней мере, можно использовать, но после завершения вызова над панелью навигации появляется уродливый зазор в 20 пикселей.

Есть ли способ лучше?

Я пропустил какой-то важный шаг, чтобы убедиться, что контроллер навигации самостоятельно учитывает строку состояния во время разговора? Он отлично работает, когда представлен встроенным модальным стилем представления.

На мой взгляд, это попахивает ошибкой UIKit - в конце концов, кажется, что контроллер навигации получает UIApplicationWillChangeStatusBarFrameNotification (см. Второй пункт проблемы). Если кто-то еще столкнулся с этой проблемой и нашел лучший способ, я был бы очень признателен за решение.


person Austin    schedule 26.02.2014    source источник
comment
Вы когда-нибудь находили исправление для этого? Моя проблема в том, что на главном экране моего приложения нет панели навигации, а на выдвижной панели есть панель навигации. Когда выдвижная панель является полноэкранной, отображается зеленая полоса текущего вызова и панель становится короче, но когда выдвижная панель является частичным экраном, полоса выполнения вызова исчезает. Таким образом, панель становится все короче и короче, когда я открываю и закрываю ее.   -  person MusiGenesis    schedule 29.05.2014
comment
@MusiGenesis Я не нашел исправления, но Apple начала просматривать мой отчет об ошибке. Я отправлю сюда ответ, если они подтвердят, что это действительно ошибка.   -  person Austin    schedule 29.05.2014
comment
@ Остин, ты нашел какое-нибудь решение для этого?   -  person tikhop    schedule 11.12.2014
comment
@tikhop Я так и не нашел решения, и Apple так и не ответила на мой отчет об ошибке. Это может быть исправлено в iOS 8; дизайн приложения изменился, поэтому в настраиваемом контроллере презентации не было необходимости, поэтому с тех пор я его не тестировал.   -  person Austin    schedule 11.12.2014
comment
@ Остин, спасибо за ответ. Итак, в iOS8 ничего не исправлено, я столкнулся с той же проблемой и нашел только одно решение, похожее на ваше.   -  person tikhop    schedule 11.12.2014


Ответы (5)


Я потратил слишком много времени на решение проблемы высоты строки состояния и придумал общее решение, которое работает для меня, и я думаю, что оно будет работать и для вашей ситуации.

Во-первых, пара необычных вещей в строке состояния.

  1. Обычно это 20 точек в высоту, а экран - 5 6 8 точек.

  2. В режиме разговора высота строки состояния составляет 40 пунктов, а экрана - 5 4 8 пунктов.

  3. Пока строка состояния скрыта, высота строки состояния составляет 0 пунктов, а высота экрана 5 6 8 пунктов.

Если строка состояния изменится, но вы не обновите высоту экрана, то вычисления будут отключены, и это можно увидеть в некоторых довольно известных (и даже по умолчанию) приложениях.

Итак, решение, которое я придумал, состоит из двух частей: 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];
person Fennelouski    schedule 13.02.2015
comment
Что конкретно делает updateViews? К какому классу вы добавляете наблюдение за уведомлением? - person George; 01.07.2017
comment
@ Джордж: Я мог бы объяснить это лучше. updateViews является общим для обновления макета любых представлений, которые у вас есть. layoutSubviews работает с UIView, но на самом деле нет ничего сопоставимого с контроллером представления, поэтому вызов layoutSubviews в представлении контроллера представления будет работать. - person Fennelouski; 01.07.2017

У меня была та же проблема, и проблема заключалась в представлении, которое я представлял, который был автоматически скорректирован на 20 пунктов из-за панели вызова. Однако, поскольку я вставляю представление в контейнер, мне пришлось сбросить фрейм, чтобы он соответствовал границам контейнера.

- (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]
}
person Ben Affleck    schedule 27.03.2016

Просто установите рамку в layoutSubviews так, как она вызывается при изменении высоты строки состояния. В общем, как правило, все настройки кадра выполняйте только в layoutSubviews.

person jarora    schedule 16.02.2015

Вот обходной путь, который я добавил в свой делегат приложения, который устранил проблему для меня:

- (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame
{
    if (newStatusBarFrame.size.height < 40) {
        for (UIView *view in self.window.subviews) {
            view.frame = self.window.bounds;
        }
    }
}
person Michael Voong    schedule 10.01.2016

У меня была такая же проблема, и я исправил ее, вызвав обновление кадра, поэтому я получаю строку состояния высоты. Моя проблема была решена.

 UIView *toViewSnapshot = [toView resizableSnapshotViewFromRect:toView.frame afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
person Son Pham    schedule 21.06.2018