iOS设计模式之三:适配器模式和观察者模式

338 查看

适配器(Adapter)模式

适配器可以让一些接口不兼容的类一起工作。它包装一个对象然后暴漏一个标准的交互接口。

如果你熟悉适配器设计模式,苹果通过一个稍微不同的方式来实现它,苹果使用了协议的方式来实现。你可能已经熟悉UITableViewDelegate, UIScrollViewDelegate, NSCoding 和 NSCopying协议。举个例子,使用NSCopying协议,任何类都可以提供一个标准的copy方法。

如何使用适配器模式

前面提到的水平滚动视图如下图所示:

为了开始实现它,在工程导航视图中右键点击View组,选择New File…使用iOS\Cocoa Touch\Objective-C class 模板创建一个类。命名这个新类为HorizontalScroller,并且设置它是UIView的子类。

打开HorizontalScroller.h文件,在@end 行后面插入如下代码:

上面的代码定义了一个名为HorizontalScrollerDelegate的协议,它采用Objective-C 类继承父类的方式继承自NSObject协议。去遵循NSObject协议或者遵循一个本身实现了NSObject协议的类 是一条最佳实践,这使得你可以给HorizontalScroller的委托发送NSObject定义的消息。你不久会意识到为什么这样做是重要的。

在@protocol和@end之间,你定义了委托必须实现以及可选的方法。所以增加下面的方法:

这里你既有必需的方法也有可选方法。必需的方法要求委托必须实现它,因为它提供一些必需的数据。在这里,必需的是视图的数量,指定索引位置的视图,以及用户点击视图后的行为,可选的方法是初始化视图;如果它没有实现,那么HorizontalScroller将缺省用第一个索引的视图。

下一步,你需要在HorizontalScroller类中引用新建的委托。但是委托的定义是在类的定义之后的,所以在类中它是不可见的,怎么办呢?

解决方案就是前置声明委托协议以便编译器(和Xcode)知道协议的存在。如何做?你只需要在@interface行前面增加下面的代码即可:

继续在HorizontalScroller.h文件中,在@interface 和@end之间增加如下的语句:

这里你声明属性为weak.这样做是为了防止循环引用。如果一个类强引用它的委托,它的委托也强引用那个类,那么你的app将会出现内存泄露,因为任何一个类都不能释放调分配给另一个类的内存。

id意味着delegate属性可以用任何遵从HorizontalScrollerDelegate的类赋值,这样可以保障一定的类型安全。

reload方法在UITableView的reloadData方法之后被调用,它重新加载所有的数据去构建水平滚动视图。

用如下的代码取代HorizontalScroller.m的内容:

让我们来对上面每个注释块的内容进行一一分析:

  • 1. 定义了一系列的常量以方便在设计的时候修改视图的布局。水平滚动视图中的每个子视图都将是100*100,10点的边框的矩形.
  • 2. HorizontalScroller遵循UIScrollViewDelegate协议。因为HorizontalScroller使用UIScrollerView去滚动专辑封面,所以它需要用户停止滚动类似的事件
  • 3.创建了UIScrollerView的实例。

下一步你需要实现初始化器。增加下面的代码:

滚动视图完全充满了HorizontalScroller。UITapGestureRecognizer检测滚动视图的触摸事件,它将检测专辑封面是否被点击了。如果专辑封面被点击了,它会通知HorizontalScroller的委托。

现在,增加下面的代码:

Gesture对象被当做参数传递,让你通过locationInView:导出点击的位置。

接下来,你调用了numberOfViewsForHorizontalScroller:委托方法,HorizontalScroller实例除了知道它可以安全的发送这个消息给委托之外,它不知道其它关于委托的信息,因为委托必须遵循HorizontalScrollerDelegate协议。

对于滚动视图中的每个子视图,通过CGRectContainsPoint方法发现被点击的视图。当你已经找到了被点击的视图,给委托发送horizontalScroller:clickedViewAtIndex:消息。在退出循环之前,将被点击的视图放置到滚动视图的中间。

现在增加下面的代码去重新加载滚动视图: