UIStatusBarStyle PreferredStatusBarStyle не работает на iOS 7

110

В моем приложении iPhone построен с Xcode 5 для IOS 7 I множеств UIViewControllerBasedStatusBarAppearance=YESв info.plist, и в моем у ViewControllerменя есть этот код:

-(UIStatusBarStyle) preferredStatusBarStyle
{
    return UIStatusBarStyleLightContent;
}

Но строка состояния по-прежнему черная на черном фоне.

Я знаю , что его можно изменить это приложение шириной, установив UIViewControllerBasedStatusBarAppearance=NOв info.plist, но я на самом деле нужно , чтобы изменить это на viewControllerна viewControllerоснове во время выполнения.

Эндрю Смит
источник
Привет, у меня такая же проблема, как вы упомянули в вопросе. Вы получили решение? Пожалуйста, предоставьте мне это.
Нундла Сандип,

Ответы:

281

Я обнаружил, что если ваш ViewController находится внутри navigationController, то navigationController navigationBar.barStyleопределяет statusBarStyle.

Установка вашего navigationBar в barStyleзначение UIBarStyleBlackTranslucentдаст белый текст строки состояния (т. Е. UIStatusBarStyleLightContent) И UIBarStyleDefaultдаст черный текст строки состояния (т. Е. UIStatusBarStyleDefault).

Обратите внимание, что это применимо, даже если вы полностью измените цвет панели навигации с помощью ее barTintColor.

mxcl
источник
для меня это имеет смысл ... отлично
Ник
14
Я считаю, что это потому, что UINavigationController's preferredStatusBarStyleне вызывает ViewController, который он размещает, а вместо этого просто возвращает на основе своего navigationBarStyle.
mxcl
В этом случае представление находится не внутри контроллера навигации.
Эндрю Смит
Очень нелогично думать, что стиль панели имеет приоритет над реализованным методом в контроллере представления, и только при представлении модальных представлений!
Matej
3
UIBarStyleBlackTranslucent устарело, используйте UIBarStyleBlackвместо этого
Нхон Нгуен
87

Хорошо, вот трюк. Вам нужно добавить ключ «Просмотр строки состояния на основе контроллера» и установить значение Нет.

Это противоречит тому, что кажется значением этого ключа, но даже если вы установите значение No, вы все равно можете изменить внешний вид строки состояния, а также будет ли она отображаться в любом контроллере представления. Таким образом, он действует как «Да», но устанавливается на «Нет»!

Теперь я могу сделать строку состояния белой или темной.

Эндрю Смит
источник
6
Для меня это было неправильно. Ключ должен быть установлен на «Да», как и следовало ожидать. Я использую Xcode 5.1 iOS 7.1, так что, возможно, он изменился.
joel.d
Я использую Xcode 5.1 и iOS 7.1, и у меня НЕТ ... СТРАННО.
Arjun Mehta
Куда мне добавить этот ключ?
Hadu
В вашем файле [AppName] -Info.plist
Сарен Инден
1
Он отлично работает, когда для клавиши «Просмотр строки состояния на основе контроллера» установлено значение «ДА» с Xcode6.0, iOS 8.0
bpolat
77

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

extension UITabBarController {
    public override func childViewControllerForStatusBarStyle() -> UIViewController? {
        return selectedViewController
    }
}

extension UINavigationController {
    public override func childViewControllerForStatusBarStyle() -> UIViewController? {
        return visibleViewController
    }
}

Для Swift 3 это не методы, а свойства:

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

extension UINavigationController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return visibleViewController
    }
}

В Swift 4.2 свойства были переименованы:

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

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

использование

class ViewController: UIViewController {

