PreferredStatusBarStyle не вызывается

Я выполнил этот поток, чтобы переопределить -preferredStatusBarStyle, но он не вызывается. Существуют ли какие-либо опции, которые я могу изменить, чтобы включить это? (Я использую XIB в своем проекте.)

Ответ 1

Возможная основная причина

У меня была такая же проблема, и выяснилось, что это происходит, потому что я не устанавливал контроллер корневого представления в моем окне приложения.

UIViewController, в котором я реализовал preferredStatusBarStyle, был использован в UITabBarController, который контролировал появление представлений на экране.

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

(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ... // other view controller loading/setup code

    self.window.rootViewController = rootTabBarController;
    [self.window makeKeyAndVisible];
    return YES;
}

Альтернативный метод (устаревший в iOS 9)

В качестве альтернативы вы можете вызвать один из следующих способов, в зависимости от ситуации, в каждом из ваших контроллеров представлений в зависимости от цвета фона, вместо того, чтобы использовать setNeedsStatusBarAppearanceUpdate:

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

или

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];

Обратите внимание, что вам также необходимо установить UIViewControllerBasedStatusBarAppearance в NO в файле plist, если вы используете этот метод.

Ответ 2

Для тех, кто использует UINavigationController:

UINavigationController не перенаправляет на вызовы preferredStatusBarStyle его дочерние контроллеры. Вместо этого он управляет своим собственным состоянием - как и должно, он рисует в верхней части экрана, где находится строка состояния, и поэтому должен нести за это ответственность. Поэтому реализация preferredStatusBarStyle в ваших VC в навигационном контроллере ничего не сделает - они никогда не будут вызваны.

Фокус в том, что использует UINavigationController, чтобы решить, что возвращать для UIStatusBarStyleDefault или UIStatusBarStyleLightContent. Он основывает это на UINavigationBar.barStyle. По умолчанию (UIBarStyleDefault) отображается строка состояния UIStatusBarStyleDefault темного фона. И UIBarStyleBlack предоставит строку состояния UIStatusBarStyleLightContent.

TL; ДР:

Если вы хотите UIStatusBarStyleLightContent на UINavigationController использовать:

self.navigationController.navigationBar.barStyle = UIBarStyleBlack;

Ответ 3

Итак, я добавил категорию в UINavigationController, но использовал методы:

-(UIViewController *)childViewControllerForStatusBarStyle;
-(UIViewController *)childViewControllerForStatusBarHidden;

и они возвращают текущий видимый UIViewController. Это позволяет текущему видимому контроллеру просмотра установить свой собственный предпочтительный стиль/видимость.

Вот полный фрагмент кода для него:

В Swift:

extension UINavigationController {

    public override func childViewControllerForStatusBarHidden() -> UIViewController? {
        return self.topViewController
    }

    public override func childViewControllerForStatusBarStyle() -> UIViewController? {
        return self.topViewController
    }
}

В Objective-C:

@interface UINavigationController (StatusBarStyle)

@end

@implementation UINavigationController (StatusBarStyle)

-(UIViewController *)childViewControllerForStatusBarStyle {
    return self.topViewController;
}

-(UIViewController *)childViewControllerForStatusBarHidden {
    return self.topViewController;
}

@end

И для хорошей меры, вот как это реализовано тогда в UIViewController:

В Swift

override public func preferredStatusBarStyle() -> UIStatusBarStyle {
    return .LightContent
}

override func prefersStatusBarHidden() -> Bool {
    return false
}

В Objective-C

-(UIStatusBarStyle)preferredStatusBarStyle {
    return UIStatusBarStyleLightContent; // your own style
}

- (BOOL)prefersStatusBarHidden {
    return NO; // your own visibility code
}

Наконец, убедитесь, что в вашем приложении plist НЕ есть "Просмотр состояния панели управления на основе контроллера", установленный на NO. Либо удалите эту строку, либо установите для нее значение YES (которое, по моему мнению, по умолчанию относится к iOS 7?)

Ответ 4

Для тех, кто все еще борется с этим, это простое расширение в swift должно решить проблему для вас.

extension UINavigationController {
    override open var childForStatusBarStyle: UIViewController? {
        return self.topViewController
    }
}

Ответ 5

Мое приложение использовал все три: UINavigationController, UISplitViewController, UITabBarController, таким образом, они все, кажется, взять под контроль в строке состояния и заставит preferedStatusBarStyle не называть своих детей. Чтобы изменить это поведение, вы можете создать расширение, как и в остальных ответах. Вот расширение для всех трех, в Swift 4. Хотелось бы, чтобы Apple была более ясна в этом отношении.

extension UINavigationController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return self.topViewController
    }

    open override var childViewControllerForStatusBarHidden: UIViewController? {
        return self.topViewController
    }
}

