iOS 高效灵活地配置可复用视图组件的主题
本文首发于 Ficow Shen's Blog,原文地址: iOS 高效灵活地配置可复用视图组件的主题。
内容概览
- 前言
- 如何配置主题?
- 如何更高效地配置主题?
- 面向协议/接口的方案
前言
在开发可视化应用的过程中,配置控件的样式是最常见的工作内容。请问读者是否遇到过这样的需求:在多个项目中复用多种可视化控件,而且这些控件可以配置颜色、字体等可视化元素?
本文主要针对控件数量较大,而且需要配置的控件属性较多的这种需求对主题配置方案进行探索,希望能够给读者带来些许启发。
如何配置主题?
大家最熟悉的方式就是给控件添加 控制样式的属性,然后 让调用方去设置控件的样式属性 以实现自定义样式的需求。
public final class ReusableComponent: UIView {
private let titleLabel = UILabel()
// 暴露一个颜色配置属性,供调用方更改文本颜色
public var titleColor: UIColor = .darkGray {
didSet {
titleLabel.textColor = titleColor
}
}
}
let component = ReusableComponent()
component.titleColor = .red
在控件数量较少、样式属性也较少的情况下,直接设置样式属性的方式是非常简单高效的。
如果控件数量大、样式属性较多、使用范围广甚至需要在多个项目中使用时,如何实现简单高效的样式配置呢?请看以下示例代码,并思考这个问题。
public final class ReusableComponent: UIView {
private let titleLabel = UILabel()
private let descriptionLabel = UILabel()
private let confirmButton = UIButton()
public var titleColor: UIColor = .darkGray {
didSet {
titleLabel.textColor = titleColor
}
}
public var titleFont: UIFont = .systemFont(ofSize: 20) {
didSet {
titleLabel.font = titleFont
}
}
public var descriptionColor: UIColor = .gray {
didSet {
descriptionLabel.textColor = descriptionColor
}
}
public var descriptionFont: UIFont = .systemFont(ofSize: 14) {
didSet {
descriptionLabel.font = descriptionFont
}
}
public var confirmTitleColor: UIColor = .darkGray {
didSet {
confirmButton.setTitleColor(confirmTitleColor, for: .normal)
}
}
public var confirmTitleFont: UIFont = .systemFont(ofSize: 16) {
didSet {
confirmButton.titleLabel?.font = confirmTitleFont
}
}
}
let component = ReusableComponent()
component.titleColor = .black
component.titleFont = .systemFont(ofSize: 19)
component.descriptionColor = .lightGray
component.descriptionFont = .systemFont(ofSize: 13)
component.confirmTitleColor = .black
component.confirmTitleFont = .systemFont(ofSize: 15)
请看上面的示例代码,这里仅仅配置几个样式属性就已经需要写很多行代码。如果需要大面积修改这种配置,我们很容易就漏掉某个属性。怎么办?
public final class ReusableComponent: UIView {
public struct Theme {
let titleColor: UIColor
let titleFont: UIFont
let descriptionColor: UIColor
let descriptionFont: UIFont
let confirmTitleColor: UIColor
let confirmTitleFont: UIFont
}
public static let defaultTheme = Theme(titleColor: .darkGray,
titleFont: .systemFont(ofSize: 20),
descriptionColor: .gray,
descriptionFont: .systemFont(ofSize: 14),
confirmTitleColor: .darkGray,
confirmTitleFont: .systemFont(ofSize: 16))
public var theme: Theme = defaultTheme {
didSet {
titleLabel.textColor = theme.titleColor
titleLabel.font = theme.titleFont
descriptionLabel.textColor = theme.descriptionColor
descriptionLabel.font = theme.descriptionFont
confirmButton.setTitleColor(theme.confirmTitleColor, for: .normal)
confirmButton.titleLabel?.font = theme.confirmTitleFont
}
}
private let titleLabel = UILabel()
private let descriptionLabel = UILabel()
private let confirmButton = UIButton()
}
let component = ReusableComponent()
let theme = ReusableComponent.Theme(titleColor: .black,
titleFont: .systemFont(ofSize: 19),
descriptionColor: .lightGray,
descriptionFont: .systemFont(ofSize: 13),
confirmTitleColor: .black,
confirmTitleFont: .systemFont(ofSize: 15))
component.theme = theme
为控件定义一个主题类型并定义一个主题属性,调用方不用担心漏掉某个配置项。而且,调用方甚至可以定义一个全局的主题对象,在需要使用的时候直接赋值即可。
但是,我们依然要为每一个控件实例进行样式配置。您可以设想一下,如果您需要对 ReusableComponent1, ReusableComponent2, ... , ReusableComponentN 这些控件进行主题配置,您就需要定义超级多的主题类型。 而且,调用方需要确切知晓控件里面的主题类型,然后在配置主题的时候去初始化一个主题类型的实例并传给控件实例。
那么,有没有什么办法更简单、灵活、高效呢?
如何更高效地配置主题?
每次用到控件都去指定主题的方式极其低效,我们先要想方设法优化这个问题。How?
public final class ReusableComponent: UIView {
// ...
public static var theme: Theme = defaultTheme
public var theme: Theme = ReusableComponent.theme {
didSet {
titleLabel.textColor = theme.titleColor
titleLabel.font = theme.titleFont
descriptionLabel.textColor = theme.descriptionColor
descriptionLabel.font = theme.descriptionFont
confirmButton.setTitleColor(theme.confirmTitleColor, for: .normal)
confirmButton.titleLabel?.font = theme.confirmTitleFont
}
}
// ...
}
ReusableComponent.theme = ReusableComponent.Theme(titleColor: .black,
titleFont: .systemFont(ofSize: 19),
descriptionColor: .lightGray,
descriptionFont: .systemFont(ofSize: 13),
confirmTitleColor: .black,
confirmTitleFont: .systemFont(ofSize: 15))
let component = ReusableComponent()
print(component.theme)
一般来说,应用内使用的控件的主题风格都是统一的。所以,更多的实际场景是我们需要对控件类型进行统一的样式配置。
在ReusableComponent类型上增加一个静态变量,这样只需要在使用控件前,对控件进行统一配置即可。如果稍后需要对某个控件实例进行定制,只需要修改控件实例的theme属性即可。这解决了配置效率低下的问题。
如果控件是定义在一个公用库里面,有多个项目需要用到库中的控件,那么直接暴露控件内部定义的主题类型给调用方将是一件非常不妙的事情。我们应该尽可能少地暴露公用库中的内容,以达到高度的封装效果。这样,以后可能会发生的内部变动就不担心会受到下游调用方的约束。
那么,怎么封装呢?
面向协议/接口的方案
如果您长期使用Swift开发语言,面向协议编程的概念您一定听说过。灵魂拷问又来了,究竟怎样的编程方式才是面向协议编程呢?
public protocol ReusableComponentTheme {
var titleColor: UIColor { get }
var titleFont: UIFont { get }
var descriptionColor: UIColor { get }
var descriptionFont: UIFont { get }
var confirmTitleColor: UIColor { get }
var confirmTitleFont: UIFont { get }
}
public final class ReusableComponent: UIView {
struct Theme: ReusableComponentTheme {
var titleColor: UIColor { .darkGray }
var titleFont: UIFont { .systemFont(ofSize: 20) }
var descriptionColor: UIColor { .gray }
var descriptionFont: UIFont { .systemFont(ofSize: 14) }
var confirmTitleColor: UIColor { .darkGray }
var confirmTitleFont: UIFont { .systemFont(ofSize: 16) }
}
public static var theme: ReusableComponentTheme = Theme()
public var theme: ReusableComponentTheme = ReusableComponent.theme {
didSet {
titleLabel.textColor = theme.titleColor
titleLabel.font = theme.titleFont
descriptionLabel.textColor = theme.descriptionColor
descriptionLabel.font = theme.descriptionFont
confirmButton.setTitleColor(theme.confirmTitleColor, for: .normal)
confirmButton.titleLabel?.font = theme.confirmTitleFont
}
}
private let titleLabel = UILabel()
private let descriptionLabel = UILabel()
private let confirmButton = UIButton()
}
struct CustomReusableComponentTheme: ReusableComponentTheme {
var titleColor: UIColor { .black }
var titleFont: UIFont { .systemFont(ofSize: 19) }
var descriptionColor: UIColor { .lightGray }
var descriptionFont: UIFont { .systemFont(ofSize: 13) }
var confirmTitleColor: UIColor { .black }
var confirmTitleFont: UIFont { .systemFont(ofSize: 15) }
}
ReusableComponent.theme = CustomReusableComponentTheme()
let component = ReusableComponent()
print(component.theme)
针对控件的主题定义一个协议,然后让主题类型去遵循这个协议。调用方不再知晓控件内部的主题类型,控件内部后续的变动不会导致调用方的编译错误,这样也就实现了调用链上下游的解耦。
如果以后需要对控件内部的样式进行调整,您可以定义新的协议来满足新的需求,而不是去修改旧的协议。这种变更方式与后端接口支持不同版本类似,也比较灵活。
以上就是本文的全部内容,希望对您有所启发!
iOS 高效灵活地配置可复用视图组件的主题的更多相关文章
- APPKIT打造稳定、灵活、高效的运营配置平台
一.背景 美团App.大众点评App都是重运营的应用.对于App里运营资源.基础配置,需要根据城市.版本.平台.渠道等不同的维度进行运营管理.如何在版本快速迭代过程中,保持运营资源能够被高效.稳定和灵 ...
- iOS高效调试
写代码难免出现bug. 储备些调试技能绝对能够提高你的工作效率,让bug无所遁形.下面就和大家分享一些我在工作中常用的iOS调试小技能. 1. 打印 最简单,基础的调试方法就是打印日志了.贴出两段封装 ...
- SAP CRM 复用视图
在设计任何视图或组件的时候,我们需要以可复用的方式来设计它.UI组件设计的主要目标即可复用. 例如:几乎每个事务都要处理合作伙伴(客户).如果我们想要在Web UI显示那些合作伙伴,需要设计一个视图. ...
- Box(视图组件)如何在多个页面不同视觉规范下的复用
本文来自 网易云社区 . 问题描述 Android App中的页面元素,都是由一个个Box(可以理解成一个个自定义View组件和Widget同级)组成,这些Box可以在不同的页面.不同的模块达到复用的 ...
- 百度DMLC分布式深度机器学习开源项目(简称“深盟”)上线了如xgboost(速度快效果好的Boosting模型)、CXXNET(极致的C++深度学习库)、Minerva(高效灵活的并行深度学习引擎)以及Parameter Server(一小时训练600T数据)等产品,在语音识别、OCR识别、人脸识别以及计算效率提升上发布了多个成熟产品。
百度为何开源深度机器学习平台? 有一系列领先优势的百度却选择开源其深度机器学习平台,为何交底自己的核心技术?深思之下,却是在面对业界无奈时的远见之举. 5月20日,百度在github上开源了其 ...
- 一些iOS高效开源类库
因为iOS SDK相对比较底层,所以开发者就得受累多做一些体力活.不过幸运的是,有很多第三方的类库可以用来简化很多不必要的工作.笔者整理了一下在本人学习过程中用到的一些比较有用Objective-C开 ...
- 灵活QinQ配置
华为交换机灵活QinQ配置列子 配置vlan2 为内层vlan vlan100 为外层vlan #用户端 Gi // qinq vlan-translation enable port hybrid ...
- iOS开发:使用Tab Bar切换视图
iOS开发:使用Tab Bar切换视图 上一篇文章提到了多视图程序中各个视图之间的切换,用的Tool Bar,说白了还是根据触发事件使用代码改变Root View Controller中的Conten ...
- Xamarin iOS教程之进度条和滚动视图
Xamarin iOS教程之进度条和滚动视图 Xamarin iOS 进度条 进度条可以看到每一项任务现在的状态.例如在下载的应用程序中有进度条,用户可以很方便的看到当前程序下载了多少,还剩下多少.Q ...
随机推荐
- VScode和IntelliJ IDEA设置自动换行
VScode自动换行 点击左上角的File-->Auto Save即可实现多文件的自动换行; IDEA自动换行 点击左侧空白处,选择Soft-Wrap就是当前文件自动换行,选择Configure ...
- 大多数人可能都不会使用socketTimeout,看了底层才知道一直都做错了
前几天一个机房网络抖动,引发了很多对外请求的超时问题,在发生问题排查日志的时候,发现了这么一个现象,httpclient我们的请求超时时间并没有按照我们的设置报超时异常 我们的大概配置如下: Requ ...
- 一次运维-堡垒机多次跳转导出及导入mysql数据库
1. 场景描述 记录一次运维,朋友公司要从线上环境同步数据到测试环境,因为公司监管问题,导致数据无法从线上获取,需要通过vpn,堡垒机,3次跳转到目标主机,通过命令导出mysql数据文件,然后再将数据 ...
- css样式冲突怎么解决?
一个网页有时会使用两种CSS,那发生css样式冲突要怎么解决?下面本篇文章就来给大家介绍一下发生css样式冲突的解决方法,希望对大家有所帮助. css冲突怎么解决? 解决方法有很多,如果可以对html ...
- 一起来看 HTML 5.2 中新的原生元素 <dialog>
不到一个月前,HTML 5.2 正式成为 W3C 的推荐标准(REC),其中,推出了一个新的原生模态对话框元素 <dialog>,乍一看,可能感觉它就是一个新增的元素,然而,作者最近在玩的 ...
- Blazor带我重玩前端(三)
写在前面 需要升级VS2019以及.NET Core到最新版(具体的最低支持,我已经忘了,总是越新支持的就越好),以更好的支持自己开发Blazor项目. WebAssembly 搜索Blazor模板 ...
- day45 如何完全删除mysql服务
卸载mysql之后,mysql的服务无法删除 解决方案 在我们在卸载mysql后会有一些东西没有删除干净,当我们把这些内容清除干净后,服务自然就消失了 步骤一: 如果是默认安装的话 在这三个文件内都有 ...
- DVWA学习记录 PartⅠ
DVWA介绍 DVWA(Damn Vulnerable Web Application)是一个用来进行安全脆弱性鉴定的PHP/MySQL Web应用,旨在为安全专业人员测试自己的专业技能和工具提供合法 ...
- mongodb(二):数据库安装,部署(linux)
1.下载安装包 wget http://fastdl.mongodb.org/linux/mongodb-linux-i686-1.8.2.tgz 下载完成后解压缩压缩包 tar zxf mongod ...
- 数据可视化之powerBI入门(七)数据清洗中最常使用的十三招
https://mp.weixin.qq.com/s?__biz=MzA4MzQwMjY4MA==&mid=2484067158&idx=1&sn=4ad955112df2f4 ...