Подтвердить что ты не робот

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

Проблема

Я заметил какое-то странное поведение при представлении UINavigationController (с контроллером корневого представления, который уже был нажат, естественно) с помощью UIViewControllerAnimatedTransitioning во время телефонного разговора.

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

4b9b3361

Ответ 1

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

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

  • Обычно он достигает 20 пунктов, а экран обычно 5 6 8 очков

  • Во время "вызова" строка состояния имеет 40 точек, а экран 5 4 8 точек высотой

  • Пока строка состояния скрыта, строка состояния имеет высоту 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];

Ответ 2

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

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

Ответ 3

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

- (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 frame by 20pt.
    destinationView.frame = container.bounds;

    // Add destination view to container
    [container insertSubview:destinationView atIndex:0];

    // [reducted]
}

Ответ 4

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