    // This will be called every time the ViewController appears
    // Works great for pushing & popping
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }

}
Дэниел Вуд
источник
6
Это, безусловно, лучший ответ (для приложений, использующих UINavigationController или UITabBarController
Кесава
1
что это за использование?
AnBisw
@Annjawn эти методы используются UIKit. Вам не нужно ничего делать, кроме как добавить его в свой проект.
Daniel Wood
@DanielWood да, я понял это и совершенно забыл, что я использовал ту же самую вещь в одном из моих предыдущих проектов, хотя и немного иначе.
AnBisw 02
Это действительно лучший ответ
Муса алматри
33

Возможно, я приду к этому немного поздно, но на случай, если кто-то еще ищет работающее и проверенное решение для всего приложения.

@mxcl правильно описывает, почему это происходит. Чтобы исправить это, мы просто создаем расширение (или категорию в obj-c), которое переопределяет метод primarySatusBarStyle () UINavigationController. Вот пример на Swift:

extension UINavigationController {
    public override func preferredStatusBarStyle() -> UIStatusBarStyle {
        if let rootViewController = self.viewControllers.first {
            return rootViewController.preferredStatusBarStyle()
        }
        return super.preferredStatusBarStyle()
    }
}

Этот код просто извлекает первый контроллер представления (корневой контроллер представления) и разворачивает его (в obj-c просто проверьте, не равен ли он нулю). Если разворачивание прошло успешно (не nil), мы получаем rootViewControllers preferredStatusBarStyle. В противном случае мы просто возвращаем значение по умолчанию.

Надеюсь, это поможет всем, кому это может понадобиться.

Кайл Бегеман
источник
2
В Swift 2.0 вы должны удалить «as? UIViewController» из оператора условия.
Thomás Calmon
Великолепно, я сделал одну модификацию в дополнение к удалению оператора «as», я изменил его с «первого» на «последний» таким образом, что любой контроллер представления, который видит пользователь наверху стека, будет иметь возможность управлять цвет строки состояния. Отличная работа, спасибо, что поделились!
Unome
1
Если ваш контроллер навигации не имеет контроллеров представления, это вызовет бесконечный цикл. return self.preferredStatusBarStyle()обратится к этому же методу.
BearMountain
1
В моем случае вместо использования rootViewController я использовал topViewController, так как во время стека стиль может измениться.
Рик Сантос
2
@Unome visibleViewControllerбыло бы еще лучше
Cœur
21

Чтобы предоставить более подробную информацию о принятом ответе, поместите следующую строку в didFinishLaunchingWithOptions:метод делегата приложения :

[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;

Затем в своем Info.plist добавьте View controller-based status bar appearanceи установите для него значениеNO .

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

РЕДАКТИРОВАТЬ : вы также можете сделать это, не вводя никакого кода: qaru.site/questions/435 / ... / 18732865/855680

MLQ
источник
1
Обратите внимание, что этот способ устарел в IOS 9.0
Мохамед Салах,
10

В viewDidLoad просто напишите это

[self setNeedsStatusBarAppearanceUpdate];

просто сделай это, и это сработает

можешь попробовать это

Set UIViewControllerBasedStatusBarAppearance to NO.
Call [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

Еще одна вещь, которую я увидел в вашем вопросе, что вы написали такой метод

 -(void)UIStatusBarStyle PreferredStatusBarStyle ()
        {
            return UIStatusBarStyle.LightContent;
        }

но это должно быть так

-(UIStatusBarStyle)preferredStatusBarStyle{ 
    return UIStatusBarStyleLightContent; 
} 
Пользователь 1531343
источник
Это приводит к вызову метода primaryStatusBarStyle, но строка состояния по-прежнему остается черной.
Эндрю Смит
пожалуйста, посмотрите мой обновленный ответ ... дайте мне знать быстро, работает это или нет
Пользователь 1531343 01
В моем исходном вопросе прямо говорится, что мне нужно выполнять просмотр за просмотром в строке состояния.
Эндрю Смит
Можете ли вы проверить свой код со ссылкой на мой обновленный вопрос?
Пользователь 1531343 03
1
[self setNeedsStatusBarAppearanceUpdate];такой отличный метод, спасибо!
Supertecnoboff
6

Вот как я это решил. Обычно NavigationController или tabBarController определяют внешний вид строки состояния (скрытый, цветной и т. Д.).

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

Вы запускаете обновление внешнего вида строки состояния, вызывая, setNeedsStatusBarAppearanceUpdateкоторый запускается preferredStatusBarStyleснова и обновляет пользовательский интерфейс в соответствии с тем, что возвращает метод.

public protocol StatusBarStyleHandler {
    var preferredStatusBarStyle: UIStatusBarStyle { get }
}

public class CustomNavigationCotnroller: UINavigationController {

    public override var preferredStatusBarStyle: UIStatusBarStyle {
        if let statusBarHandler = visibleViewController as? StatusBarStyleHandler {
            return statusBarHandler.preferredStatusBarStyle
        }

        return .default
    }
}

Тогда использование

public class SomeController: UIViewController, StatusBarStyleHandler {

    private var statusBarToggle = true

    // just a sample for toggling the status bar style each time method is called
    private func toggleStatusBarColor() {
        statusBarToggle = !statusBarToggle
        setNeedsStatusBarAppearanceUpdate()
    }

    public override var preferredStatusBarStyle: UIStatusBarStyle {
        return statusBarToggle ? .lightContent : .default
    }
}
арьякст
источник
Этот пост был бы намного улучшен, если бы вы могли объяснить, почему и как это решает проблему.
Вместо создания подкласса UINavigationController вы также можете просто создать расширение для UINavigationController и достичь того же результата без создания подкласса.
Wilforeal
4

1) Одна настройка для всего проекта:

Если возможно, удалите UIViewControllerBasedStatusBarAppearanceпару ключ-значение из вашего info.plist или установите, NOне удаляя ее. Если его нет в вашем info.plist, ничего не делайте. По умолчанию используется NOэто свойство.

Добавьте код ниже в свой AppDelegate.m:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
}

2) Различные настройки для разных контроллеров просмотра:

