Попытка перезагрузить контроллер представления, чтобы обновить текущую тему - PullRequest
2 голосов
/ 16 апреля 2019

Что я ищу

Я пытаюсь перезагрузить все представления в моем контроллере представлений, чтобы переключаться между темами (аналогично тому, что делает Twitter или Apple Maps).

Twitter Apple Maps


Как настроить мои разные темы

У меня есть тематические представления, настроенные так:

@IBDesignable
extension UIView {

    @IBInspectable
    var lightBackgroundColor: UIColor? {
        set {
            switch GEUserSettings.theme {
            case .light:    backgroundColor = newValue
            case .dark:     break
            }
        }
        get {
            return self.lightBackgroundColor
        }
    }

    @IBInspectable
    var darkBackgroundColor: UIColor? {
        set {
            switch GEUserSettings.theme {
            case .light:    break
            case .dark:     backgroundColor = newValue
            }
        }
        get {
            return self.darkBackgroundColor
        }
    }
}

Это позволяет мне в Main.storyboard установить цвет фона темы light и dark, в зависимости от текущей темы. Мой эффект размытия фона исключен из этого, так как я не смог найти способ обновить style в коде, поэтому он создан в viewDidLoad.


Запуск темы от сотрясения устройства

Однако, когда я хочу сменить тему, я не уверен, как это сделать. Я хочу, чтобы вызвать это от встряхивания устройства, например, так:

override func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
    print("Shaken!")
    let oppositeTheme: GEUserSettings.Theme = {
        switch GEUserSettings.theme {
        case .light:    return .dark
        case .dark:     return .light
        }
    }()

    GEUserSettings.theme = oppositeTheme

    // My attempt to update the view controller to
    // update the theme, which doesn't do anything.
    dismiss(animated: true) {
        UIApplication.shared.keyWindow?.rootViewController?.present(self, animated: true, completion: nil)
        // Yes, the presenting is working, but the views don't change.
    }
}

Каковы возможные решения?

Настройки вступают в силу, если приложение закрывается и перезапускается. Я мог бы либо принудительно закрыть приложение (не используя exit(0), либо что-либо, что считается крахом), либо перезагрузить его во время использования приложения.

Я попытался закрыть, а затем перезагрузить контроллер представления, как показано в коде выше. Тот, который я перезагружаю, представлен сверху основного контроллера представления.

Как я могу заставить это работать, когда я использую раскадровки?

Редактировать - Добавлено изображение моих светлых / темных режимов, чтобы прояснить мой вопрос:

Light/dark modes

Ответы [ 3 ]

1 голос
/ 16 апреля 2019

Если вы собираетесь использовать темы в своем приложении, Apple предлагает протокол UIApperance, который помогает вам одновременно изменять свойства элементов управления определенного вида, используя это, вы будете иметь унифицированный внешний вид для вашего пользовательского интерфейса.Способ использования действительно прост: изменить все UILabel цвета фона можно следующим образом:

UILabel.apperance().backgroundColor = .lightGray

Если вы хотите управлять всем в одном месте, как в вашем примере кода, вы можете создать структурусодержит характеристики вашего пользовательского интерфейса, проверьте эту структуру (я использовал то же имя, что и вы):

import UIKit

struct GEUserSettings {
    enum Theme { case light, dark }

    static public var theme: Theme = .light {
        didSet {
            guard theme != oldValue else { return }
            apply()
        }
    }
    static weak var window: UIWindow?

    static public func toggleTheme() {
        self.theme = theme == .light ? .dark : .light
    }

    static private func apply() {
        setColors()
        if let window = window {
            window.subviews.forEach({ (view: UIView) in
                view.removeFromSuperview()
                window.addSubview(view)
            })
        }
    }

    static public func setColors() {
        switch theme {
        case .light:
            UILabel.appearance().textColor = .black
            UISegmentedControl.appearance().tintColor = .blue
            UILabel.appearance(whenContainedInInstancesOf:    [UISegmentedControl.self]).backgroundColor = .clear
            UITableViewHeaderFooterView.appearance().backgroundColor = .lightGray
            UITableView.appearance().backgroundColor = .white
        case .dark:
            UILabel.appearance().textColor = .red
            UISegmentedControl.appearance().tintColor = .purple
            UILabel.appearance(whenContainedInInstancesOf: [UISegmentedControl.self]).backgroundColor = .clear
            UITableViewHeaderFooterView.appearance().backgroundColor = .black        
            UITableView.appearance().backgroundColor = .darkGray
        }
    }
}

В AppDelegate или как можно скорее вы должны передать ссылку UIWindow на темуструктура менеджера.Я сделал это в AppDelegate didFinishLaunchingWithOptions.Это необходимо для немедленного изменения цвета.

С этой определенной структурой вы можете настроить любой элемент управления пользовательского интерфейса по своему усмотрению.Например, вы можете определить определенный цвет фона для UILabel и иметь другой, если он содержится в UISegmentedControl.

Определяемое вами событие встряхивания может переключаться между темами следующим образом:

override func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
    GEUserSettings.toggleTheme()
}

