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 ...
随机推荐
- 《UNIX环境高级编程》(APUE) 笔记第四章 - 文件和目录
4 - 文件和目录 1. 函数 stat.fstat.fstatat 和 lstat #inlcude <sys/stat.h> int stat(const char *restrict ...
- P2629 【好消息,坏消息】
其实刚开始看到这道题,应该很多都会想到区间DP中的合并石子,开一个2倍的空间(严格来说的话应该是2n-1),将本来的环变成一个链式的结构.然后对于得到的消息,可以预处理一个前缀和,这样就可以很方便的知 ...
- 版本管理工具(git)
Git是一个开源的分布式版本控制系统 工作区: 电脑目录中,git_test文件夹就是一个工作区. 版本库: 在进行git操作的时候,会生成一个隐藏目录.git,这是git的版本库,其中stage(或 ...
- 重学 Java 设计模式:实战策略模式「模拟多种营销类型优惠券,折扣金额计算策略场景」
作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 文无第一,武无第二 不同方向但同样努力的人,都有自身的价值和亮 ...
- vscode F2无法使用
rope库可能存在bug 解决方法: "python.jediEnabled": false //自动补全用微软自带
- [网鼎杯 2020 青龙组]AreUSerialz
题目分析 <?php include("flag.php"); highlight_file(FILE); class FileHandler { protected $op ...
- scrapy shell 的使用
是什么?:是一个终端下的调试工具,用来调试scrapy 安装ipython :pip install ipython 启动: scrapy shell + 需要请求的url 进来之后,response ...
- css 浮动 定位
浮动 元素的浮动是指设置了浮动属性的元素会脱离标准普通 流的控制,移动到其父元素中指定位置的过程. 语法: float . left . right . none(默认) 注意: 1 ...
- Iphone上对于动态生成的html元素绑定点击事件$(document).click()失效解决办法
在Iphone上,新生成的DOM元素不支持$(document).click的绑定方法,该怎么办呢? 百度了N久都没找到解决办法,在快要走投无路之时,试了试Google,我去,还真找到了,歪国人就是牛 ...
- bzoj3620似乎在梦中见过的样子
bzoj3620似乎在梦中见过的样子 题意: 给出一个字符串,要求求出形如A+B+A的子串数量,且lenA≥k,lenB≥1.字符串长度≤15000,k≤100,所以字符长度为小写字母. 题解: 第一 ...