Переопределение внешнего вида строки состояния на основе контроллера представления

У меня есть приложение, в котором внешний вид строки состояния на основе контроллера представления установлен на YES. Некоторые из моих представлений имеют темный цвет, некоторые из моих представлений имеют светлое содержимое, а приложение имеет довольно сложную иерархию контроллеров представлений, но оно отлично работает с подклассами и переопределением соответствующих методов в сочетании с модальными представлениями, захватывающими стили представления и т. Д.).

Однако мне нужен глобальный способ просмотра определенного элемента вверху (за строкой состояния, внутри границ моего приложения), точно так же, как панель, такая как личная точка доступа / запись GarageBand / панель вызова и т. Д. Вверху . Из-за цвета фона панели я хочу переопределить внешний вид строки состояния при отображении панели (которая может отображаться в любом месте приложения, поэтому я создал подкласс UIWindow и поместил его код представления и просмотр прямо там). Строка отображается точно так, как я хотел, на экранах со светлой строкой состояния содержимого (поскольку текст моей строки белый, а фон темный), но выглядит ужасно в строке состояния темного содержимого (и нет, я не могу изменить цвета строки).

Как я могу глобально переопределить предпочтительный стиль строки состояния «независимо от того, является ли текущий представленный контроллер представления» (конечно, без обхода всех экземпляров методов строки состояния во всех контроллерах представления), при этом все еще используя внешний вид строки состояния на основе контроллера представления? Мое приложение предназначено для iOS 8.0+.


person Can Poyrazoğlu    schedule 19.03.2017    source источник


Ответы (1)


Я закончил очень хакерским (но работающим) способом. Это могло сработать не во всех сценариях, но в моем сработало. Я сохранил представление как есть, и не коснулся ни одного представления или контроллера.

Во-первых, у меня сейчас отображается самый верхний контроллер представления. Я использовал код из iPhone - Как найти самый верхний view controller и немного изменил его для обработки случаев контроллера навигации и панели вкладок:

+ (UIViewController*) topmostControllerForViewController:(__kindof UIViewController*)topController
{
    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }
    if([topController isKindOfClass:[UINavigationController class]]){
        UINavigationController *navController = topController;
        return [self topmostControllerForViewController:navController.visibleViewController];
    }
    if([topController isKindOfClass:[UITabBarController class]]){
        UITabBarController *tabController = topController;
        return [self topmostControllerForViewController:tabController.selectedViewController];
    }
    return topController;
}

+ (UIViewController*) topmostController
{
    __kindof UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
    return [self topmostControllerForViewController:topController];
}

Затем я создал контроллер представления без представления (view равно nil). В этом init методе (не работает при первом вызове, если ввести viewDidLoad:, поскольку он вызывается в процессе перехода, и уже слишком поздно), я добавил следующее:

self.modalPresentationCapturesStatusBarAppearance = YES;
self.modalPresentationStyle = UIModalPresentationOverCurrentContext;

Этот код позволял моему «фиктивному» контроллеру представления (меньше) обрабатывать весь контекст презентации, включая внешний вид строки состояния и то, что происходит с другими контроллерами представлений, когда она представлена. При представлении в текущем контексте контроллер представления сзади не удаляется из иерархии представлений. Если я этого не сделаю, он будет удален, а экран станет черным (поскольку у меня нет представления, и я хочу, чтобы отображался предыдущий контроллер представления).

Все идет нормально. Затем я обычно отображал свою панель, но одновременно представлял этот контроллер представления модально без какого-либо представления. Поскольку контроллер представления не имел представления и был представлен в текущем контексте, он визуально не отображался никаким образом, но, поскольку это было модальное представление, а фиктивный контроллер представления был настроен на захват стиля представления, он запускал iOS чтобы запросить у моего приложения стиль строки состояния. Я просто настроил свой стиль строки состояния, как я хотел, в методах контроллера представления.

Была небольшая проблема. Когда я представил новый контроллер представления, система добавила UITransitionView поверх моего предыдущего контроллера представления. Если бы существовал реальный вид, он был бы поверх вида перехода. Представление перехода полностью прозрачно, но в нем включено взаимодействие с пользователем и фиксируются все события касания, в результате чего мое приложение не отвечает, пока я не отключу контроллер. Мне нужен был мой предыдущий контроллер представления для получения событий касания. Я копал глубже и нашел, где модальная презентация добавляет представление перехода, и удалил его при представлении контроллера представления после завершения анимации перехода:

for (UIView *view in self.subviews) {
        NSString *className = NSStringFromClass([view class]);
        if([className hasPrefix:@"UIT"] && className.length == 16){
            //this must be UITransitionView, but I'm not using it directly since it may interfere with private API usage and get app rejected by Apple.
            //now, we need to find another transition view inside this and remove it
            for (UIView *innerView in view.subviews) {
                className = NSStringFromClass([innerView class]);
                if([className hasPrefix:@"UIT"] && className.length == 16){
                    //this is the transition view that we need to remove
                    [innerView removeFromSuperview];
                } 
            }
        }
}

Поскольку UITransitionView - это частный тип представления, и я не уверен, вызывает ли он проблему с App Store, я выполнил эвристическую проверку UITransitionView, проверив первые буквы UIT и проверив длину имени класса. Это не пуленепробиваемое устройство, но похоже, что оно работает и вряд ли вернет ложное срабатывание.

Теперь все работает как положено. Это взломано и может сломаться в будущем, особенно если модальное представление изменится под капотом. Но будьте уверены, это работает.

person Can Poyrazoğlu    schedule 19.03.2017