preferenceStatusBarStyle не називається


257

Я стежив за цією темою, щоб переоцінити -preferredStatusBarStyle, але вона не називається. Чи є варіанти, які я можу змінити, щоб увімкнути це? (Я використовую XIB у своєму проекті.)


Це не називається в якому контексті: тренажер? на пристрої?
bneely

@bneely їх обох.
trgoofi

Ви використовуєте тренажер iOS 7, пристрій iOS 7, а ваш базовий SDK - 7.0?
bneely

@bneely iOS SDK 7.0 показано нижче мого проекту, це означає, що мій базовий пакет SDK становить 7,0?
trgoofi

У налаштуваннях збірки "Base 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 файл , якщо ви використовуєте цей метод.


2
У мене така ж проблема, як і у вас, не встановлюючи контролер кореневого перегляду. Як чорт ти це знайшов?
trgoofi

1
Я підозрював, що щось у рамках не отримує сповіщення setNeedsStatusBarAppearanceUpdate- мої підозри підтверджувалися, коли я вносив цю зміну.
AbdullahC

2
Пов’язана проблема, яку я знайшов у додатку, - це контролер подання даних із повноекранним контролером дочірнього перегляду, який не переміщав childViewControllerForStatusBarStyle та childViewControllerForStatusBarHidden, щоб повернути цей контролер дочірнього виду. Якщо у вас є власна ієрархія контролера перегляду, вам потрібно надати ці методи для інформування системи, який контролер перегляду слід використовувати для визначення стилю рядка стану.
Джон Штайнмет

встановлення rootviewcontroller нічого не змінює. Вам слід попрацювати з коментарем Йона. І будьте обережні, телефонуючи setneedsstatusbarappearanceUpdate. Вам слід зателефонувати йому від батьків на роботу.
doozMen

1
@Hippo ти геній !! Як ви виявили, що це було через те, що він не встановив rootviewcontroller?
ViruMax

1019

Для всіх, хто використовує UINavigationController:

Пристрій UINavigationControllerне пересилає preferredStatusBarStyleдзвінки до своїх контролерів дочірнього перегляду. Натомість він управляє власним станом - як слід, він малює у верхній частині екрана, де живе рядок стану, і тому він повинен відповідати за нього. Для цього реалізація preferredStatusBarStyleу своїх ВК в навигаційному контролері нічого не зробить - вони ніколи не будуть викликатися.

Хитрість полягає в тому, що 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]
Томас Дезерт

15
Ідеальна відповідь. Жодна з інших відповідей на SO не врахувала UINavigationControl. 2 години удару головою об клавіатуру.
Райан Елфорд

10
Престижність @Patrick для вказівки , що navigationBarHiddenсет YESбуде на самому справі preferredStatusBarStyleназивається, і попередженням для тих, хто може наштовхнутися на це: він працює з navigationBarHidden, але не з navigationBar.hidden!
jcaron

4
Це повинно бути очевидним, але для цього потрібно також "Перегляд зовнішнього вигляду рядка стану на основі контролера", встановленого "ТАК".
Код балера

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
}

Нарешті, переконайтеся, що у вашому списку програм НЕ встановлено значення "Перегляд вигляду рядка стану на основі контролера". Або видаліть цей рядок або встановіть його ТАК (що, на мою думку, зараз для iOS 7?)


Виглядає як return self.topViewController;твори для мене, але return self.visibleViewController;- не
k06a

vidViewController може повернути представлений в даний час модальний контролер, коли ви відхилите його. Який облом. Використовуйте topViewController.
Бен Сінклер

1
@ d.lebedev нормально, але я не думаю, що жодна з цих проблем тут застосовується. Вам не потрібно телефонувати superв цьому методі, і ви дійсно хочете змінити поведінку всіх контролерів цього типу
ed '