Если вы встряхиваете устройство, экраны будут переключаться между этими двумя (я изменил только несколько свойств):

Light mode

Dark mode

Если вы хотите поиграть с примером проекта, доступен на Github

Надеюсь, я помогу!

0 голосов
/ 16 апреля 2019

Я наконец понял это, используя NotificationCenter!

GEUserSettings

GEUserSettings теперь выглядит следующим образом:

enum GEUserSettings {

    enum Theme: String {
        case light
        case dark
    }
    /// The current theme for the user.
    static var theme: Theme = .dark
    #warning("Store theme in UserDefaults")

    /// Toggles the theme.
    static func toggleTheme() {
        switch GEUserSettings.theme {
        case .light:    theme = .dark
        case .dark:     theme = .light
        }

        NotificationCenter.default.post(name: Notification.Name("UpdateThemeNotification"), object: nil)
    }
}

GEView

GEView - мой пользовательский подкласс UIView.Это замена вместо моего расширения до UIView.Теперь это выглядит примерно так:

/// UIView subclass to allow creating corners, shadows, and borders in storyboards.
@IBDesignable
final class GEView: UIView {

    // MARK: - Initializers
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        // Triggers when the theme is changed
        NotificationCenter.default.addObserver(self, selector: #selector(updateBackgroundColorNotification), name: Notification.Name("UpdateThemeNotification"), object: nil)
    }
    @objc
    private func updateBackgroundColorNotification() {
        updateBackgroundColor()
    }


    /* ... */


    // MARK: - Background
    @IBInspectable
    var lightBackgroundColor: UIColor? {
        didSet {
            updateBackgroundColor()
        }
    }
    @IBInspectable
    var darkBackgroundColor: UIColor? {
        didSet {
            updateBackgroundColor()
        }
    }

    /// Updates the background color depending on the theme.
    private func updateBackgroundColor() {
        switch GEUserSettings.theme {
        case .light:    backgroundColor = self.lightBackgroundColor
        case .dark:     backgroundColor = self.darkBackgroundColor
        }
    }
}

Обновление через motionBegan(_:with:)

override func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
    super.motionBegan(motion, with: event)

    // Toggles the theme and update the views
    GEUserSettings.toggleTheme()
    drawerViewModel.updateBlurEffect(drawerView: drawerView)
}

И размытие удаляется и воссоздается следующим образом:

/// Creates the blur effect behind the collection view.
func updateBlurEffect(drawerView: GEView) {
    if let blurView = drawerView.subviews[0] as? UIVisualEffectView {
        blurView.removeFromSuperview()
    }

    let blurEffect: UIBlurEffect = {
        switch GEUserSettings.theme {
        case .light:    return UIBlurEffect(style: .light)
        case .dark:     return UIBlurEffect(style: .dark)
        }
    }()
    let blurView = UIVisualEffectView(effect: blurEffect)

    drawerView.addSubview(blurView)
    drawerView.sendSubviewToBack(blurView)
    GEConstraints.fillView(with: blurView, for: drawerView)
}

Это даже не требует выхода из приложения или перезагрузки контроллера представления, это происходит мгновенно!

Extra (анимации)

Если вы хотите, вы также можете анимировать изменение цвета с помощьюизменение функции updateBackgroundColor():

/// Updates the background color depending on the theme.
private func updateBackgroundColor() {
    UIView.animate(withDuration: 0.25) {
        switch GEUserSettings.theme {
        case .light:    self.backgroundColor = self.lightBackgroundColor
        case .dark:     self.backgroundColor = self.darkBackgroundColor
        }
    }
}

Вы также можете анимировать размытие:

/// Creates the blur effect behind the collection view.
func updateBlurEffect(drawerView: GEView) {
    if let blurView = drawerView.subviews[0] as? UIVisualEffectView {
        UIView.animate(withDuration: 0.25, animations: {
            blurView.alpha = 0
        }, completion: { _ in
            blurView.removeFromSuperview()
        })
    }

    let blurEffect: UIBlurEffect = {
        switch GEUserSettings.theme {
        case .light:    return UIBlurEffect(style: .light)
        case .dark:     return UIBlurEffect(style: .dark)
        }
    }()
    let blurView = UIVisualEffectView(effect: blurEffect)
    blurView.alpha = 0

    drawerView.addSubview(blurView)
    drawerView.sendSubviewToBack(blurView)
    GEConstraints.fillView(with: blurView, for: drawerView)

    UIView.animate(withDuration: 0.25, animations: {
        blurView.alpha = 1
    })
}
0 голосов
/ 16 апреля 2019
    typealias Style = StyleManager
