尝试重新加载视图控制器以更新当前主题

时间:2019-04-15 21:01:13

标签: ios swift uiviewcontroller xcode-storyboard

我在寻找什么

我试图重新加载视图控制器中的所有视图,以在主题之间进行更改(类似于TwitterApple 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中设置lightdark主题背景颜色,具体取决于当前主题。我的背景模糊效果被排除在外,因为我找不到代码中更新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 个答案:

答案 0 :(得分:1)

如果要在应用程序中使用主题,Apple提供了UIApperance协议,该协议可帮助您同时更改某种控件的属性,使用该协议,您的UI外观将统一。使用方法非常简单,即可更改所有UILabel背景颜色,如下所示:

UILabel.apperance().backgroundColor = .lightGray

如果要像在示例代码中那样在一个地方管理所有内容,可以创建一个包含UI特性的结构,请检查此结构(我使用的名称与您相同):

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中做到了。为了立即更改颜色,这是必需的。

使用此结构定义后,您可以根据需要自定义任何UI控件。例如,您可以为UILabel定义一种特定的背景色,如果包含在UISegmentedControl中,则可以使用另一种背景色。

您定义的震动事件可以在这样的主题之间切换:

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

如果摇晃设备,屏幕将在这两个屏幕之间切换(我仅更改了一些属性):

Light mode

Dark mode

如果您想玩示例项目,请访问Github

希望我能帮忙!

答案 1 :(得分:0)

    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中覆盖该方法,就可以在该函数中覆盖UI更新逻辑,当设备晃动时,调用覆盖的方法,如下所示

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
}

注意: 您必须根据需要更新颜色,其中包含一些无法用于您的情况的颜色

答案 2 :(得分:0)

我终于用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)
}

这甚至不需要退出应用程序或重新加载视图控制器,它会立即发生!

额外(动画)

如果愿意,还可以通过更改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
    })
}