1
це не працює для мене на iOS 9.3. Я думаю, в цьому і полягає проблема: це питання має особливе значення, оскільки багато класів какао реалізуються за допомогою категорій. Метод, визначений рамкою, який ви намагаєтеся переосмислити, може бути реалізований у категорії, і тому, яка реалізація має перевагу, не визначається.
vikingosegundo

2
Це неправильно, і він працює в iOS 13.4. Тому що розширення об'єктивних класів C у Swift реалізується через цілі C категорії. Перевизначення методів через цілі C категорій не рекомендується і, ймовірно, може порушитися. Дивіться stackoverflow.com/a/38274660/2438634
Marc Etcheverry

79

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

extension UINavigationController {
    override open var childForStatusBarStyle: UIViewController? {
        return self.topViewController
    }
}

10
Ви, сер, заслужили медаль.
nikans

2
Дуже дякую людино. Натомість я повертав vidViewController без успіху.
Фабіо Салата

1
Це золото. У мене вбудований контролер навігації у вкладку, і я просто кинув це у файл, і тепер я можу змінити зовнішній вигляд рядка стану в будь-якому місці.
Вахід Амірі

2
Це неправильно, і він працює в iOS 13.4. Тому що розширення об'єктивних класів C у Swift реалізується через цілі C категорії. Перевизначення методів через цілі C категорій не рекомендується і, ймовірно, може порушитися. Дивіться stackoverflow.com/a/38274660/2438634
Marc Etcheverry

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
    }
}

Редагування: оновлення змін 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 реалізується через цілі C категорії. Перевизначення методів через цілі C категорій не рекомендується і, ймовірно, може порушитися. Дивіться stackoverflow.com/a/38274660/2438634
Marc Etcheverry

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. Отже, щоб 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
    }
}

11

На додаток до відповіді serenn, якщо ви представляєте контролер подання з modalPresentationStyle(наприклад .overCurrentContext), вам слід також зателефонувати це на нещодавно представлений контролер перегляду:

presentedViewController.modalPresentationCapturesStatusBarAppearance = true

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


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: Чому це порушить майбутню поведінку? preferenceStatusBarStyle: є кращим методом Apple для налаштування стилю рядка стану.
Артем Абрамов

2shuiyouren: Чому я повинен збільшувати складність шляхом підкласифікації, якщо я можу просто використовувати категорію і включати її в будь-яке місце, де я захочу? У всякому разі, це питання архітектури, а не реалізації.
Артем Абрамов

2
@ArtemAbramov Оскільки UINavigationController вже реалізує preferredStatusBarStyleта виконує певну логіку UINavigationController. Зараз ця логіка заснована, navigationBar.barStyleале я бачу додаткові перевірки (наприклад, UISearchDisplayControllerперехід до режиму навігації). Перевіряючи логіку за замовчуванням, ви втрачаєте всю цю функціональність і залишаєте себе відкритим для дратівливих моментів "wtf" у майбутньому. Дивіться мою відповідь вище про правильний спосіб це зробити, підтримуючи вбудовану поведінку навігатора.
Тайсон

9

Швидкий 4.2 та вище

Як зазначено у вибраній відповіді , першопричиною є перевірка вашого контролера об'єкта контролера перегляду вікна.

Можливі випадки вашої структури потоку

  • Спеціальний об'єкт UIViewController - це контролер перегляду коренів вікна. Контролер

    кореневого вікна вікна - це об'єкт UIViewController, і він додатково додає або видаляє контролер навігації або tabController залежно від потоку вашої програми.

    Такий потік зазвичай використовується, якщо ваш додаток має попередній потік входу на навігаційному стеку без вкладок і після входу в систему за допомогою вкладок і, можливо, кожна вкладка додатково містить контролер навігації.

  • Об'єктом TabBarController є контролер перегляду коренів вікна

    Це потік, де контролером перегляду кореневих вікон є tabBarController, можливо, кожна вкладка додатково містить контролер навігації.

  • Об'єктом NavigationController є контролер перегляду кореневих вікон вікна

    Це потік, у якому контролером перегляду кореневих вікон є 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 реалізується через цілі C категорії. Перевизначення методів через цілі C категорій не рекомендується і, ймовірно, може порушитися. Дивіться stackoverflow.com/a/38274660/2438634
