TJBinder是一个轻薄但仍然强大的iOS实现,它是基于OS-X中Cocoa绑定技术的模型-视图绑定科技。其目标是相同的:创建一种“提供一种不编写大量胶水代码也能同步模型和视图值的技术”。
本节以某种叙述风格解释为什么和何时使用TJBinder
对您有用。如果您想直接跳到细节,请继续阅读下一节。
假设您必须编写另一个基于MVC的简单表视图--详细信息视图应用程序。如果您以前从未尝试过,我建议您阅读Ray Wenderlich的绝佳教程。当您第二次或第三次编写这种应用程序时,您肯定会注意到您需要编写的样板代码和类似动作的数量。
UITableViewController
子类,在您查找数据模型对象数组的或获取结果控制器。UITableViewCell
子类,创建IBOutlet @properties并连接它们与Storyboard/xib元素。UITableViewDataSource
实现的方法中请求时,您必须使用适当的数据模型对象配置单元格。现在您的表视图正在运行,用户点击单元格,您想为选定的数据模型对象显示一个详细信息视图。您需要做什么?您需要通过非常相似的过程进行:创建一个UIViewController
子类,在IB中设计其布局,创建出口,连接它们,编写根据模型数据的视图控制器UI元素配置的代码。
如果您的数据对象在运行过程中会发生变化,情况会变得更加严重,例如您想要显示一个时钟或设备的GPS坐标。那么您必须实现事件监听器,很可能通过某种代理协议,并每次您注意到数据对象发生变化时都更新视图对象的新值。
但直到今天,它仅限于OS-X开发者可用。它被称为Cocoa绑定。Cocoa绑定是一个非常强大的技术,您可以直接在Interface Builder中说“我想将我的标签的text
属性直接绑定到包含在我表格视图中单元格内的数据模型对象的albumName
属性。在运行时,框架会自动处理更新标签的数据模型。通过不执行上一列表中的步骤4和5,您可以节省大量时间:子类化表格视图单元格,为您的标签和其他元素创建@properties
,通过Outlets与IB元素连接,并编写更新代码。iOS开发者们一直羡慕的技术,但现在TJBinder
允许您做到完全相同的事情。
安装TJBinder
的最佳方式是通过CocoaPods。您可以在您的Podfile
中添加对TJBinder的依赖关系。
pod 'TJBinder'
如果您不希望使用CocoaPods,您可以直接从TJBinder
文件夹中下载所有文件并将它们添加到您的项目中。
TJBinder
允许您使用您喜欢的日志记录器。目前支持没有日志、NSLog
和CocoaLumberjack。
与其他库不同,使用TJBinder
不需要您编写一行新的代码。相反,您可能需要删除旧代码的一些部分,因为TJBinder
将完成这项工作而不是您。
假设您有以下数据模型
// Fruit.h
@interface Fruit : NSObject
@property (strong) NSString* name;
@property (strong) UIColor* color;
@end
// Fruit.m
@implementation Fruit
@end
您想要编写一个视图控制器,其中有一个UILabel
,该标签以与水果相同颜色显示水果的名称。使用TJBinder
,所有您需要做的就是
// FruitViewController.h
@interface FruitViewController : UIViewController
@end
// FruitViewController.m
#import <TJBinder.h>
#import "FruitViewController.h"
#import "Fruit.h"
@implementation FruitViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Retrieve your data object from an external resource here
Fruit* fruit = [Fruit new];
fruit.name = @"apple";
fruit.color = [UIColor redColor];
// Tell the root view what is its data object
self.view.dataObject = fruit;
}
@end
您可以通过这种方式在Interface Builder中完成剩余的工作
这里有几点需要注意
text
和textColor
属性。相反,您使用标签的特别格式的“用户定义运行时属性”指定了标签与水果之间的关系,如图像右侧红色框所示。此示例中的两行说明,标签的text
属性应从父视图的数据对象中获取其值,标签的textColor
属性应绑定到水果的color
属性。之前,您在FruitViewController.m中分配数据对象到视图控制器的主视图,这个主视图是标签的父视图,因此TJBinder将能够找到您的数据对象。
其余的将由TJBinder
完成。它将根据您在键值列中定义的上游视图层次结构进行遍历,找到数据对象,从中提取值并传递到视图对象的指定属性。如果您运行应用程序,您将看到屏幕上的红色的“苹果”文本,而无需编写更多代码。
表格视图和集合视图
TJBinder
与表格视图和集合视图也表现卓越。您需要指定单元格的数据对象以及单元格 UI 元素的绑定。您可以在示例应用程序中找到更多如何使用这些类与 TJBinder
相结合使用的详细信息。
数据对象随时间更新
有时数据对象的某些内容可能随时间更新。它们可以表示设备的 GPS 坐标,或者您可能想要实现一个计数器或时钟。一旦您正确配置了视图对象和数据对象之间的绑定,当数据对象更改时,TJBinder
将会自动更新您的视图。
为了能找到数据对象,TJBinder
会遍历视图层次结构。您必须“引导”TJBinder
在该层次结构中到达包含您在 Interface Builder 的用户定义运行时属性字段中定义的键路径的数据对象的视图。
每个键路径必须从 bindTo
开始。这会触发 TJBinder
开始一个新的绑定过程。键路径的元素通过点(.)字符分隔。在 bindTo
元素之后,TJBinder
将从当前视图开始遍历视图层次结构,即这个运行时属性定义的视图。您可以使用 @superview
或 @superviewWith...
键路径命令跳转到层次结构中的父视图,并通过 @subviewWith...
命令之一遍历到一个子视图(尽管您需要这种情况的频率要低得多)。
一旦到达包含数据对象的视图,应在键路径中添加 dataObject
键。这告诉 TJBinder
它应该停止遍历视图层次结构并查找当前视图中的数据对象。在 dataObject
键之后,可以添加数据对象的属性名称(标准键路径),以便 TJBinder
可以找到传递给视图对象的确切值。
应在 Interface Builder 中视图对象的“用户定义运行时属性”部分的“键路径”列中插入以这种方式构建的键路径。该属性的“类型”应始终设置为“字符串”,并应将视图对象的属性指定为“值”。例如,如果您想让数据对象的属性值绑定到标签的文本,可以为 UILabel
定义 text
。另一个示例可以是 UILabel
视图的 textColor
值,以将标签的颜色绑定到数据对象公开的颜色对象。
重要
您应确保
TJBinder
在键路径末尾找到的数据类型与视图对象的属性的类型相同。例如,对于上面提到的
FruitViewController
的UILabel
视图,我们指定了一个Fruit
对象作为dataObject
。该Fruit
对象有一个color
属性,类型为UIColor*
,我们可以将其绑定到具有相同UIColor*
类型的UILabel
的textColor
属性。
BindProxy
将作为 dataObject
叶子属性的观察者订阅,因此 Objective-C 运行时将在属性值更改时通知 BindProxy
。BindProxy
将处理此通知,提取属性的值,并使用此值更新关联的 UIView
属性。
由于键值编码的本质,TJBinder
和编译器无法在编译时检查您定义的属性的存在和键路径的有效性。如果在运行时收到“此类未针对键 ... 进行键值编码合规性”异常,请仔细检查您在 Interface Builder 中输入的键路径,尤其注意:
TJBinder
和编译器无法在编译时检查您定义的属性的存在和键路径的有效性。如果在运行时收到“此类未针对键 ... 进行键值编码合规性”异常,请仔细检查您在 Interface Builder 中输入的键路径,尤其注意:TJBinder
支持标准属性 UIView
,让您可以通过 superview
和 window
属性定义数据对象搜索路径,或者如果您定义了 UIView
子类的命名属性,还可以通过属性名称来引用它。
然而,您会发现 TJBinder
定义的键路径运算符非常有用,这些运算符可以简化对视图层次的遍历。这些运算符以前缀 @
字符为前缀,允许您跳转到视图层次的另一个视图。以下是所有键路径运算符的参考表。
示例 | 参数 | 描述 |
---|---|---|
@superview |
无 | 是 superview 的别名:返回父视图 |
@rootview |
无 | 沿着视图层次向上穿越,直到找到一个没有父视图的视图。注意:如果您使用容器视图来显示嵌入的视图控制器,根视图可能与视图控制器的 view 属性不同。 |
@superviewWithTag[12] |
视图标签(整数) | 沿着视图层次向上穿越,直到找到一个具有指定标签的父视图。此示例返回最接近的父视图,其 tag == 12 。 |
@superviewWithRestorationID[myUd] |
视图恢复ID(字符串) | 沿着视图层次向上穿越,直到找到一个具有指定恢复ID的父视图。此示例返回最接近的父视图,其 restorationIdentifier == myID 。 |
@superviewWithClass[UITableViewCell] |
类名(字符串) | 沿着视图层次向上穿越,直到找到一个具有指定类的父视图。此示例返回继承自 UITableViewCell 的父视图。可能是有用的键路径运算符。 |
@subviews[2] |
子视图索引(整数) | 返回视图在指定索引处的 subviews 数组中的元素。此示例返回 subviews 数组中的第3个元素。 |
@subviewWithTag[12] |
视图标签(整数) | 查找并返回视图在指定标签处的 subviews 数组中的元素。此示例返回标签为 tag == 12 的子视图。 |
@subviewWithRestorationID[myId] |
视图恢复ID(字符串) | 查找并返回视图在指定恢复标识符处的 subviews 数组中的元素。此示例返回 restorationIdentifier == 12 的子视图。 |
@subviewWithClass[UILabel] |
类名(字符串) | 查找并返回视图在指定类中的 subviews 数组的第一元素。此示例返回 subviews 数组中的第一个 UILabel 。 |
@cell |
无 | 沿着视图层次向上穿越,直到找到一个具有 UITableViewCell 或 UICollectionViewCell 类的父视图。类似于 @superviewWithClass[UITableViewCell] 和 @superviewWithClass[UICollectionViewCell] 的快捷方式。 |