Добавьте UIViewControllerBasedStatusBarAppearanceпару ключ-значение в свой info.plist и установите для нее значение YES.

Если ваш View Controller не встроен в Navigation Controller. Скажем, MyViewController. просто добавьте приведенный ниже код в файл MyViewController.m. Если ваш контроллер представления встроен в контроллер навигации, создайте новый класс касания какао и сделайте его подклассом UINavigationController. Скажем MyNC. На правой панели выберите «Просмотр контроллера навигации» на раскадровке; Утилиты -> Инспектор идентичности -> Пользовательский класс -> Класс, введите «MyNC». После связывания раскадровки с вашим классом какао "MyNC" добавьте код ниже в свой MyNC.m:

- (BOOL)prefersStatusBarHidden {
    return NO;
}

-(UIStatusBarStyle)preferredStatusBarStyle {
    return UIStatusBarStyleLightContent;
}
Фатих Аксу
источник
Кажется, в iOS9 UIViewControllerBasedStatusBarAppearance по умолчанию содержит значение YES, так как мне нужно было добавить его вручную в .plist и установить значение NO для правильной работы.
Мохд Асим
4

Даже со всеми ответами здесь я все еще не нашел для себя точного решения, но начал с ответа от Дэниела. В итоге я получил:

override var preferredStatusBarStyle: UIStatusBarStyle {
     return visibleViewController?.preferredStatusBarStyle ?? .lightContent
}

в контроллерах навигации (аналогично вкладке, только что selectedViewController). И тогда он будет уважать:

override var preferredStatusBarStyle: UIStatusBarStyle {
     return .lightContent
}

В каждом контроллере представления, если вы не установили иное. Мне не нужно setNeedsStatusBarAppearanceUpdate()никуда звонить , он просто обновляется, когда вы подходите к каждому контроллеру представления.

Эндрю Пламмер
источник
2
Я получил почти идентичное решение после нескольких часов борьбы с этим.
Скотт Юнгвирт
В какой-то момент это, кажется, было исправлено, просто использование предпочитаемогоStatusBarStyle в каждом VC теперь отлично работает для меня.
Эндрю Пламмер
4

iOS 13 Решение (я)

В ответе с наибольшим количеством голосов используется "устаревший" код 👎

barStyleТеперь установка свойства (iOS 13+) считается «устаревшей настройкой». Согласно Apple ,

В iOS 13 и новее настройте панель навигации, используя свойства standardAppearance, compactAppearance и scrollEdgeAppearance. Вы можете продолжать использовать эти устаревшие средства доступа для непосредственной настройки внешнего вида панели навигации, но вы должны сами обновить внешний вид для различных конфигураций панели.

Что касается вашей попытки - вы были на правильном пути!

UINavigationControllerявляется подклассом UIViewController(кто знал 🙃)!

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

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

  1. Переопределить preferredStatusBarStyleвUINavigationController

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

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

      ИЛИ

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

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

      "Если ваш контейнерный контроллер представления наследует свой стиль строки состояния от одного из своих дочерних контроллеров представления, [переопределите это свойство] и верните этот контроллер дочернего представления. Если вы вернете nil или не переопределите этот метод, будет использоваться стиль строки состояния для себя . Если возвращаемое значение из этого метода изменится, вызовите метод 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(из UINavigationController) ( документ ) - Контроллер представления, связанный с текущим видимым представлением в интерфейсе навигации (подсказка: это может включать «контроллер представления, который был представлен модально поверх самого контроллера навигации»)

Примечание. Если вы решили создать подкласс UINavigationController, не забудьте применить этот класс к своим навигационным контроллерам через инспектор идентичности в IB.

PS В моем коде используется синтаксис Swift 5.1 😎