extension UITabBarController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return self.childViewControllers.first
    }

    open override var childViewControllerForStatusBarHidden: UIViewController? {
        return self.childViewControllers.first
    }
}

extension UISplitViewController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return self.childViewControllers.first
    }

    open override var childViewControllerForStatusBarHidden: UIViewController? {
        return self.childViewControllers.first
    }
}

Изменение: Обновление для изменений API Swift 4.2

extension UINavigationController {
    open override var childForStatusBarStyle: UIViewController? {
        return self.topViewController
    }

    open override var childForStatusBarHidden: UIViewController? {
        return self.topViewController
    }
}

extension UITabBarController {
    open override var childForStatusBarStyle: UIViewController? {
        return self.children.first
    }

    open override var childForStatusBarHidden: UIViewController? {
        return self.children.first
    }
}

extension UISplitViewController {
    open override var childForStatusBarStyle: UIViewController? {
        return self.children.first
    }

    open override var childForStatusBarHidden: UIViewController? {
        return self.children.first
    }
}

Ответ 6

Ответ Tyson правильный для изменения цвета строки состояния на белый в UINavigationController.

Если кто-то хочет выполнить тот же результат, написав код в AppDelegate, используйте ниже код и напишите его внутри метода AppDelegate's didFinishLaunchingWithOptions.

И не забудьте установить UIViewControllerBasedStatusBarAppearance в YES в файле .plist, иначе это изменение не отразится.

код

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
     // status bar appearance code
     [[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];

     return YES;
}

Ответ 7

В дополнение к запросу Hippo: если вы используете UINavigationController, то, вероятно, лучше добавить категорию:

//  UINavigationController+StatusBarStyle.h:

@interface UINavigationController (StatusBarStyle)

@end



//  UINavigationController+StatusBarStyle.m:

@implementation UINavigationController (StatusBarStyle)

- (UIStatusBarStyle)preferredStatusBarStyle
{
    //also you may add any fancy condition-based code here
    return UIStatusBarStyleLightContent;
}

@end

Это решение, вероятно, лучше, чем переход к быстрому устаревшему поведению.

Ответ 8

На UINavigationController, preferredStatusBarStyle не называется, потому что его topViewController предпочтительно self. Таким образом, чтобы получить preferredStatusBarStyle childViewControllerForStatusBarStyle вызываемый для UINavigationController, вам нужно изменить его childViewControllerForStatusBarStyle.

Чтобы сделать это для одного UINavigationController (моя рекомендация):

class MyRootNavigationController: UINavigationController {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    override var childViewControllerForStatusBarStyle: UIViewController? {
        return nil
    }
}

Чтобы сделать это для всех UINavigationController (предупреждение: это влияет на UIDocumentPickerViewController, UIImagePickerController и т.д.):

extension UINavigationController {
    open override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return nil
    }
}

Ответ 9

@serenn answer выше по-прежнему является отличным примером для UINavigationControllers. Однако для swift 3 функции childViewController были изменены на vars. Таким образом, код расширения UINavigationController должен быть:

override open var childViewControllerForStatusBarStyle: UIViewController? {
  return topViewController
}

override open var childViewControllerForStatusBarHidden: UIViewController? {
  return topViewController
}

И затем в контроллере представления, который должен диктовать стиль строки состояния:

override var preferredStatusBarStyle: UIStatusBarStyle {
   return .lightContent
}

Ответ 10

В дополнение к ответу Серенна, если вы представляете контроллер представления с помощью modalPresentationStyle (например, .overCurrentContext), вы должны также вызвать это на недавно представленном контроллере представления:

presentedViewController.modalPresentationCapturesStatusBarAppearance = true

Не забудьте также переопределить preferredStatusBarStyle в представленном контроллере представления.

Ответ 11

Swift 4.2 и выше

Как уже упоминалось в выбранном ответе, основная причина заключается в проверке объекта контроллера корневого представления окна.

Возможные случаи вашей структуры потока

  • Пользовательский объект UIViewController является корневым контроллером окна

    Контроллер корневого представления вашего окна - это объект UIViewController, который дополнительно добавляет или удаляет контроллер навигации или tabController в зависимости от потока вашего приложения.

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

  • Объект TabBarController является корневым контроллером окна

    Это поток, в котором контроллер корневого представления окна является tabBarController, возможно, каждая вкладка дополнительно содержит контроллер навигации.

  • Объект NavigationController является корневым контроллером окна

    Это поток, в котором контроллером корневого представления окна является навигационный контроллер.

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

Используйте следующие расширения, он обрабатывает все вышеперечисленные сценарии -

extension UITabBarController {
    open override var childForStatusBarStyle: UIViewController? {
        return selectedViewController?.childForStatusBarStyle ?? selectedViewController
    }
}

