IOS应用切换主题

Categories: 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


    }

UIAppearance.2019-04-22 13_15_49

可以看到直接设置window的tintColor是能马上提现的,但是通过UIAppearance设置的就不行,它只能在view放到window之前设置才有效。比如上面我们设置好了主题,切换TabController内部的UIViewController,设置好的NavigationBar的背景颜色就体现出来了。

UIAppearance它其实有一个大统一的思路,它把所有同一类型的UI都进行了统一管理,但是实际业务比这个复杂的多,所以用它来做切换皮肤的功能难度还是很大的。

SwiftTheme

后来在GitHub上找到了一个国人写的换肤框架,SwiftTheme。 先看效果: switch

它的使用:

//设置红色皮肤 读取Red.plist里面的配置的内容设置皮肤
ThemeManager.setTheme(plistName: "Red", path: .mainBundle)

这个demo里面Red.plist的内容: Screen Shot 2019-04-22 at 14.06.30

这些配置和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。

这种框架就是开发的时候要主题皮肤这边结合起来,能变化的地方一定要用主题的扩展方法来设置,但是确实是最灵活的,不管是自定义控件也好,还是业务上的一些特殊控件样式也好支持度很高。

不管怎么样还是要具体使用一段时间看看,先小规模试用一段时间,看看效果再说!