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

257

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

trgoofi
источник
В каком контексте это не называется: симулятор? на устройстве?
bneely
@ оба они оба.
trgoofi
Вы используете симулятор iOS 7, устройство iOS 7, и ваш базовый SDK 7.0?
bneely
@bneely iOS SDK 7.0 отображается под названием моего проекта, значит ли это, что мой базовый SDK 7.0?
trgoofi
В настройках сборки «Базовый SDK» - это то место, где установлено значение. Похоже, ваш проект настроен на 7.0.
bneely

Ответы:

117

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

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

Объект, 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, если вы используете этот метод.

AbdullahC
источник
2
У меня такая же проблема, как и у вас, не настройка корневого контроллера просмотра. Как, черт возьми, ты это нашел?
trgoofi
1
Я подозревал, что что-то в структуре не получало уведомление setNeedsStatusBarAppearanceUpdate- мои подозрения подтвердились, когда я сделал это изменение.
Абдулла,
2
Связанной проблемой, которую я обнаружил в приложении, был контроллер представления с полноэкранным дочерним контроллером представления, который не переопределял childViewControllerForStatusBarStyle и childViewControllerForStatusBarHidden для возврата этого дочернего контроллера представления. Если у вас есть собственная иерархия контроллеров представления, вам необходимо предоставить эти методы, чтобы сообщить системе, какой контроллер представления следует использовать для определения стиля строки состояния.
Джон Штейнмец
установка rootviewcontroller ничего не меняет. Вы должны работать с комментарием Джона. И будьте осторожны при вызове setneedsstatusbarappearanceUpdate. Вы должны позвонить от родителя на работу.
doozMen
1
@ Гиппо ты гений !! Как вы узнали, что это из-за не установки rootviewcontroller?
ViruMax
1019

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

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

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

TL; DR:

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

self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
Тайсон
источник
59
Ницца! Обратите внимание, что preferredStatusBarStyleна самом деле вызов будет вызываться на дочернем контроллере представления, если вы спрячете панель навигации (установлена navigationBarHiddenв YES), точно так, как это необходимо.
Патрик Пийнаппель
25
Спасибо за этот ответ. Если вы хотите установить barStyle для всех ваших навигационных панелей, позвоните[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack]
Thomas Desert
15
Идеальный ответ. Ни один из других ответов по SO не принял во внимание UINavigationController. 2 часа ударился головой о клавиатуру.
Райан Олфорд
10
Престижность @Patrick за указание того, что navigationBarHiddenset to YESфактически preferredStatusBarStyleвызвал, и предупреждение тем, кто может наткнуться на это: он работает с navigationBarHidden, но не с navigationBar.hidden!
Jcaron
4
должно быть очевидным, но вам также нужно, чтобы «Просмотр внешнего вида строки состояния на основе контроллера» был установлен в YES в Info.plist, чтобы это работало.
Код Baller
99

Поэтому я фактически добавил категорию в 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?)

serenn
источник
Похоже, return self.topViewController;работает для меня, но return self.visibleViewController;- нет
k06a
visibleViewController может вернуть текущий представленный модальный контроллер, когда вы его отклоните. Который облом Используйте topViewController.
Бен Синклер
1
@ d.lebedev хорошо, но я не думаю, что какие-либо из этих проблем применимы здесь. Вам не нужно вызывать superэтот метод, и вы действительно хотите изменить поведение всех контроллеров этого типа
изд. '
1
это не работает для меня на iOS 9.3. Я думаю, это проблема: эта проблема имеет особое значение, потому что многие классы Какао реализованы с использованием категорий. Определенный каркасом метод, который вы пытаетесь переопределить, сам может быть реализован в категории, и поэтому реализация, которая имеет приоритет, не определена.
Викингосегунд
2
Это неправильно и ломается в iOS 13.4. Поскольку расширение целевых классов C в Swift реализовано через категории Objective C. Переопределение методов в категориях Objective C не рекомендуется и может привести к поломке. См. Stackoverflow.com/a/38274660/2438634
Марк Этчеверри
79

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