//MARK: - Style
final class StyleManager {
    static func selectedThem()->Int?
    {
        return AppUtility?.getObject(forKey: "selectedTheme") as? Int       // 1 for dark Theme ...... 2 for light Theme
    }
    static func BoldFont()->UIFont {
        return UIFont(name: FontType.bold.fontName, size: FontType.bold.fontSize)!
    }
    // MARK: - Style
    static func setUpTheme() {
        Chameleon.setGlobalThemeUsingPrimaryColor(primaryTheme(), withSecondaryColor: theme(), usingFontName: font(), andContentStyle: content())
    }
    // MARK: - Theme
    static func SetPagerViewsColor()->UIColor
    {
       return secondarythemeColor
    }
    static func primaryTheme() -> UIColor {
        setCheckMarkBackground()
        if selectedThem() == 1
        {
            return UIColor.white
        }
        else
        {
            return OddRowColorlight
        }
    }
    static func theme() -> UIColor {
        if selectedThem() == 1
        {
            EvenRowColor = EvenRowColordark
            OddRowColor = OddRowColorlight
            primaryThemeColor=EvenRowColor
            secondarythemeColor=OddRowColor
            return darkGrayThemeColor
        }
        else
        {
            EvenRowColor = lightWhiteThemeColor!
            OddRowColor = UIColor.white
            primaryThemeColor=EvenRowColor
            secondarythemeColor=OddRowColor
            return lightWhiteThemeColor!
        }
        // return FlatWhite()
    }
    static func toolBarTheme() -> UIColor {
        if selectedThem() == 1
        {
            return UIColor.white
        }
        else
        {
            return FlatBlack()
        }
    }
    static func tintTheme() -> UIColor {
        if selectedThem() == 1
        {
            return UIColor.white
        }
        else
        {
            return FlatBlack()
        }
    }
    static func titleTextTheme() -> UIColor {
        if selectedThem() == 1
        {
            return UIColor.white
        }
        else
        {
            return UIColor.white
        }
    }
    static func titleTheme() -> UIColor {
        if selectedThem() == 1
        {
            return darkGrayThemeColor
        }
        else
        {
            return FlatWhite()
        }
    }
    static func textTheme() -> UIColor {
        if selectedThem() == 1
        {
            return UIColor.white
        }
        else
        {
            return FlatBlack()
        }
        //return FlatMint()
    }
    static func backgroudTheme() -> UIColor {
        if selectedThem() == 1
        {

            return .darkGray
        }
        else
        { 
            return .white
        }
    }
}

Теперь создайте некоторые переменные в глобальной области видимости

var primaryThemeColor:UIColor!
var secondarythemeColor:UIColor!
var themeColor:UIColor!
var toolBarThemeColor:UIColor!
var tintThemeColor:UIColor!
var titleTextThemeColor:UIColor!
var titleThemeColor:UIColor!
var textThemeColor:UIColor!
var backgroundThemeColor:UIColor!
var positiveThemeColor:UIColor!
var negativeThemeColor:UIColor!
var clearThemeColor:UIColor!
var setCheckMarkBackgroundColor:UIColor!
var menuSectioColor:UIColor!
var menuCellColor:UIColor!
var menuBackgroundColor:UIColor!
var menuTextTHeme:UIColor!
var themeName:String!
var btnIconColor:UIColor!

Теперь в AppDelegate создайте функцию ниже и вызовите эту функцию в didFinish Launch

func setCurrentThemeColors()
{
    themeColor = Style.theme()
    toolBarThemeColor = Style.toolBarTheme()
    tintThemeColor = Style.tintTheme()
    titleTextThemeColor = Style.titleTextTheme()
    titleThemeColor = Style.titleTheme()
    textThemeColor = Style.textTheme()
    backgroundThemeColor = Style.backgroudTheme()
}
 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    self.setCurrentThemeColors()
    return true
}

Теперь у вас все установлено сВаша тема вам просто необходима. Создайте функцию обновления темы в вашем baseController и переопределите этот метод в каждом ViewController. Поместите логику обновления пользовательского интерфейса в эту функцию, а когда устройство встряхивает, вызовите переопределенный метод, как показано ниже

override func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
print("Shaken!")
updateTheme()
appDelegate.setCurrentThemeColors()
}

  func setLightTheme(){
    AppUtility?.saveObject(obj: 0 as AnyObject, forKey: "selectedTheme")
}
func setDarkTheme(){
    AppUtility?.saveObject(obj: 1 as AnyObject, forKey: "selectedTheme")
}
func updateTheme()
{
    let theme = AppUtility?.getObject(forKey: "selectedTheme") as? Int
    if theme != nil
    {
        _ = theme == 1 ? setLightTheme() : setDarkTheme()
    }
    else
    {
        setDarkTheme()
    }
    appDelegate.setCurrentThemeColors()
    ConfigureView()

}
 func ConfigureView(){
    btnDownLoadPdf.backgroundColor = .clear
    btnRightSide.backgroundColor = .clear
    btnRefreshPage.backgroundColor = .clear
    self.View.backgroundColor = secondarythemeColor
    PeriodicePastDatesPickerView.backgroundColor = secondarythemeColor
    customDatePicker.backgroundColor = secondarythemeColor
    UnitPicker.backgroundColor = secondarythemeColor
    currencyPicker.backgroundColor = secondarythemeColor
}

Примечание: Вы должны обновить цвета в соответствии с вашими потребностями, в нем есть некоторые цвета, которые не будут доступны в вашем случае

...