extension UINavigationController {
    open override var childForStatusBarStyle: UIViewController? {
        return topViewController?.childForStatusBarStyle ?? topViewController
    }
}

extension AppRootViewController {
    open override var preferredStatusBarStyle: UIStatusBarStyle {
        return children.first { $0.childForStatusBarStyle != nil }?.childForStatusBarStyle?.preferredStatusBarStyle ?? .default
    }
}
  • Вам не нужно UIViewControllerBasedStatusBarAppearance ключ в info.plist, как это верно по умолчанию

Точки для рассмотрения для более сложных потоков

  • Если вы представляете новый поток модально, он отключается от существующего потока стилей строки состояния. Итак, предположим, что вы представляете NewFlowUIViewController а затем добавляете новый контроллер навигации или tabBar в NewFlowUIViewController, затем добавляете расширение NewFlowUIViewController а также для управления стилем строки состояния дальнейшего просмотра контроллера.

  • Если вы устанавливаете modalPresentationStyle, отличный от fullScreen при представлении модально, вы должны установить для modalPresentationCapturesStatusBarAppearance значение true, чтобы представленный контроллер представления должен был получать управление внешним видом строки состояния.

Ответ 12

Если ваш viewController находится под UINavigationController.

Подкласс UINavigationController и добавьте

override var preferredStatusBarStyle: UIStatusBarStyle {
    return topViewController?.preferredStatusBarStyle ?? .default
}

Будет вызван ViewController preferredStatusBarStyle.

Ответ 13

UIStatusBarStyle в iOS 7

Строка состояния в iOS 7 прозрачна, вид за ней показывает.

Стиль строки состояния ссылается на внешний вид его содержимого. В iOS 7 содержимое строки состояния либо темное (UIStatusBarStyleDefault), либо светлое (UIStatusBarStyleLightContent). Оба UIStatusBarStyleBlackTranslucent и UIStatusBarStyleBlackOpaque устарели в iOS 7.0. Вместо этого используйте UIStatusBarStyleLightContent.

Как изменить UIStatusBarStyle

Если ниже строки состояния находится панель навигации, стиль строки состояния будет настроен в соответствии со стилем панели навигации (UINavigationBar.barStyle):

В частности, если стиль панели навигации - UIBarStyleDefault, стиль строки состояния будет UIStatusBarStyleDefault; если стиль панели навигации UIBarStyleBlack, стиль строки состояния будет UIStatusBarStyleLightContent.

Если на панели состояния нет панели навигации, стиль строки состояния можно контролировать и изменять отдельным контроллером представления во время запуска приложения.

- [UIViewController preferredStatusBarStyle] - это новый метод, добавленный в iOS 7. Его можно переопределить, чтобы вернуть предпочтительный стиль строки состояния:

- (UIStatusBarStyle)preferredStatusBarStyle
  {
      return UIStatusBarStyleLightContent;
  }

Если стиль строки состояния должен управляться контроллером дочернего представления вместо self, переопределите -[UIViewController childViewControllerForStatusBarStyle], чтобы вернуть этот дочерний контроллер.

Если вы предпочитаете отказаться от этого поведения и установить стиль строки состояния с помощью метода -[UIApplication statusBarStyle], добавьте ключ UIViewControllerBasedStatusBarAppearance в файл apps Info.plist и присвойте ему значение NO.

Ответ 14

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

extension UINavigationController {

    override open func viewDidLoad() {
        super.viewDidLoad()

        self.navigationBar.barStyle = UIBarStyle.black
    }

}

Ответ 15

В Swift для любого типа UIViewController:

В вашем AppDelegate установите:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    window!.rootViewController = myRootController
    return true
}

myRootController может быть любым типом UIViewController, например. UITabBarController или UINavigationController.

Затем переопределите этот корневой контроллер следующим образом:

class RootController: UIViewController {
    override func preferredStatusBarStyle() -> UIStatusBarStyle {
        return .LightContent
    }
}

Это изменит внешний вид строки состояния в вашем приложении, потому что корневой контроллер несет полную ответственность за появление строки состояния.

Не забудьте установить для свойства View controller-based status bar appearance значение YES в вашем Info.plist, чтобы сделать эту работу (которая по умолчанию).

Ответ 16

Swift 3 Решение iOS 10:

override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent
 }

Ответ 17

Большинство ответов не включают в себя хорошую реализацию метода childViewControllerForStatusBarStyle для UINavigationController. Согласно моему опыту, вы должны обрабатывать такие случаи, когда контроллер прозрачного представления представлен поверх контроллера навигации. В этих случаях вы должны передать управление вашему модальному контроллеру (visibleViewController), но не тогда, когда он исчезнет.

