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).
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
.
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 } } }
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:
3 Réponses :
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 }
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):
Si vous souhaitez jouer avec l'exemple de projet est disponible sur Github
J'espère que j'aide!
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 code >, 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.
J'ai finalement compris, en utilisant NotificationCenter
!
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
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 } } }
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 !
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 } } }
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 /…