Я стежив за цією темою, щоб переоцінити -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
у своїх ВК в навигаційному контролері нічого не зробить - вони ніколи не будуть викликатися.
Хитрість полягає в тому, що 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
сет 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
}
Нарешті, переконайтеся, що у вашому списку програм НЕ встановлено значення "Перегляд вигляду рядка стану на основі контролера". Або видаліть цей рядок або встановіть його ТАК (що, на мою думку, зараз для iOS 7?)
return self.topViewController;
твори для мене, але return self.visibleViewController;
- не
super
в цьому методі, і ви дійсно хочете змінити поведінку всіх контролерів цього типу
Для тих, хто все ще бореться з цим, це просте розширення у швидкому режимі повинно вирішити проблему для вас.
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
}
}
Редагування: оновлення змін 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
}
}
На додаток до відповіді serenn, якщо ви представляєте контролер подання з 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
перехід до режиму навігації). Перевіряючи логіку за замовчуванням, ви втрачаєте всю цю функціональність і залишаєте себе відкритим для дратівливих моментів "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 ) - Викликається, коли системі потрібен контролер перегляду, який використовується для визначення стилю рядка стану"Якщо контролер перегляду контейнерів отримує свій стиль рядка стану від одного з його дочірніх контролерів перегляду, [переосмислить це властивість] і поверне цей дочірній контролер перегляду. Якщо ви повернете нуль або не замініть цей метод, використовується стиль рядка стану для себе Якщо значення повернення цього методу змінюється, викличте метод 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
, не забудьте застосувати цей клас до своїх контролерів nav через контролера ідентичності в 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
файлу програми та надайте йому значення НІ.
Якщо хтось використовує контролер навігації і хоче, щоб усі його контролери навігації мали чорний стиль, ви можете написати розширення для 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
категорії не назветься, схоже, що шипіння є єдиним варіантом без необхідності використання підкласу.
Приклад:
Заголовок категорії:
@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 + Оновлення.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