Эндрю Кирна
источник
1
Очень полный ответ, спасибо! Кроме того, я некоторое время боролся, в iOS 13 .defaultстиль учитывает темный режим и не документирован, поэтому, если вы также поддерживаете предыдущие версии iOS, вы можете добавить их, if #available(iOS 13, *) { return .darkContent } else { return .default }если пытаетесь настроить стиль строки состояния вручную в соответствии с цвет за строкой состояния и этот цвет «яркий».
valcanaia 07
1
Обратите внимание, что метод расширения для выполнения override var больше не работает в Xcode 11.4 / iOS 13.4
Марк Эчеверри,
Потому что расширение объективных классов C в Swift реализовано с помощью категорий Objective C. Переопределение методов через категории Objective C не рекомендуется и может сломаться. См. Stackoverflow.com/a/38274660/2438634
Марк Эчеверри,
Хотя переопределение UINavigationController определенно работает, это похоже на ошибку со стороны Apple, заключающуюся в том, что childForStatusBarStyle по умолчанию не возвращает свой topViewController. Например, UITabBarController делает это для своих вкладок. Для меня нет причин, по которым UINavigationController, являясь строго контейнерным контроллером для размещения «настоящих» контроллеров представления, а не для представления собственного пользовательского интерфейса, должен «съесть» эти стили строки состояния. Создадим радар для Apple.
Игорь Васильев,
1

Если вы хотите скрыть statusBar во время splashScreen, но хотите изменить стиль на светлое содержимое (StatusBarInitiallyHidden на Plist должно быть НЕТ, чтобы скрыть statusBar при заставке), вы можете добавить это в метод didFinishLaunchingWithOptions appDelegate, чтобы изменить его на lightContent.

[[UIApplication sharedApplication]setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
[[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent];
Aalesano
источник
1

быстрый пример

в AppDelegate.swift

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
    UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.LightContent;

    return true
}

в наборе info.plist Просмотр внешнего вида строки состояния на основе контроллера: НЕТ

фьялавуз
источник
1

Если вы используете NavigationController, вы можете создать подклассNavigationController чтобы он обращался к своему дочернему контроллеру представления

// MyCustomNavigationController

- (NSUInteger)supportedInterfaceOrientations {
    UIViewController *viewControllerToAsk = [self findChildVC];
    return [viewControllerToAsk supportedInterfaceOrientations];
}

- (BOOL)shouldAutorotate {
    UIViewController *viewControllerToAsk = [self findChildVC];
    return [viewControllerToAsk shouldAutorotate];
}

- (UIStatusBarStyle)preferredStatusBarStyle {
    UIViewController *viewControllerToAsk = [self findChildVC];
    return [viewControllerToAsk preferredStatusBarStyle];
}

- (UIViewController *)findChildVC {
    return self.viewControllers.firstObject;
}
onmyway133
источник
1

Swift 4.2

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

extension UINavigationController {
    open override var childForStatusBarStyle: UIViewController? {
        return visibleViewController
    }
}
Вячеслав
источник
Обратите внимание, что метод расширения для выполнения override var больше не работает в Xcode 11.4 / iOS 13.4
Марк Эчеверри,
@MarcEtcheverry, так почему ты проголосовал против ответа? это кажется странным.
Вячеслав
Потому что расширение объективных классов C в Swift реализовано с помощью категорий Objective C. Переопределение методов через категории Objective C не рекомендуется и может сломаться. См. Stackoverflow.com/a/38274660/2438634
Марк Эчеверри,
@MarcEtcheverry 'не рекомендуется'! = 'Никогда не используйте это!'. на июль 2018 ответ был верным. Даже этот ответ не актуален, это не повод снижать его. Я не вижу будущего
Вячеслав
0

Вы можете установить стиль строки состояния. Она будет напоминать строку состояния, как в iOS 6 и ниже.
Вставьте эти методы в свой контроллер представления

-(UIStatusBarStyle)preferredStatusBarStyle{
    return UIStatusBarStyleBlackOpaque;
}

и вызовите этот метод из вида, загрузился так

if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f)
    {
       [self setNeedsStatusBarAppearanceUpdate];
    }
Ганапати
источник
Вы имеете ввиду [self setStatusBarNeedsUpdate]во втором блоке? (Или хотя бы что-то еще).
mxcl
0

Я просто хочу добавить примечание для конкретного случая, с которым я столкнулся. В моем приложении был еще один UIWindow, чтобы отображать лицо чата, которое постоянно перемещалось по всему моему приложению. В результате ни одно из вышеперечисленных решений не сработало, и я не совсем уверен, почему! Все, что я заметил, это то, что причиной этого был мой ViewController в новом UIWindow! И если я хочу изменить стиль строки состояния, я должен сделать это в этом контроллере представления нового UIWindow.

Эта заметка может помочь другим, у кого подобная структура! Таким образом, вы можете применять упомянутые выше решения в ViewController нового UIWindow.

Опять же это частный случай.

Спасибо

Эхаб Сайфан
источник
-1

Для быстрого 3 в вашем UIViewController:

override var preferredStatusBarStyle : UIStatusBarStyle { return UIStatusBarStyle.lightContent }
Маврик Лааксо
источник