Marc Etcheverry

@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,

      "Якщо контролер перегляду контейнерів отримує свій стиль рядка стану від одного з його дочірніх контролерів перегляду, [переосмислить це властивість] і поверне цей дочірній контролер перегляду. Якщо ви повернете нуль або не замініть цей метод, використовується стиль рядка стану для себе Якщо значення повернення цього методу змінюється, викличте метод 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, не забудьте застосувати цей клас до своїх контролерів nav через контролера ідентичності в 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 реалізується через цілі C категорії. Перевизначення методів через цілі C категорій не рекомендується і, ймовірно, може порушитися. Дивіться stackoverflow.com/a/38274660/2438634
Marc Etcheverry

@MarcEtcheverry цей конкретний екземпляр не помилився. Справа в тому, що підкласи інших об'єктів / протоколів, таких як UINavigationController, не мали їх попередньої реалізації для конфлікту в динамічній розсилці. Не було за замовчуванням чи реалізацією в межах фактичних підкласів, саме тому це був найчистіший спосіб реалізувати це через додаток, не створюючи зайвої залежності (періоду). На жаль, 13.4, схоже, змінило цю поведінку. Я здогадуюсь за лаштунками, у них є перевірка чи реалізація, які вже не існують роками .........
TheCodingArt


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файлу програми та надайте йому значення НІ.


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 appearanceYES у вашому, Info.plistщоб зробити цю роботу (що за замовчуванням).


@Як це в swift3?
літак

1

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

override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent
 }

1

Більшість відповідей не містять належної реалізації childViewControllerForStatusBarStyleметоду UINavigationController. Згідно з моїм досвідом, ви маєте працювати з такими випадками, як, коли контролер прозорого перегляду представлений над контролером навігації. У цих випадках ви повинні передати управління модальному контролеру ( visibleViewController), але не тоді, коли воно зникає.

override var childViewControllerForStatusBarStyle: UIViewController? {
  var childViewController = visibleViewController
  if let controller = childViewController, controller.isBeingDismissed {
    childViewController = topViewController
  }
  return childViewController?.childViewControllerForStatusBarStyle ?? childViewController
}

1

У моєму випадку я випадково представив Контролер перегляду / навігації як UIModalPresentationStyle.overFullScreen, що preferredStatusBarStyleне викликає виклику. Після переключення на нього UIModalPresentationStyle.fullScreenвсе працює.


1

Що стосується 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];

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


0

Якщо хтось зіткнувся з цією проблемою з UISearchController. Просто створіть новий підклас UISearchController, а потім додайте код нижче до цього класу:

override func preferredStatusBarStyle() -> UIStatusBarStyle {
    return .LightContent
}

0

Зверніть увагу, що при використанні self.navigationController.navigationBar.barStyle = UIBarStyleBlack;розчину

не забудьте перейти до вашого списку та встановити "Перегляд зовнішнього вигляду на основі контролера" на ТАК. Якщо його немає, він не працюватиме.


Налаштування UIViewControllerBasedStatusBarAppearance to YES у проектному списку змінило для мене все значення. Я про це забув.
філо

0

Починаючи з Xcode 11.4, preferredStatusBarStyleперестановка властивості в розширенні UINavigationController більше не працює, оскільки вона не буде викликана.

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

Правильне рішення, яке я придумав, - додавши туди підклас UINavigationControllerта замінюючи preferredStatusBarStyleйого. Якщо ви використовуєте цей користувальницький UINavigationController для всіх своїх переглядів, ви будете на стороні збереження.


-1

NavigationController або TabBarController - це ті, яким потрібно надати стиль. Ось як я вирішив: https://stackoverflow.com/a/39072526/242769


Якщо ви думаєте, що це дублікат іншого питання, будь ласка, закрийте його, як дублікат
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.