Я следил за этой веткой, чтобы переопределить -preferredStatusBarStyle
, но она не называется. Есть ли варианты, которые я могу изменить, чтобы включить его? (Я использую XIB в моем проекте.)
Я следил за этой веткой, чтобы переопределить -preferredStatusBarStyle
, но она не называется. Есть ли варианты, которые я могу изменить, чтобы включить его? (Я использую XIB в моем проекте.)
Ответы:
У меня была та же проблема, и я понял, что это происходит, потому что я не настраивал контроллер корневого представления в моем окне приложения.
Объект, 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;
}
Кроме того, вы можете вызвать один из следующих методов, в зависимости от ситуации, в каждом из ваших контроллеров представления, в зависимости от его цвета фона, вместо необходимости использовать setNeedsStatusBarAppearanceUpdate
:
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
или
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];
Обратите внимание, что вам также нужно установить UIViewControllerBasedStatusBarAppearance
значение NO
в файле plist, если вы используете этот метод.
setNeedsStatusBarAppearanceUpdate
- мои подозрения подтвердились, когда я сделал это изменение.
Для тех, кто использует UINavigationController:
UINavigationController
Не передает на preferredStatusBarStyle
звонки на его взгляд ребенка контроллеров. Вместо этого он управляет своим собственным состоянием - как и положено, он рисует в верхней части экрана, где находится строка состояния, и поэтому должен нести за него ответственность. Поэтому реализация preferredStatusBarStyle
в ваших VC в контроллере nav ничего не даст - они никогда не будут вызываться.
Хитрость в том, что UINavigationController
использует, чтобы решить, за что возвращаться UIStatusBarStyleDefault
или UIStatusBarStyleLightContent
. Это основано на его UINavigationBar.barStyle
. По умолчанию ( UIBarStyleDefault
) отображается UIStatusBarStyleDefault
строка состояния темного переднего плана . И UIBarStyleBlack
даст UIStatusBarStyleLightContent
статус бар.
TL; DR:
Если вы хотите UIStatusBarStyleLightContent
на UINavigationController
использование:
self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
preferredStatusBarStyle
на самом деле вызов будет вызываться на дочернем контроллере представления, если вы спрячете панель навигации (установлена navigationBarHidden
в YES
), точно так, как это необходимо.
[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack]
navigationBarHidden
set to YES
фактически preferredStatusBarStyle
вызвал, и предупреждение тем, кто может наткнуться на это: он работает с navigationBarHidden
, но не с navigationBar.hidden
!
Поэтому я фактически добавил категорию в UINavigationController, но использовал методы:
-(UIViewController *)childViewControllerForStatusBarStyle;
-(UIViewController *)childViewControllerForStatusBarHidden;
и они возвращали текущий видимый UIViewController. Это позволяет текущему видимому контроллеру представления установить свой собственный предпочтительный стиль / видимость.
Вот полный фрагмент кода для этого:
В Свифте:
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:
В Свифте
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 это НЕ есть «View контроллера на основе строки состояния внешнего вида» набор для NO. Либо удалите эту строку, либо задайте для нее значение ДА (что, по моему мнению, является значением по умолчанию для iOS 7?)
return self.topViewController;
работает для меня, но return self.visibleViewController;
- нет
super
этот метод, и вы действительно хотите изменить поведение всех контроллеров этого типа
Для тех, кто все еще борется с этим, это простое расширение в swift должно решить проблему для вас.
extension UINavigationController {
override open var childForStatusBarStyle: UIViewController? {
return self.topViewController
}
}
Мое приложение используется всего три: 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
}
}
Ответ Тайсона является правильным для изменения цвета строки состояния на белый в 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;
}
На UINavigationController preferredStatusBarStyle
не вызывается, потому что его topViewController
предпочитают self
. Таким образом, чтобы вызвать preferredStatusBarStyle
UINavigationController, вам нужно изменить его childViewControllerForStatusBarStyle
.
Переопределите свой UINavigationController в своем классе:
class MyRootNavigationController: UINavigationController {
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override var childViewControllerForStatusBarStyle: UIViewController? {
return nil
}
}
Чтобы сделать это для всех UINavigationController, вы можете переопределить в расширении (предупреждение: это влияет на UIDocumentPickerViewController, UIImagePickerController и т. Д.), Но вам, вероятно, не следует делать это в соответствии с документацией Swift :
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
open override var childViewControllerForStatusBarStyle: UIViewController? {
return nil
}
}
В дополнение к ответу Серенна, если вы представляете контроллер представления с modalPresentationStyle
(например .overCurrentContext
), вы должны также вызвать это на недавно представленном контроллере представления:
presentedViewController.modalPresentationCapturesStatusBarAppearance = true
Не забудьте также переопределить представленный preferredStatusBarStyle
в представлении контроллер.
Дополнение к ответу Бегемота: если вы используете 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
Это решение, вероятно, лучше, чем переключение на поведение, которое скоро станет устаревшим.
preferredStatusBarStyle
и выполняет специфическую логику UINavigationController. Прямо сейчас эта логика основана на, navigationBar.barStyle
но я вижу дополнительные проверки, добавляемые (например, UISearchDisplayController
чтобы скрыть режим navbar). Переопределяя логику по умолчанию, вы теряете всю эту функциональность и оставляете себя открытым для раздражающих моментов 'wtf' в будущем. Смотрите мой ответ выше для правильного способа сделать это, все еще поддерживая встроенное поведение контроллера навигации.
Как уже упоминалось в выбранном ответе , основная причина заключается в проверке объекта контроллера корневого представления окна.
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, чтобы представленный контроллер представления должен был получить контроль внешнего вида строки состояния.
UINavigationController
это подкласс UIViewController
(который знал 🙃)!
Следовательно, при представлении контроллеров представления, встроенных в контроллеры навигации, вы на самом деле не представляете встроенные контроллеры представления; Вы представляете навигационные контроллеры! UINavigationController
, как подкласс UIViewController
, наследует preferredStatusBarStyle
и childForStatusBarStyle
, который вы можете установить по своему усмотрению.
Любой из следующих методов должен работать:
info.plist
, добавьте следующее свойство:
UIUserInterfaceStyle
(он же «Стиль интерфейса пользователя»)Переопределить preferredStatusBarStyle
внутриUINavigationController
preferredStatusBarStyle
( doc ) - предпочтительный стиль строки состояния для контроллера представленияПодкласс или расширение UINavigationController
class MyNavigationController: UINavigationController {
override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
}
ИЛИ
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
}
Переопределить childForStatusBarStyle
внутриUINavigationController
childForStatusBarStyle
( doc ) - вызывается, когда системе требуется контроллер представления, чтобы использовать для определения стиля строки состояния«Если ваш контроллер представления контейнера выводит свой стиль строки состояния из одного из его дочерних контроллеров представления, [переопределите это свойство] и верните этот дочерний контроллер представления. Если вы вернете nil или не переопределите этот метод, используется стиль строки состояния для self . Если возвращаемое значение из этого метода изменяется, вызовите метод setNeedsStatusBarAppearanceUpdate (). "
Подкласс или расширение UINavigationController
class MyNavigationController: UINavigationController {
override var childForStatusBarStyle: UIViewController? {
topViewController
}
}
ИЛИ
extension UINavigationController {
open override var childForStatusBarStyle: UIViewController? {
topViewController
}
}
Вы можете вернуть любой контроллер представления, который вы хотели бы выше. Я рекомендую одно из следующего:
topViewController
(of UINavigationController
) ( doc ) - контроллер представления в верхней части стека навигацииvisibleViewController
(of UINavigationController
) ( doc ) - Контроллер представления, связанный с текущим видимым представлением в интерфейсе навигации (подсказка: это может включать в себя «контроллер представления, который был представлен модально поверх самого контроллера навигации»)Примечание: если вы решили подкласс UINavigationController
, не забудьте применить этот класс к контроллерам навигации с помощью инспектора идентификации в IB.
PS Мой код использует синтаксис Swift 5.1 😎
@ serenn в ответ выше еще один большой для случая 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
}
Если ваш viewController находится под UINavigationController.
Подкласс UINavigationController и добавить
override var preferredStatusBarStyle: UIStatusBarStyle {
return topViewController?.preferredStatusBarStyle ?? .default
}
ViewController preferredStatusBarStyle
будет вызван.
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;
}
Если стиль строки состояния должен контролироваться дочерним контроллером представления вместо собственного, переопределите -[UIViewController childViewControllerForStatusBarStyle]
чтобы вернуть этот дочерний контроллер представления.
Если вы предпочитаете отказаться от этого поведения и установить стиль строки состояния с помощью -[UIApplication statusBarStyle]
метода, добавьте UIViewControllerBasedStatusBarAppearance
ключ в Info.plist
файл приложения и присвойте ему значение NO.
Если кто-либо использует навигационный контроллер и хочет, чтобы все его навигационные контроллеры имели черный стиль, вы можете написать расширение для UINavigationController, подобное этому, в Swift 3, и оно будет применяться ко всем навигационным контроллерам (вместо назначения его одному контроллеру в время).
extension UINavigationController {
override open func viewDidLoad() {
super.viewDidLoad()
self.navigationBar.barStyle = UIBarStyle.black
}
}
В 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
чтобы это работало (по умолчанию).
Большинство ответов не включают в себя хорошую реализацию childViewControllerForStatusBarStyle
метода для UINavigationController
. Согласно моему опыту, вы должны обрабатывать такие случаи, когда контроллер прозрачного представления представлен поверх контроллера навигации. В этих случаях вы должны передать управление вашему модальному контроллеру ( visibleViewController
), но не тогда, когда он исчезает.
override var childViewControllerForStatusBarStyle: UIViewController? {
var childViewController = visibleViewController
if let controller = childViewController, controller.isBeingDismissed {
childViewController = topViewController
}
return childViewController?.childViewControllerForStatusBarStyle ?? childViewController
}
Что касается iOS 13.4, preferredStatusBarStyle
метод в UINavigationController
категории не будет вызываться, swizzling, кажется, является единственным вариантом без необходимости использования подкласса.
Пример:
Заголовок категории:
@interface UINavigationController (StatusBarStyle)
+ (void)setUseLightStatusBarStyle;
@end
Реализация:
#import "UINavigationController+StatusBarStyle.h"
#import <objc/runtime.h>
@implementation UINavigationController (StatusBarStyle)
void (^swizzle)(Class, SEL, SEL) = ^(Class c, SEL orig, SEL new){
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
};
+ (void)setUseLightStatusBarStyle {
swizzle(self.class, @selector(preferredStatusBarStyle), @selector(_light_preferredStatusBarStyle));
}
- (UIStatusBarStyle)_light_preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}
@end
Использование в AppDelegate.h:
#import "UINavigationController+StatusBarStyle.h"
[UINavigationController setUseLightStatusBarStyle];
Вот мой метод решения этого.
Определите протокол с именем AGViewControllerAppearance .
AGViewControllerAppearance.h
#import <Foundation/Foundation.h>
@protocol AGViewControllerAppearance <NSObject>
@optional
- (BOOL)showsStatusBar;
- (BOOL)animatesStatusBarVisibility;
- (UIStatusBarStyle)preferredStatusBarStyle;
- (UIStatusBarAnimation)prefferedStatusBarAnimation;
@end
Определите категорию в UIViewController, которая называется Upgrade .
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
Конечно, вы можете реализовать остальные методы ( showsStatusBar , animatesStatusBarVisibility , prefferedStatusBarAnimation ) из протокола и UIViewController + Обновление будет делать правильную настройку на основе значений , предоставляемых ими.
Обратите внимание, что при использовании self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
решения
Обязательно зайдите в свой список и установите «Просмотр внешнего вида строки состояния на основе контроллера» на ДА. Если его нет, он не будет работать.
Начиная с Xcode 11.4, переопределяя preferredStatusBarStyle
свойства в расширении UINavigationController больше не работает, так как оно не будет вызываться.
Установка barStyle
из navigationBar
к .black
работам , действительно , но это добавит нежелательные побочные эффекты , если добавить подвиды в Панель навигации , которая может иметь разный внешний вид для светлого и темного режима. Потому что, установив в barStyle
черный цвет, userInterfaceStyle
представление, встроенное в навигационную панель, будет всегда иметь userInterfaceStyle.dark
независимо userInterfaceStyle
от приложения.
Правильное решение, которое я придумаю, это добавление подкласса UINavigationController
и переопределение preferredStatusBarStyle
там. Если вы затем используете этот пользовательский UINavigationController для всех ваших просмотров, вы будете на стороне сохранения.
NavigationController или TabBarController - те, которые должны обеспечить стиль. Вот как я решил: https://stackoverflow.com/a/39072526/242769