extension UINavigationController {
    override open var childForStatusBarStyle: UIViewController? {
        return self.topViewController
    }
}
Алекс Браун
источник
10
Вы, сэр, заслуживаете медали.
Никанс
2
Большое спасибо, мужчина. Я возвращал visibleViewController, но безуспешно.
Фабио Салата
1
Это золото. У меня есть контроллер навигации, встроенный в панель вкладок, и я просто выбросил это в файл, и теперь я могу изменить внешний вид строки состояния в любом месте, где захочу.
Вахид Амири
2
Это неправильно и ломается в iOS 13.4. Поскольку расширение целевых классов C в Swift реализовано через категории Objective C. Переопределение методов в категориях Objective C не рекомендуется и может привести к поломке. См. Stackoverflow.com/a/38274660/2438634
Марк Этчеверри
1
@MarcEtcheverry этот конкретный случай не ошибся. Дело в том, что подклассы других объектов / протоколов, таких как UINavigationController, ранее не реализовывали их, чтобы конфликтовать при динамической диспетчеризации. В реальных подклассах не было ни значений по умолчанию, ни реализаций, поэтому это был самый чистый способ реализовать это в приложении без создания ненужной зависимости (точки). К сожалению, 13.4, похоже, изменило это поведение. Я предполагаю, что за кулисами у них есть проверка или реализация, которой не было уже много лет .........
TheCodingArt
20

Мое приложение используется всего три: 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
    }
}
Луис
источник
1
Это единственное решение, которое работает. Все ответы на SO указывают на стандартное решение, которое не будет работать для любого приложения с NavigationControllers. Спасибо!!!
Хоуман
Использование расширений для переопределения просто неправильно. Это не безопасно. Есть несколько простых решений. Вместо этого используйте подкласс.
Султан
2
Это неправильно и ломается в iOS 13.4. Поскольку расширение целевых классов C в Swift реализовано через категории Objective C. Переопределение методов в категориях Objective C не рекомендуется и может привести к поломке. См. Stackoverflow.com/a/38274660/2438634
Марк Этчеверри
1
@MarcEtcheverry этот конкретный случай не ошибся. Дело в том, что подклассы других объектов / протоколов, таких как UINavigationController, ранее не реализовывали их, чтобы конфликтовать при динамической диспетчеризации. В реальных подклассах не было ни значений по умолчанию, ни реализаций, поэтому это был самый чистый способ реализовать это в приложении без создания ненужной зависимости (точки). К сожалению, 13.4, похоже, изменило это поведение. Я предполагаю, что за кулисами у них сейчас есть проверка или реализация, которой не было годами .........
TheCodingArt
15

Ответ Тайсона является правильным для изменения цвета строки состояния на белый в 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;
}
Йогеш Сутар
источник
14

На 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
    }
}
Кер
источник
11

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

presentedViewController.modalPresentationCapturesStatusBarAppearance = true

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

Frin
источник
9

Дополнение к ответу Бегемота: если вы используете 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

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

Артем Абрамов
источник
Не делайте этого, это работает на данный момент, но может нарушить будущее поведение. Просто измените стиль navBar - смотрите мой ответ stackoverflow.com/a/19513714/505457
Тайсон
2
Вы должны использовать подкласс, а не категорию.
shuiyouren
2Tyson: Почему это нарушит будущее поведение? ferenceStatusBarStyle: предпочтительный метод Apple для настройки стиля строки состояния.
Артем Абрамов
2shuiyouren: Зачем мне увеличивать сложность за счет создания подклассов, если я могу просто использовать категорию и включать ее в каждом месте, где я хочу? Во всяком случае, это вопрос архитектуры, а не реализации.
Артем Абрамов
2
@ArtemAbramov Поскольку UINavigationController уже реализует preferredStatusBarStyleи выполняет специфическую логику UINavigationController. Прямо сейчас эта логика основана на, navigationBar.barStyleно я вижу дополнительные проверки, добавляемые (например, UISearchDisplayControllerчтобы скрыть режим navbar). Переопределяя логику по умолчанию, вы теряете всю эту функциональность и оставляете себя открытым для раздражающих моментов 'wtf' в будущем. Смотрите мой ответ выше для правильного способа сделать это, все еще поддерживая встроенное поведение контроллера навигации.
Тайсон
9

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, чтобы представленный контроллер представления должен был получить контроль внешнего вида строки состояния.