override var childViewControllerForStatusBarStyle: UIViewController? {
  var childViewController = visibleViewController
  if let controller = childViewController, controller.isBeingDismissed {
    childViewController = topViewController
  }
  return childViewController?.childViewControllerForStatusBarStyle ?? childViewController
}

Ответ 18

Здесь мой метод для решения этого вопроса.

Определите протокол с именем AGViewControllerAppearance.

AGViewControllerAppearance.h

#import <Foundation/Foundation.h>

@protocol AGViewControllerAppearance <NSObject>

@optional

- (BOOL)showsStatusBar;
- (BOOL)animatesStatusBarVisibility;
- (UIStatusBarStyle)preferredStatusBarStyle;
- (UIStatusBarAnimation)prefferedStatusBarAnimation;

@end

Определите категорию в UIViewController, названную Обновить.

UIViewController + Upgrade.h

#import <UIKit/UIKit.h>

@interface UIViewController (Upgrade)

//
//  Replacements
//

- (void)upgradedViewWillAppear:(BOOL)animated;

@end

UIViewController + Upgrade.m

#import "UIViewController+Upgrade.h"

#import <objc/runtime.h>

#import "AGViewControllerAppearance.h" // This is the appearance protocol

@implementation UIViewController (Upgrade)

+ (void)load
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wselector"
    Method viewWillAppear = class_getInstanceMethod(self, @selector(viewWillAppear:));
#pragma clang diagnostic pop
    Method upgradedViewWillAppear = class_getInstanceMethod(self, @selector(upgradedViewWillAppear:));
    method_exchangeImplementations(viewWillAppear, upgradedViewWillAppear);
}

#pragma mark - Implementation

- (void)upgradedViewWillAppear:(BOOL)animated
{
    //
    //  Call the original message (it may be a little confusing that we're
    //  calling the 'same' method, but we're actually calling the original one :) )
    //

    [self upgradedViewWillAppear:animated];

    //
    //  Implementation
    //

    if ([self conformsToProtocol:@protocol(AGViewControllerAppearance)])
    {
        UIViewController <AGViewControllerAppearance> *viewControllerConformingToAppearance =
        (UIViewController <AGViewControllerAppearance> *)self;

        //
        //  Status bar
        //

        if ([viewControllerConformingToAppearance respondsToSelector:@selector(preferredStatusBarStyle)])
        {
            BOOL shouldAnimate = YES;

            if ([viewControllerConformingToAppearance respondsToSelector:@selector(animatesStatusBarVisibility)])
            {
                shouldAnimate = [viewControllerConformingToAppearance animatesStatusBarVisibility];
            }

            [[UIApplication sharedApplication] setStatusBarStyle:[viewControllerConformingToAppearance preferredStatusBarStyle]
                                                        animated:shouldAnimate];
        }

        if ([viewControllerConformingToAppearance respondsToSelector:@selector(showsStatusBar)])
        {
            UIStatusBarAnimation animation = UIStatusBarAnimationSlide;

            if ([viewControllerConformingToAppearance respondsToSelector:@selector(prefferedStatusBarAnimation)])
            {
                animation = [viewControllerConformingToAppearance prefferedStatusBarAnimation];
            }

            [[UIApplication sharedApplication] setStatusBarHidden:(! [viewControllerConformingToAppearance showsStatusBar])
                                                    withAnimation:animation];
        }
    }
}

@end

Теперь пришло время сказать, что вы контроллер представления реализует протокол AGViewControllerAppearance.

Пример:

@interface XYSampleViewController () <AGViewControllerAppearance>

... the rest of the interface

@end

Конечно, вы можете реализовать остальные методы (showStatusBar, animatesStatusBarVisibility, prefferedStatusBarAnimation) из протокола и UIViewController + Обновление сделает правильный настройка на основе значений, предоставленных ими.

Ответ 19

Если кто-то столкнется с этой проблемой с UISearchController. Просто создайте новый подкласс UISearchController, а затем добавьте следующий код в этот класс:

override func preferredStatusBarStyle() -> UIStatusBarStyle {
    return .LightContent
}

Ответ 20

Обратите внимание, что при использовании решения self.navigationController.navigationBar.barStyle = UIBarStyleBlack;

обязательно перейдите к своему plist и установите "Просмотреть строку состояния на основе контроллера" на YES. Если его нет, это не сработает.

Ответ 21

В моем случае я случайно представил View/Navigation Controller как UIModalPresentationStyle.overFullScreen, что приводит к тому, что preferredStatusBarStyle не вызывается. После переключения обратно на UIModalPresentationStyle.fullScreen все работает.

Ответ 22

Контроллер NavigationController или TabBarController - это те, которые должны обеспечивать стиль. Вот как я решил: fooobar.com/info/22493/...