MarkupKit 是一个用于简化 iOS 应用程序开发的开源框架。它允许开发人员使用人类可读的标记语言以声明方式构建用户界面,而不是通过代码编程或使用 Interface Builder 等可视化建模工具以交互方式构建用户界面。
在标记中构建界面可以轻松地可视化结果输出并识别修订之间的差异。由于 HTML 和万维网的无处不在,它也是许多开发人员都喜欢的比喻。
本文介绍了 MarkupKit 框架并概述了它的一些主要功能。
文档结构
MarkupKit 使用 XML 来定义用户界面的结构。在 MarkupKit 文档中,XML 元素表示
UIView
子类的实例,而 XML 属性通常表示与这些视图关联的属性。例如,以下标记声明了
UILabel
的实例并将其
text
属性的值设置为“Hello, World!”:
<UILabel text="Hello, World!"/>
此标记产生的输出与以下代码的输出相同:
<UILabel text="Hello, World!"/>
MarkupKit 使用键值编码 (KVC) 来设置属性值。属性的名称表示正在设置的属性的路径(例如前面示例中显示的“文本”属性)。但是,因为属性名代表一个关键路径,属性也可以用来设置嵌套属性的值。例如,以下标记创建了一个
UITableViewCell
实例,其文本标签的
text
属性设置为“这是一个表格行”:
<UILabel text="Hello, World!"/>
字符串、数字和布尔属性的类型转换由 KVC 自动处理。其他类型,例如枚举、颜色、字体和图像,由 MarkupKit 专门处理。例如,以下标记将上一示例中的文本字体设置为 24 磅 Helvetica Bold,并将文本颜色设置为红色。在设置属性值之前,MarkupKit 将字体名称和十六进制编码的颜色值分别转换为
UIFont
和
UIColor
实例:
<UILabel text="Hello, World!"/>
本土化
如果属性的值以“@”开头,MarkupKit 会在设置属性之前尝试查找该值的本地化版本。例如,如果应用程序在 Localizable.strings 中定义了如下本地化问候语:
<UILabel text="Hello, World!"/>
以下标记将生成一个
UILabel
实例,其
text
属性的值设置为“Hello, World!”:
<UILabel text="Hello, World!"/>
除了
Localizable.strings
中定义的全局值外,
strings
处理指令 (PI) 还可用于定义一组仅对当前文档可见的局部字符串值。这有助于简化本地化资源的管理,因为它们可以拆分为许多较小的特定于上下文的部分,而不是一个大型的应用程序范围的字符串值集合。
保留属性
有些属性在 MarkupKit 中有特殊含义,不代表视图属性。这些包括 style 、 class 和 id 。 “样式”属性用于调用“工厂方法”来构造视图实例。 “class”属性指定一组应用于实例化视图的模板属性,类似于 CSS 中的样式。 “id”属性用于定义视图的“出口”,类似于 Xcode 中的出口。
此外,保留名称以“on”开头的属性。除了名称以“on”开头的现有属性外,这些属性代表事件处理程序或“动作”,类似于 Xcode 中的动作。
下面将更详细地讨论每种保留的属性类型。
工厂方法
“样式”属性用于创建特定类型或“样式”的视图实例。大多数 UIKit 类型都可以通过调用类型的新方法来实例化;但是,某些类型需要使用特殊的构造方法。例如,
UIButton
实例是使用
UIButton
类的
buttonWithType:
方法创建的,而
UITableView
实例是通过
initWithFrame:style
初始化的,而不是通过
new
调用的无参数
init
方法。
为了处理这些情况,MarkupKit 支持一个名为“style”的特殊属性。此属性的值表示“工厂方法”的名称,这是一种产生给定类型实例的零参数方法。 MarkupKit 将工厂方法添加到
UIButton
和
UITableView
等类中,使这些类型能够在标记中构造。
例如,以下标记创建了一个“系统风格”的
UIButton
实例:
<UILabel text="Hello, World!"/>
请注意用于设置按钮标题的属性名称。标准的
UIButton
类没有定义一个名为
normalTitle
的属性,而是定义了一个
setTitle:forState:
方法。但是,此类方法不符合 KVC,不能通过标记直接调用。 MarkupKit 向
UIButton
和其他类似的类添加了许多属性,这些类委托给这些方法并允许在标记中配置它们各自的类型。
模板属性
通常,在构建用户界面时,同一组属性值会重复应用于给定类型的实例。例如,应用程序设计者可能希望所有按钮都具有相似的外观。虽然可以简单地复制每个按钮实例的属性定义,但这是重复的,并且不允许以后轻松修改设计 - 每个按钮实例必须单独定位和修改,这可能既耗时又容易出错.
MarkupKit 允许开发人员将通用属性定义集抽象为“模板”,然后可以按名称将其应用于类实例。这使得分配公共属性值以及稍后修改它们变得更加容易。
属性模板在属性列表(或 .plist )文件中定义。每个模板都由属性列表顶层定义的字典表示。字典的内容表示将在应用模板时设置的属性值。
使用属性处理指令 (PI) 将模板添加到 MarkupKit 文档。例如下面的PI将 MyStyles.plist 定义的所有模板导入到当前文档中:
<UILabel text="Hello, World!"/>
使用保留的“类”属性将模板应用于类实例。该属性的值是指由属性列表定义的模板的名称。模板定义的所有属性值都应用于类实例。属性模板支持嵌套属性,例如“titleLabel.font”。
例如,如果 MyStyles.plist 定义了一个名为“label.greeting”的字典,其中包含以下值(为清楚起见缩写):
<UILabel text="Hello, World!"/>
以下标记将生成一个标签,上面写着“Hello, World!”在 24 点 Helvetica 中,文本水平居中:
<UILabel text="Hello, World!"/>
可以在单个文档中指定多个属性 PI。它们的内容被合并到文档可用的单个模板集合中。如果同一个模板由多个属性列表定义,模板的内容将合并到一个模板中,并且最近定义的值优先。这允许标记文档使用本地值“覆盖”更多全局定义的样式。
奥特莱斯
标记中定义的视图本身并不是特别有用。保留的“id”属性可用于为视图实例命名。为视图分配一个 ID 为视图定义了一个“出口”,并使其可供调用代码访问。使用 KVC,MarkupKit 将命名视图实例“注入”到文档的所有者(通常是根视图的视图控制器或根视图本身),允许应用程序代码与其交互。
例如,以下标记声明了一个包含
UITextField
的
LMTableView
实例。
LMTableView
和
LMTableViewCell
分别是 MarkupKit 提供的
UITableView
和
UITableViewCell
的子类,它们简化了静态表视图内容的定义:
<UILabel text="Hello, World!"/>
文本字段分配有“textField”的 ID。与 Interface Builder 一样,拥有类可能会像这样在 Objective-C 中为文本字段声明一个出口:
<UILabel text="Hello, World!"/>
或者在 Swift 中,像这样:
<UILabel text="Hello, World!"/>
在任何一种情况下,当文档被加载时,outlet 将填充文本字段实例,并且应用程序可以与其交互,就像它是以编程方式创建的一样。
行动
大多数重要的应用程序需要以某种方式响应用户交互。 UIKit 控件(
UIControl
类的子类)会在发生此类交互时触发通知应用程序的事件。例如,当点击按钮实例时,
UIButton
类会触发
UIControlEventTouchUpInside
事件。
虽然应用程序可以使用插座以编程方式注册事件,但 MarkupKit 提供了一种更方便的替代方法。名称以“on”开头(但不等于名称等于或以“on”开头的现有属性的名称)的属性被视为控制事件。该属性的值表示触发事件时将触发的操作的名称。属性的名称只是“on”前缀后跟事件名称,减去“UIControlEvent”前缀。
例如,以下标记创建了一个系统样式的
UIButton
,它在点击按钮时调用文档所有者的
handleButtonTouchUpInside:
方法:
<UILabel text="Hello, World!"/>
例子
以下标记提供了一个简单示例,其中包含许多先前讨论的功能:
<UILabel text="Hello, World!"/>
XML 声明之后的第一行导入了一个名为 Styles.plist 的属性列表,其定义如下(同样,为清楚起见缩写):
<UILabel text="Hello, World!"/>
这些属性用于设置用于向用户显示问候语的标签的属性(标记文档中名为“greetingLabel”的
UILabel
)。该标签将使用粗体、24 号系统字体,并使用浅绿色显示居中文本。
文档的根元素是
LMColumnView
的一个实例,它是
UIView
的一个子类,它自动将其子视图排列在一条垂直线上。它是 MarkupKit 提供的几个类之一,有助于简化自动适应设备方向或内容更改的应用程序的开发。除了
LMColumnView
之外,MarkupKit 还提供了
LMRowView
和
LMLayerView
,前者将子视图垂直排列,后者将子视图分层排列,就像一堆透明胶片。
文档接下来声明一个行视图,其中包含一个允许用户输入名称的
UITextField
。文本字段被分配了一个 ID“nameField”,它在视图的控制器中为该字段创建了一个出口。名称文本字段和问候语标签的出口在控制器中声明如下:
<UILabel text="Hello, World!"/>
文本字段的“占位符”属性指定为“@name”。这是对应用程序的 Localizable.strings 文件中“名称”键的引用:
<UILabel text="Hello, World!"/>
加载文档时,此值将替换为键的实际字符串值(即“名称”)。
接下来,该文档声明了一个系统样式
UIButton
的实例,该实例具有解析为“Say Hello”的正常状态标题。点击此按钮会调用按钮的
UIControlEventTouchUpInside
事件的操作处理程序,该事件使用用户在名称字段中输入的文本设置问候语标签的值:
<UILabel text="Hello, World!"/>
最后,文档声明了
LMSpacer
的一个实例。 Spacer 是空视图,可以增长以填充父视图中的任何剩余空间。在列视图的末尾放置一个间隔符可确保列的内容与列的顶部对齐。
运行该应用程序会产生类似于以下内容的输出:
概括
本文介绍了 MarkupKit 框架并概述了它的一些主要功能。访问 GitHub 上的 MarkupKit 项目了解更多信息。