jumbotron

格式塔

Gestalt 是一个非侵入式轻量级的框架,用于应用主题化,并支持动画主题切换

screencast

用法

假设你想为一个带有单个标签的视图控制器设置主题

import Gestalt

struct Theme: Gestalt.Theme {
    let view: ViewTheme = .init()

    static let light: Theme = .init(view: .light)
    static let dark: Theme = .init(view: .dark)
}

struct ViewTheme: Gestalt.Theme {
    let font = UIFont.preferredFont(forTextStyle: .headline)
    let color: UIColor
    let backgroundColor: UIColor

    static let light: Theme = .init(
        color: UIColor.black
        backgroundColor: UIColor.white
    )

    static let dark: Theme = .init(
        color: UIColor.white
        backgroundColor: UIColor.black
    )
}

// In `AppDelegate.application(_:didFinishLaunchingWithOptions:)`
// assign a default theme (or user's choice from user defaults):
ThemeManager.default.theme = Theme.light

class ViewController: UIViewController {
    @IBOutlet var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.observe(theme: \Theme.view)
    }
}

extension ViewController: Themeable {

    typealias Theme = ViewTheme

    func apply(theme: Theme) {
        self.view.backgroundColor = theme.backgroundColor
        self.label.textColor = theme.color
        self.label.font = theme.font
    }
}

调用 self.observe(theme: \Theme.view) 会将接收器注册到 ThemeManager.default 以进行主题观察,以便在未来主题更改时接收通知,并立即调用一次。 首次调用没有动画效果,但之后的所有更改都将具有动画效果。

要更改当前主题(即使在应用程序运行时),只需将不同的主题分配给您正在使用的 ThemeManager

ThemeManager.default.theme = Theme.dark

这将导致给定 ThemeManager 上所有先前注册的闭包再次被调用。

请参阅 GestaltDemo 目标以获取更真实/详细的用法示例。

注意

  1. 通常情况下,使用 ThemeManager.default 就足够了。 但是,也可以通过 let manager = ThemeManager() 创建专用的 ThemeManager

在 App Extensions 中的用法

在视图已经加载后使用外观代理时,此库使用了一种技巧,即从主窗口中移除并重新添加应用程序的根视图以激活代理。 这在 App Extensions 中是不可能的,例如 Today 小部件,因为扩展安全 API 限制了对主窗口的访问。 因此,要在 App Extensions 中使用此库,您需要在设置主题后,通过在根视图控制器中添加类似这样的代码来手动触发根视图的重新加载。

ThemeManager.default.observe(theme: Theme.self) { [weak self] _ in
        if let strongSelf = self, let superview = strongSelf.view.superview {
            strongSelf.view.removeFromSuperview()
            superview.addSubview(strongSelf.view)
        }
    }

重要提示

  1. func apply(theme: Theme) 的主体应该是 幂等 的,以避免在重复调用时产生不必要的副作用。

安装

推荐将 Gestalt 添加到项目的方式是通过 Carthage

github 'regexident/Gestalt' ~> 2.0.0

或者通过 Cocoapods

pod 'Gestalt', '~> 2.0.0'

或者通过 Swift Package Manager

let package = Package(
    name: "GestaltDemo",
    dependencies: [
        .package(url: "https://github.com/regexident/Gestalt.git", from: "2.0.0")
    ],
    targets: [
        .target(name: "GestaltDemo", dependencies: [ "Gestalt" ])
    ]
)

许可证

GestaltMPL-2.0 许可证下可用。 有关更多信息,请参阅 LICENSE 文件。