TJBinder0.1.0

TJBinder0.1.0

测试已测试
语言 Obj-CObjective C
许可证 MIT
发布上次发布2016年7月

Janos Tolgyesi维护。



  • 作者:
  • Janos Tolgyesi

TJBinder是一个轻薄但仍然强大的iOS实现,它是基于OS-X中Cocoa绑定技术的模型-视图绑定科技。其目标是相同的:创建一种“提供一种不编写大量胶水代码也能同步模型和视图值的技术”。

介绍

本节以某种叙述风格解释为什么和何时使用TJBinder对您有用。如果您想直接跳到细节,请继续阅读下一节。

问题

假设您必须编写另一个基于MVC的简单表视图--详细信息视图应用程序。如果您以前从未尝试过,我建议您阅读Ray Wenderlich的绝佳教程。当您第二次或第三次编写这种应用程序时,您肯定会注意到您需要编写的样板代码和类似动作的数量。

  1. 在Interface Builder中创建UI,例如向Storyboard中添加一个表视图控制器。
  2. 创建一个UITableViewController子类,在您查找数据模型对象数组的或获取结果控制器。
  3. 如果您需要一个具有自定义布局的单元格,则必须在Interface Builder中创建一个表视图单元格原型(如果您使用Storyboard的话)或一个单独的xib文件来为您单元格(如果您使用xibs的话),并将标签,图像视图和其他UI元素添加到您的单元格中。
  4. 您必须编写自己的UITableViewCell子类,创建IBOutlet @properties并连接它们与Storyboard/xib元素。
  5. 在您在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允许您使用您喜欢的日志记录器。目前支持没有日志、NSLogCocoaLumberjack

如何使用TJBinder

与其他库不同,使用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中完成剩余的工作

screenshot

这里有几点需要注意

  • 您没有为标签创建IBOutlet属性
  • 没有代码设置标签的texttextColor属性。

相反,您使用标签的特别格式的“用户定义运行时属性”指定了标签与水果之间的关系,如图像右侧红色框所示。此示例中的两行说明,标签的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 在键路径末尾找到的数据类型与视图对象的属性的类型相同。

例如,对于上面提到的 FruitViewControllerUILabel 视图,我们指定了一个 Fruit 对象作为 dataObject。该 Fruit 对象有一个 color 属性,类型为 UIColor*,我们可以将其绑定到具有相同 UIColor* 类型的 UILabeltextColor 属性。

它是如何工作的?

BindProxy 将作为 dataObject 叶子属性的观察者订阅,因此 Objective-C 运行时将在属性值更改时通知 BindProxyBindProxy 将处理此通知,提取属性的值,并使用此值更新关联的 UIView 属性。

由于键值编码的本质,TJBinder 和编译器无法在编译时检查您定义的属性的存在和键路径的有效性。如果在运行时收到“此类未针对键 ... 进行键值编码合规性”异常,请仔细检查您在 Interface Builder 中输入的键路径,尤其注意:

  • 由于键值编码的本质,TJBinder 和编译器无法在编译时检查您定义的属性的存在和键路径的有效性。如果在运行时收到“此类未针对键 ... 进行键值编码合规性”异常,请仔细检查您在 Interface Builder 中输入的键路径,尤其注意:

TJBinder 支持标准属性 UIView,让您可以通过 superviewwindow 属性定义数据对象搜索路径,或者如果您定义了 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 沿着视图层次向上穿越,直到找到一个具有 UITableViewCellUICollectionViewCell 类的父视图。类似于 @superviewWithClass[UITableViewCell]@superviewWithClass[UICollectionViewCell] 的快捷方式。