ios - 尝试重新加载 View Controller 以更新当前主题

标签 ios swift uiviewcontroller xcode-storyboard

我在找什么

我正在尝试重新加载我的 View Controller 中的所有 View ,以在主题之间切换(类似于 TwitterApple Maps 所做的)。

Twitter Apple Maps


我如何设置不同的主题

我有这样的主题 View 设置:

@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) 或任何算作崩溃的东西),或者在使用该应用程序时重新加载它。

我尝试关闭然后重新加载 View Controller ,如上面的代码所示。我正在重新加载的那个显示在基本 View Controller 的顶部。

由于我使用的是 Storyboard ,我怎样才能完成这项工作?

编辑 - 添加了我的亮/暗模式的图像以使我的问题更清楚:

Light/dark modes

最佳答案

如果你打算在你的应用程序中使用主题,Apple 提供了 UIApperance 协议(protocol),它可以帮助你同时更改某种控件的属性,使用它你将拥有统一的外观你的用户界面。使用方法非常简单,改变所有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

希望能帮到你!

关于ios - 尝试重新加载 View Controller 以更新当前主题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55697172/

相关文章:

ios - MKAnnotationView 框架的初始化程序

ios - 调用另一个 View Controller 的 func 来控制 alpha 返回 nil 错误

ios - 如何使用 Storyboard在另一个 Controller 中 subview UITableViewController

ios - UITableViewController 作为 subview 获取错误

ios - WKWebview的container content height如何调整到webview的内容?

ios - 沿途发送的多点连接和数据

ios - 如何通过特定的力量旋转 Sprite 节点,例如指尖陀螺

ios - Swift 4 backgroundColor 错误?

ios - 更改 UIDatePicker 所选日期文本颜色以实现深色模式 (iOS 13)

ios - 缺少 iOS 分发签名身份/"Certificate has an invalid issuer"