IOS应用切换主题
应用切换主题,或者叫换皮肤,这在现在的应用中还是蛮常见,还有一些比如节日主题的需求也是一样,都需要动态的切换颜色、背景、图片等内容,这种情况其实应用开发开始的时候规划蛮重要的,不然等到应用开发到一定程度再考虑往往工作量就会很大,修改的地方很多。
UIAppearance
开始考虑的时候发现官方有提供一个叫UIAppearance的代理类,说是处理全局统一管理IOS控件的样式的。这个代理方式的思路蛮好的,把官方提供的所有IOS控件都进行处理,可以统一设置各种控件自身的样式。而且代码没有侵入性。 比如:
// 设置主题色
UIApplication.shared.delegate?.window??.tintColor = theme.mainColor
// 设置NavigationBar的样式
UINavigationBar.appearance().barStyle = .black
// 设置TabBar背景
UITabBar.appearance().backgroundImage = UIImage(named: "barBackground")
...
所以有这个代理类,我们只要定义好主题,然后切换的时候设置一下就都变过来了。 如下定义主题:
enum Theme: Int {
case Red = 0
case Blue = 1
case Green = 2
//主题色
var mainColor: UIColor {
switch self {
case .Red:
return UIColor.red
case .Blue:
return UIColor.blue
case .Green:
return UIColor.green
}
}
...
}
切换主题:
static func applyTheme(theme: Theme) {
// 保存当前主题
UserDefaults.standard.set(theme.rawValue, forKey: SelectedThemeKey)
UserDefaults.standard.synchronize()
// 设置各个组件的主题色
let sharedApplication = UIApplication.shared
sharedApplication.delegate?.window??.tintColor = theme.mainColor
UIApplication.shared.delegate?.window??.tintColor = theme.mainColor
let navBar = UINavigationBar.appearance()
navBar.barStyle = theme.barStyle
navBar.tintColor = theme.barTextColor
navBar.barTintColor = theme.mainColor
let tabBar = UITabBar.appearance()
tabBar.barStyle = theme.barStyle
tabBar.barTintColor = theme.tabBarBackgroundColor
UISwitch.appearance().tintColor = theme.mainColor
}
可以看到直接设置window的tintColor是能马上提现的,但是通过UIAppearance设置的就不行,它只能在view放到window之前设置才有效。比如上面我们设置好了主题,切换TabController内部的UIViewController,设置好的NavigationBar的背景颜色就体现出来了。
UIAppearance它其实有一个大统一的思路,它把所有同一类型的UI都进行了统一管理,但是实际业务比这个复杂的多,所以用它来做切换皮肤的功能难度还是很大的。
SwiftTheme
后来在GitHub上找到了一个国人写的换肤框架,SwiftTheme。 先看效果:
它的使用:
//设置红色皮肤 读取Red.plist里面的配置的内容设置皮肤
ThemeManager.setTheme(plistName: "Red", path: .mainBundle)
这个demo里面Red.plist的内容:
这些配置和ThemeManager设置主题后,具体到每个UI控件它是怎么用的呢: 比如Demo中间那个圆形大按钮:
changeTheme.theme_setTitleColor("ChangeThemeCell.buttonTitleColorNormal", forState: .normal)
changeTheme.theme_setTitleColor("ChangeThemeCell.buttonTitleColorNormal", forState: .highlighted)
changeTheme.theme_backgroundColor = "ChangeThemeCell.buttonBackgroundColor"
changeTheme.layer.cornerRadius = changeTheme.bounds.size.width / 2
看这个Button应该是多个了几个扩展方法用来设置titleColor和backgroundColor,他们的值是一串定义的字符串就是plist文件中的key。 再去看SwiftTheme的源码,确实能找到对应的扩展:
@objc public extension UIButton
{
func theme_setImage(_ picker: ThemeImagePicker?, forState state: UIControl.State) {
let statePicker = makeStatePicker(self, "setImage:forState:", picker, state)
setThemePicker(self, "setImage:forState:", statePicker)
}
func theme_setBackgroundImage(_ picker: ThemeImagePicker?, forState state: UIControl.State) {
let statePicker = makeStatePicker(self, "setBackgroundImage:forState:", picker, state)
setThemePicker(self, "setBackgroundImage:forState:", statePicker)
}
func theme_setTitleColor(_ picker: ThemeColorPicker?, forState state: UIControl.State) {
let statePicker = makeStatePicker(self, "setTitleColor:forState:", picker, state)
setThemePicker(self, "setTitleColor:forState:", statePicker)
}
}
再翻翻这个换肤框架的源码,发现它的思路跟我以前做过的Android的那个换肤框架的思路是差不多的【android换肤】 。 就是把所有UI跟换肤相关的属性都进行管理,在展现的时候看是否有自定义主题的设置,如果有就把自定义主题里面配置的属性值设置进去,比如button的backgroundColor。
这种框架就是开发的时候要主题皮肤这边结合起来,能变化的地方一定要用主题的扩展方法来设置,但是确实是最灵活的,不管是自定义控件也好,还是业务上的一些特殊控件样式也好支持度很高。
不管怎么样还是要具体使用一段时间看看,先小规模试用一段时间,看看效果再说!