Абхиманью Джиндал
источник
Отличный ответ!
Амин Бенариеб
3
Это неправильно и ломается в iOS 13.4. Поскольку расширение целевых классов C в Swift реализовано через категории Objective C. Переопределение методов в категориях Objective C не рекомендуется и может привести к поломке. См. Stackoverflow.com/a/38274660/2438634
Марк Этчеверри
@MarcEtcheverry этот конкретный случай не ошибся. Дело в том, что подклассы других объектов / протоколов, таких как UINavigationController, ранее не реализовывали их, чтобы конфликтовать при динамической диспетчеризации. В реальных подклассах не было ни значений по умолчанию, ни реализаций, поэтому это был самый чистый способ реализовать это в приложении без создания ненужной зависимости (точки). К сожалению, 13.4, похоже, изменило это поведение. Я предполагаю, что за кулисами у них сейчас есть проверка или реализация, которой не было годами .........
TheCodingArt
8

Решение для iOS 13

UINavigationControllerэто подкласс UIViewController(который знал 🙃)!

Следовательно, при представлении контроллеров представления, встроенных в контроллеры навигации, вы на самом деле не представляете встроенные контроллеры представления; Вы представляете навигационные контроллеры! UINavigationController, как подкласс UIViewController, наследует preferredStatusBarStyleи childForStatusBarStyle, который вы можете установить по своему усмотрению.

Любой из следующих методов должен работать:

  1. Отказаться от темного режима полностью
    • В вашем info.plist, добавьте следующее свойство:
      • Ключ - UIUserInterfaceStyle(он же «Стиль интерфейса пользователя»)
      • Значение - Свет
  2. Переопределить preferredStatusBarStyleвнутриUINavigationController

    • preferredStatusBarStyle( doc ) - предпочтительный стиль строки состояния для контроллера представления
    • Подкласс или расширение UINavigationController

      class MyNavigationController: UINavigationController {
          override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }

      ИЛИ

      extension UINavigationController {
          open override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }
  3. Переопределить childForStatusBarStyleвнутриUINavigationController

    • childForStatusBarStyle( doc ) - вызывается, когда системе требуется контроллер представления, чтобы использовать для определения стиля строки состояния
    • Согласно документации Apple,

      «Если ваш контроллер представления контейнера выводит свой стиль строки состояния из одного из его дочерних контроллеров представления, [переопределите это свойство] и верните этот дочерний контроллер представления. Если вы вернете nil или не переопределите этот метод, используется стиль строки состояния для self . Если возвращаемое значение из этого метода изменяется, вызовите метод setNeedsStatusBarAppearanceUpdate (). "

    • Другими словами, если вы не реализуете решение 3 здесь, система обратится к решению 2 выше.
    • Подкласс или расширение 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 😎

Эндрю Кирна
источник
Моя строка состояния становится черной после поворота экрана. Есть идеи почему? Это происходит только на симуляторе iPad Pro.
Педро Пауло Аморим
@PedroPauloAmorim, можете ли вы предоставить больше информации? Как представлен контроллер вида сверху (модальный, полноэкранный, шоу)? Он вложен в контроллер навигации? Текст становится черным или фон тоже? Что вы пытаетесь достичь?
Андрей Кирна
Я установил светлую строку состояния во всем приложении. Он получает свет в два поворота, в третьем он становится темным и никогда не возвращается к свету, даже заставляя его перерисовать. Это происходит на симуляторе iPad Pro. Представления представляются в полноэкранном режиме, и они не вложены в контроллер навигации. Только текст становится темным.
Педро Пауло Аморим
Как вы настраиваете световую строку состояния в первую очередь?
Андрей Кирна
3
Ваше переопределение через расширение не является настоящим переопределением. Это небезопасное неправильное использование языка. Это может сломаться очень легко.
Султан
7

@ 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
}
Джон Стрикер
источник
2
Это неправильно и ломается в iOS 13.4. Поскольку расширение целевых классов C в Swift реализовано через категории Objective C. Переопределение методов в категориях Objective C не рекомендуется и может привести к поломке. См. Stackoverflow.com/a/38274660/2438634
Марк Этчеверри
@MarcEtcheverry этот конкретный случай не ошибся. Дело в том, что подклассы других объектов / протоколов, таких как UINavigationController, ранее не реализовывали их, чтобы конфликтовать при динамической диспетчеризации. В реальных подклассах не было ни значений по умолчанию, ни реализаций, поэтому это был самый чистый способ реализовать это в приложении без создания ненужной зависимости (точки). К сожалению, 13.4, похоже, изменило это поведение. Я предполагаю, что за кулисами у них сейчас есть проверка или реализация, которой не было годами .........
TheCodingArt
6

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

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

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

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

PowHu
источник
1
см. этот пост: medium.com/@_riteshhh/swift-snippet-1-c2d485028185#.yg96zi7ah
Гарри Блум
4

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.

oscarr
источник
3

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

extension UINavigationController {

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

        self.navigationBar.barStyle = UIBarStyle.black
    }

}
Бенджамин Лоури
источник
1
Но что если у меня навигационная панель скрыта?
Славчо
1
Потому что мне нужно, чтобы навигация была скрыта, а строка состояния была видна.
Славчо
1

В 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чтобы это работало (по умолчанию).

Damnum
источник
@ Как это в swift3?
самолет
1

Решение Swift 3 для iOS 10:

override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent
 }
Statik
источник
1

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

override var childViewControllerForStatusBarStyle: UIViewController? {
  var childViewController = visibleViewController
  if let controller = childViewController, controller.isBeingDismissed {
    childViewController = topViewController
  }
  return childViewController?.childViewControllerForStatusBarStyle ?? childViewController
}
Тимур Берникович
источник
1

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

Casey
источник
1

Что касается 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];
Кевин Флахсманн
источник
0

Вот мой метод решения этого.

Определите протокол с именем 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 + Обновление будет делать правильную настройку на основе значений , предоставляемых ими.

arturgrigor
источник
0

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

override func preferredStatusBarStyle() -> UIStatusBarStyle {
    return .LightContent
}
Тай Ле
источник
0

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

Обязательно зайдите в свой список и установите «Просмотр внешнего вида строки состояния на основе контроллера» на ДА. Если его нет, он не будет работать.

Ричард Гарфилд
источник
Установка UIViewControllerBasedStatusBarAppearance в YES в plist проекта имела для меня значение. Я забыл об этом.
Фило
0

Начиная с Xcode 11.4, переопределяя preferredStatusBarStyle свойства в расширении UINavigationController больше не работает, так как оно не будет вызываться.

Установка barStyleиз navigationBarк .blackработам , действительно , но это добавит нежелательные побочные эффекты , если добавить подвиды в Панель навигации , которая может иметь разный внешний вид для светлого и темного режима. Потому что, установив в barStyleчерный цвет, userInterfaceStyleпредставление, встроенное в навигационную панель, будет всегда иметь userInterfaceStyle.darkнезависимо userInterfaceStyleот приложения.

Правильное решение, которое я придумаю, это добавление подкласса UINavigationControllerи переопределение preferredStatusBarStyleтам. Если вы затем используете этот пользовательский UINavigationController для всех ваших просмотров, вы будете на стороне сохранения.

PatrickDotStar
источник
-1

NavigationController или TabBarController - те, которые должны обеспечить стиль. Вот как я решил: https://stackoverflow.com/a/39072526/242769

aryaxt
источник
Если вы считаете, что это дубликат другого вопроса, пожалуйста, закройте его как дубликат