swift4 App切換主題的實現方法總結
宣告:本篇部落格的程式碼來自 @Finb 開發的 Swift" rel="nofollow,noindex" target="_blank">V2ex-Swift 裡的 V2exColor.swift,感謝大大開源這麼好用的app
先上圖
依賴
本DEMO依賴
- UIColor_Hex_Swift 將16進位制的顏色轉成swift認識的顏色,可以增加程式碼可讀性
- KVOController 對app裡鍵值監聽的一個庫
- SnapKit 程式碼設定佈局的工具
使用 pod install
安裝好
專案裡用到的小logo,可以去 https://material.io/tools/icons/?style=baseline 搜尋下載
基本程式碼
ViewTableViewCell.swift
import UIKit import SnapKit class ViewTableViewCell: UITableViewCell { var titleLabel: UILabel = { var label = UILabel() label.font = UIFont.systemFont(ofSize: 16) return label }() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) let bgView = UIView() self.selectedBackgroundView = bgView self.addSubview(titleLabel) self.titleLabel.snp.makeConstraints { (make) in make.centerY.equalTo(self.contentView) make.left.equalTo(20) } } func bind(_ title : String) { self.titleLabel.text = title } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
ViewController.swift
import UIKit import SnapKit class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { let menuButton = UIButton() let tableView = UITableView() let data = ["Swift", "SnapKit", "UIColor_Hex_Swift", "KVOController"] override func viewDidLoad() { super.viewDidLoad() self.title = "Demo"; menuButton.contentMode = .center menuButton.setImage(UIImage(named: "baseline_brightness_2_black_24pt"), for: UIControl.State.normal) self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: menuButton) menuButton.addTarget(self, action: #selector(ViewController.changeMode), for: .touchUpInside) self.view.addSubview(self.tableView) self.tableView.register(ViewTableViewCell.self, forCellReuseIdentifier: "cell") self.tableView.dataSource = self self.tableView.delegate = self self.tableView.snp.makeConstraints { (make) in make.edges.equalTo(0) } } @objc func changeMode() { // TODO } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return data.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ViewTableViewCell cell.bind(data[indexPath.row]) return cell } }
對 AppDelegate.swift
的啟動方法進行修改,加上 UINavigationController
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. self.window?.rootViewController = UINavigationController(rootViewController: ViewController()) return true }
這樣一個簡單的列表App就做好了
下面加上主題顏色
主題類
@Finb 大大將主題封裝成了一個類,直接拷貝就可以用的,下面是原始碼,關於最下面設定KVO的程式碼,我也看不懂,直接用就好 : )
import UIKit import KVOController import UIColor_Hex_Swift //使用協議 方便以後切換顏色配置檔案、或者做主題配色之類亂七八糟產品經理最愛的功能 protocol AppColorProtocol{ var backgroundColor: UIColor {get} var cellSelectionColor: UIColor {get} var menuButtonColor: UIColor {get} var titleTextColor: UIColor {get} } class DefaultColor: NSObject,AppColorProtocol { static let sharedInstance = DefaultColor() fileprivate override init(){ super.init() } var backgroundColor : UIColor{ get{ return UIColor.white } } var cellSelectionColor: UIColor { get { return UIColor("#f0f0f0") } } var menuButtonColor : UIColor{ get{ return UIColor.black } } var titleTextColor : UIColor{ get{ return UIColor.black } } } /// Dark Colors class DarkColor: NSObject,AppColorProtocol { static let sharedInstance = DarkColor() fileprivate override init(){ super.init() } var backgroundColor : UIColor{ get{ return UIColor.black } } var cellSelectionColor: UIColor { get { return UIColor("#222") } } var menuButtonColor : UIColor{ get{ return UIColor.white } } var titleTextColor : UIColor{ get{ return UIColor.white } } } class AppColor :NSObject{ fileprivate static let STYLE_KEY = "styleKey" static let AppColorStyleDefault = "Default" static let AppColorStyleDark = "Dark" fileprivate static var _colors:AppColorProtocol? static var colors: AppColorProtocol { get{ if let c = AppColor._colors { return c } else{ if AppColor.sharedInstance.style == AppColor.AppColorStyleDefault{ return DefaultColor.sharedInstance } else{ return DarkColor.sharedInstance } } } set{ AppColor._colors = newValue } } @objc dynamic var style:String static let sharedInstance = AppColor() fileprivate override init(){ if let style = UserDefaults.standard.string(forKey: AppColor.STYLE_KEY) { self.style = style } else{ self.style = AppColor.AppColorStyleDefault } super.init() } func setStyleAndSave(_ style:String){ if self.style == style { return } if style == AppColor.AppColorStyleDefault { AppColor.colors = DefaultColor.sharedInstance } else{ AppColor.colors = DarkColor.sharedInstance } self.style = style UserDefaults.standard.setValue(style, forKey: AppColor.STYLE_KEY) } } //MARK: - 主題更改時,自動執行 extension NSObject { fileprivate struct AssociatedKeys { static var themeChanged = "themeChanged" } /// 當前主題更改時、第一次設定時 自動呼叫的閉包 public typealias ThemeChangedClosure = @convention(block) (_ style:String) -> Void /// 自動呼叫的閉包 /// 設定時,會設定一個KVO監聽,當V2Style.style更改時、第一次賦值時 會自動呼叫這個閉包 var themeChangedHandler:ThemeChangedClosure? { get { let closureObject: AnyObject? = objc_getAssociatedObject(self, &AssociatedKeys.themeChanged) as AnyObject? guard closureObject != nil else{ return nil } let closure = unsafeBitCast(closureObject, to: ThemeChangedClosure.self) return closure } set{ guard let value = newValue else{ return } let dealObject: AnyObject = unsafeBitCast(value, to: AnyObject.self) objc_setAssociatedObject(self, &AssociatedKeys.themeChanged,dealObject,objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) //設定KVO監聽 self.kvoController.observe(AppColor.sharedInstance, keyPath: "style", options: [.initial,.new] , block: {[weak self] (nav, color, change) -> Void in self?.themeChangedHandler?(AppColor.sharedInstance.style) } ) } } }
專案內使用
使用很簡單,在需要更新主題的時候改變元件顏色的地方,實現 AppColor
裡的閉包函式 themeChangedHandler
就可以了
在 ViewController.swift
裡的 viewDidLoad()
方法里加上這個閉包的實現
override func viewDidLoad() { // ... self.themeChangedHandler = {[weak self] (style) -> Void in self?.view.backgroundColor = AppColor.colors.backgroundColor self?.tableView.backgroundColor = AppColor.colors.backgroundColor self?.menuButton.tintColor = AppColor.colors.menuButtonColor if AppColor.sharedInstance.style == AppColor.AppColorStyleDefault { self?.navigationController?.navigationBar.barStyle = .default } else { self?.navigationController?.navigationBar.barStyle = .black } } }
實現一下點選小圖示的事件
@objc func changeMode() { if AppColor.sharedInstance.style == AppColor.AppColorStyleDefault { AppColor.sharedInstance.setStyleAndSave(AppColor.AppColorStyleDark) } else { AppColor.sharedInstance.setStyleAndSave(AppColor.AppColorStyleDefault) } }
再次執行專案,點點看看,背景,navigationBar都已經可以換色了,但 TableViewCell 還不行,那就在 ViewTableViewCell.swift
裡也加上主題更新的閉包實現
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { // ... self.themeChangedHandler = {[weak self] (style) -> Void in self?.titleLabel.textColor = AppColor.colors.titleTextColor self?.backgroundColor = AppColor.colors.backgroundColor bgView.backgroundColor = AppColor.colors.cellSelectionColor } }
再執行就沒問題了
其它
我在開發 CNodeJS-Swift 專案的時候用到了 XLPagerTabStrip ,這貨的顏色設定都是寫在 super.viewDidLoad()
方法上面才能生效,這導致了我在更新主題的時候執行閉包函式沒效果
解決方法:直接把原始碼下載下來,在程式裡改的 :joy
另外,給大家再推薦一個主題更新的開源庫,小專案還是比較好用的 NightNight
原文連結: