Avatar ·

Изменение цвета статус-бара для отдельных ViewControllers в iOS 8 на Swift

Всем привет! Я новичок в Swift и разработке под iOS. Пишу приложение, которое должно поддерживать iOS 8. Столкнулся с проблемой: мне нужно, чтобы на разных экранах цвет текста статус-бара (вверху экрана, где отображается время, заряд батареи и т.д.) был разным. Например, на главном экране с белым фоном текст должен быть чёрным, а на экране с тёмным фоном — белым. Я уже пробовал устанавливать `UIApplication.shared.statusBarStyle = .lightContent` в `viewDidLoad`, но это меняет цвет для всего приложения, а не для конкретного контроллера. Кроме того, Xcode выдаёт предупреждение, что этот метод устарел в iOS 9 и выше. Я читал, что нужно переопределять метод `preferredStatusBarStyle` в своём ViewController, но у меня ничего не получается. Вот мой код: ```swift class MyViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Пытаюсь изменить здесь — не работает } override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent } } ``` Но статус-бар всё равно остаётся тёмным. Я также слышал, что нужно настраивать `Info.plist` и добавлять ключ `UIViewControllerBasedStatusBarAppearance`. Подскажите, как правильно это сделать для iOS 8? Нужно ли что-то менять в настройках навигационного контроллера, если я использую UINavigationController? Буду благодарен за подробное объяснение с примерами кода. <<>>

Правильное решение для iOS 8 и выше

Проблема в том, что вы не учли два ключевых момента: настройку Info.plist и особенности работы с UINavigationController. Давайте разберём всё по шагам.

Шаг 1: Настройка Info.plist

Добавьте в файл Info.plist ключ UIViewControllerBasedStatusBarAppearance (на русском: «Основанный на UIViewController внешний вид строки состояния») и установите его значение в YES (или true для Xcode). Это даст каждому ViewController возможность самостоятельно управлять цветом статус-бара. Если этот ключ отсутствует или установлен в NO, система будет использовать глобальную настройку из UIApplication.shared.statusBarStyle.

Шаг 2: Переопределение preferredStatusBarStyle

В вашем ViewController переопределите свойство preferredStatusBarStyle:

class MyViewController: UIViewController {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent  // Белый текст для тёмного фона
        // Для чёрного текста используйте .default
    }
}

Шаг 3: Обновление статус-бара после изменений

Если вы хотите динамически менять цвет (например, после смены темы), вызовите setNeedsStatusBarAppearanceUpdate():

class MyViewController: UIViewController {
    var isDarkBackground = true

    override var preferredStatusBarStyle: UIStatusBarStyle {
        return isDarkBackground ? .lightContent : .default
    }

    func toggleBackground() {
        isDarkBackground.toggle()
        setNeedsStatusBarAppearanceUpdate()  // Перерисовывает статус-бар
    }
}

Шаг 4: Особенности с UINavigationController

Если ваш ViewController находится внутри UINavigationController, то именно NavigationController управляет статус-баром, а не ваш контроллер. Есть два решения:

  • Вариант A (рекомендуемый): Создайте подкласс UINavigationController и переопределите в нём preferredStatusBarStyle, делегируя выбор текущему контроллеру:
class CustomNavigationController: UINavigationController {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return topViewController?.preferredStatusBarStyle ?? .default
    }
}

Используйте этот класс вместо стандартного UINavigationController в Storyboard или коде.

  • Вариант B: В каждом дочернем контроллере переопределите childForStatusBarStyle, возвращая self. Но это сложнее и реже используется.

Полный пример для iOS 8

// В Info.plist: UIViewControllerBasedStatusBarAppearance = YES

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

class LightViewController: UIViewController {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .default  // Чёрный текст на белом фоне
    }
}

class DarkViewController: UIViewController {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent  // Белый текст на тёмном фоне
    }
}

Важное замечание для iOS 8

Метод UIApplication.shared.statusBarStyle действительно устарел в iOS 9, но в iOS 8 он ещё работал. Однако описанный выше подход с preferredStatusBarStyle полностью совместим с iOS 8 и является правильным способом. Убедитесь, что ваш проект настроен на iOS 8.0 как минимальную версию в настройках проекта (Deployment Target).

<<>>

Альтернативный подход: Использование childForStatusBarStyle

Если вы не хотите создавать подкласс UINavigationController, можно переопределить свойство childForStatusBarStyle в вашем NavigationController. Это более гибкий способ, когда контроллер сам решает, кто будет управлять статус-баром.

class MyNavigationController: UINavigationController {
    override var childForStatusBarStyle: UIViewController? {
        return topViewController
    }
}

Теперь каждый дочерний ViewController может переопределять preferredStatusBarStyle, и NavigationController будет делегировать управление ему. Этот подход работает начиная с iOS 7 и не требует дополнительных настроек в Info.plist (хотя ключ UIViewControllerBasedStatusBarAppearance всё равно рекомендуется установить в YES для совместимости).

Динамическое изменение без переопределения

Если вам нужно менять цвет статус-бара в зависимости от действий пользователя (например, при появлении модального окна), используйте setNeedsStatusBarAppearanceUpdate() в нужный момент. Это вызовет повторный запрос preferredStatusBarStyle у текущего контроллера.

class MyViewController: UIViewController {
    @IBAction func buttonTapped(_ sender: UIButton) {
        // Изменяем какое-то условие
        setNeedsStatusBarAppearanceUpdate()
    }
}

Обратите внимание: если у вас вложенные контроллеры (например, TabBarController внутри NavigationController), придётся переопределять childForStatusBarStyle на каждом уровне, чтобы цепочка делегирования работала корректно.