Обработка строки состояния In-Call с пользовательской модальной презентацией
Эта проблема
Я заметил странное поведение при представлении UINavigationController
(с контроллером корневого представления, уже нажал, естественно) с UIViewControllerAnimatedTransitioning
во время телефонного звонка.
- Если строка состояния вызова включена после представления навигационного контроллера, навигационный контроллер смещает свой вид вниз, как и ожидалось. Но когда вызов завершен, контроллер не сдвигает свой взгляд назад, оставляя пробел в 20 пунктов под строкой состояния.
- Если строка состояния во время разговора включена до представления контроллера, контроллер вообще не учитывает строку состояния, оставляя 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
(см. второй пункт "Проблемы"). Если кто-то еще сталкивался с этой проблемой и нашел лучший способ, я был бы очень признателен за решение.
5 ответов
Я потратил слишком много времени на преодоление проблемы с высотой строки состояния и нашел общее решение, которое работает для меня, и я думаю, что оно подойдет и для вашей ситуации.
Во-первых, пара вещей, которые странны в строке состояния.
Это обычно 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];
У меня была та же самая проблема, и проблема была с представлением, которое я представлял, который был автоматически настроен на 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]
}
Вот обходной путь, который я добавил в свой делегат приложения, который исправил проблему для меня:
- (void)application:(UIApplication *)application willChangeStatusBarFrame:(CGRect)newStatusBarFrame
{
if (newStatusBarFrame.size.height < 40) {
for (UIView *view in self.window.subviews) {
view.frame = self.window.bounds;
}
}
}
У меня была та же проблема, и я исправил ее с помощью просмотра кадра обновления вызовов, поэтому я получил строку состояния высоты. Моя проблема была решена.
UIView *toViewSnapshot = [toView resizableSnapshotViewFromRect:toView.frame afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
Просто установите фрейм в layoutSubviews так, как он вызывается при изменении высоты строки состояния. Как правило, все настройки фрейма выполняются только в layoutSubviews.