Я следил за этой веткой, чтобы переопределить -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]
navigationBarHiddenset 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. Таким образом, чтобы вызвать preferredStatusBarStyleUINavigationController, вам нужно изменить его 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