3
votes

Essayer de recharger le contrôleur de vue pour mettre à jour le thème actuel

Ce que je recherche

J'essaye de recharger toutes les vues dans mon contrôleur de vue, pour changer entre les thèmes (similaire à ce que Twitter ou Apple Maps fait).

 Twitter Apple Maps


Comment j'ai configuré mes différents thèmes

J'ai configuré des vues thématiques comme suit:

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

Cela me permet dans mon Main.storyboard de définir une couleur d'arrière-plan de thème clair et sombre , en fonction de la couleur actuelle thème. Mon effet de flou d'arrière-plan en est exclu, car je n'ai pas trouvé de moyen de mettre à jour le style dans le code, il est donc créé dans viewDidLoad .

Déclencher le thème de secouer l'appareil

Cependant, lorsque je veux changer de thème, je ne sais pas comment le faire. Je veux le déclencher en secouant l'appareil, comme ceci:

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

Quelles sont les solutions possibles?

Les paramètres prennent effet si le l'application est fermée et relancée. Je pourrais forcer l'application à se fermer (sans utiliser exit (0) ou tout ce qui compte comme un plantage), ou la recharger tout en utilisant l'application.

J'ai essayé de fermer puis rechargez le contrôleur de vue, comme indiqué dans le code ci-dessus. Celui que je recharge est présenté en haut du contrôleur de vue de base.

Comment puis-je faire fonctionner cela, car j'utilise des storyboards?

Modifier - Ajout d'une image de mes modes clair / sombre pour rendre ma question plus claire:

Modes clair / sombre


3 commentaires

Créez une fonction de mise à jour de thème dans votre baseController et remplacez cette méthode dans everyoController Mettez la logique de mise à jour de l'interface utilisateur dans cette fonction et lorsque l'appareil a tremblé, appelez la méthode remplacée ... je l'ai fait comme même


comme Abu UI Hassan l'a dit Create method in base controller override dans chaque contrôleur, mettez la logique là pour votre mise à jour de thème. Vous devez utiliser NotificationCenter pour diffuser le message à chaque contrôleur que le contrôleur doit changer de thème.


Voici un bon exemple d'utilisation d'un modèle de conception composite pour appliquer un thème à n'importe quelle hiérarchie de vues. Il peut également être étendu pour afficher les contrôleurs refactoring.guru/design-patterns/ composite / rapide /…


3 Réponses :


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

0 commentaires

1
votes

Si vous prévoyez d'utiliser des thèmes dans votre application, Apple propose le protocole UIApperance qui vous aide à modifier les propriétés des contrôles d'un certain type en même temps, en l'utilisant, vous aurez une apparence uniforme pour votre interface utilisateur. La façon d'utiliser est vraiment simple, pour changer toute la couleur d'arrière-plan de UILabel est comme ceci:

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

Si vous voulez tout gérer en un seul endroit comme dans votre échantillon code, vous pouvez créer une structure contenant les caractéristiques de votre interface utilisateur, vérifiez cette structure (j'ai utilisé le même nom que vous l'avez fait):

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

Dans l'AppDelegate, ou dès que possible, vous devez passer la référence UIWindow à la structure du gestionnaire de thèmes. Je l'ai fait dans AppDelegate didFinishLaunchingWithOptions . Ceci est nécessaire pour effectuer les changements de couleur immédiatement.

Avec cette structure définie, vous pouvez personnaliser n'importe quel contrôle de l'interface utilisateur comme vous le souhaitez. Par exemple, vous pouvez définir une certaine couleur d'arrière-plan pour UILabel et en avoir une autre si elle est contenue dans un UISegmentedControl.

L'événement de secousse que vous define peut basculer entre les thèmes comme celui-ci:

UILabel.apperance().backgroundColor = .lightGray

Si vous secouez l'appareil, les écrans basculeront entre ces deux (je n'ai changé que quelques propriétés):

Mode clair

Mode sombre

Si vous souhaitez jouer avec l'exemple de projet est disponible sur Github

J'espère que j'aide!


3 commentaires

Qu'en est-il lorsque je souhaite que certaines vues ne changent de couleur? Puis-je faire cela? Par exemple, j'ai plusieurs UIView s. Ce sont des couleurs différentes en mode clair, et également des couleurs différentes en mode sombre. Comment y parvenir?


Dans ce cas, ce n'est pas si simple, mais vous pouvez soit placer ces vues dans un conteneur d'une certaine classe, soit spécialiser ces vues dans une nouvelle classe vide qui hérite d'une classe de base. Par exemple: vous avez trois UILabel différents. Créez des classes vides qui héritent de UILabel comme UIRedLabel , UIGreenLabel et UIBlueLabel . Ensuite, dans votre gestionnaire de thèmes, vous définissez différentes propriétés pour ces classes, quelque chose comme: UIRedLabel.apperance (). TextColor = .red , UIGreenLabel.apperance (). TextColor = .green , etc.


J'ai ajouté mon dernier commentaire depuis mon téléphone et je n'ai pas vu votre solution. Joli! J'essaierai cet effet de flou quand je rentrerai à la maison ce soir.



1
votes

J'ai finalement compris, en utilisant NotificationCenter!

GEUserSettings

GEUserSettings ressemble maintenant à ce qui suit:

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

GEView

GEView est ma sous-classe personnalisée de UIView . Ceci est un remplacement au lieu de mon extension à UIView . Cela ressemble maintenant à ceci:

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

Mise à jour via motionBegan(_:with:)

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

Et le flou est supprimé et recréé, comme tel:

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

Cela ne nécessite même pas de quitter l'application ou de recharger le contrôleur de vue, cela se produit instantanément !

Extra (animations)

Si vous le souhaitez, vous pouvez également animer le changement de couleur en changeant la fonction updateBackgroundColor () :

XXX

Vous pouvez également animer le flou:

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


0 commentaires