版权声明:原创作品,谢绝转载!否则将追究法律责任。

那么怎么使用适配器设计模式呢?

这个之前提到的水平滚动的视图像这样:

为了开始实现他,我们创建一个新的继承与UIView的HorizontalScroller 类。打开头文件添加以下代码:

@protocol HorizontalScrollerDelegate <NSObject>

// methods declaration goes in here

@end

这里定义了一个叫做HorizontalScrollerDelegate的协议并且他继承与NSObject协议就像Objective-c继承与他的父类一样。符合NSObject协议是一个很好的做法,或者符合一个协议这个协议也符合NSObject协议。这允许你向HorizontalScroller的代表发送定义在NSObject的消息。

在@protocol和@end之间添加可选和必须的协议方法:

@required

// ask the delegate how many views he wants to present inside the horizontal scroller- (NSInteger)numberOfViewsForHorizontalScroller:(HorizontalScroller*)scroller;

// ask the delegate to return the view that should appear at <index>- (UIView*)horizontalScroller:(HorizontalScroller*)scroller viewAtIndex:(int)index;

// inform the delegate what the view at <index> has been clicked- (void)horizontalScroller:(HorizontalScroller*)scroller clickedViewAtIndex:(int)index;

@optional

// ask the delegate for the index of the initial view to display. this method is optional// and defaults to 0 if it's not implemented by the delegate- (NSInteger)initialViewIndexForHorizontalScroller:(HorizontalScroller*)scroller;

在这里你都有必选和可选方法。必选方法必须被代理实现并且包含一些数据绝对是被这个类需要的。这个例子中,所需的是一些视图和视图在特定的索引还有当视图被点击时候的行为。这个可选方法在这是初始化的一个视图。如果没有实现然后这个HorizontalScroller 将默认是第一个索引。

下一步。你需要从HorizontalScroller 类的定义引用新的代理。但是这个协议定义在类定义之下,所以在这一点上不可见,你能做什么。

这个解决方法是向前声明协议让编译器知道这个协议是可用的。为了这样做在@interface上面加上

@protocol HorizontalScrollerDelegate;

在@interface和@end之间添加下面代码:

@property (weak) id<HorizontalScrollerDelegate> delegate;

- (void)reload;

这个属性的关键字是weak。目的是阻止循环引用。如果一个类保持了一个强引用向代理,并且代理保持了一个强引用向这个类,那么应用程序将会造成内存泄漏,无论是类将释放分配的内存。

id意味着这个代理可以是符合这个HorizontalScrollerDelegate的任何类。给你一些类型安全。

这个reload方法类似tableView的reloadData。他重新加载数据来构建水平滑动视图。

在HorizontalScroller实现文件里添加下面代码:

#import "HorizontalScroller.h"

// 1#define VIEW_PADDING 10#define VIEW_DIMENSIONS 100#define VIEWS_OFFSET 100

// 2@interface HorizontalScroller () <UIScrollViewDelegate>

@end

// 3@implementation HorizontalScroller

{

UIScrollView *scroller;

}

@end

解释下每个步骤:

定义一个常量便于修改布局。

HorizontalScroller 符合这个UIScrollViewDelegate 协议因此HorizontalScroller 用UIScrollView 来滚动专辑。他需要知道用户的活动比如当用户停止滚动。

创建一个UIScrollView 来包含一些视图。

下一步你需要实现初始化:

- (id)initWithFrame:(CGRect)frame

{

self = [super initWithFrame:frame];

if (self)

{

scroller = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];

scroller.delegate = self;

[self addSubview:scroller];

UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scrollerTapped:)];

[scroller addGestureRecognizer:tapRecognizer];

}

return self;

}

这个UIScrollView 完全充满HorizontalScroller。一个UITapGestureRecognizer 检测手势并且检测一个专辑被点击。现在添加这个方法:

- (void)scrollerTapped:(UITapGestureRecognizer*)gesture

{

CGPoint location = [gesture locationInView:gesture.view];

// we can't use an enumerator here, because we don't want to enumerate over ALL of the UIScrollView subviews.

// we want to enumerate only the subviews that we added

for (int index=0; index<[self.delegate numberOfViewsForHorizontalScroller:self]; index++)

{

UIView *view = scroller.subviews[index];

if (CGRectContainsPoint(view.frame, location))

{

[self.delegate horizontalScroller:self clickedViewAtIndex:index];

[scroller setContentOffset:CGPointMake(view.frame.origin.x - self.frame.size.width/2 + view.frame.size.width/2, 0) animated:YES];

break;

}

}}

gesture作为参数传递让并使用locationInView:能获得点击的位置。

下一步调用代理的numberOfViewsForHorizontalScroller:方法这时候HorizontalScroller 的实例不知道代理的很多信息除了知道他可以安全的发送信息因为他必须符合HorizontalScrollerDelegate 协议。

UIScrollView的没一个视图用CGRectContainsPoint 方法检测是不是被点击。当这个视图找到的时候就向代理发送 horizontalScroller:clickedViewAtIndex: 消息。在跳出这个循环时候,你必须让被点击的视图在UIScrollView中间。

现在添加下面代码来重新加载UIScrollView:

- (void)reload

{

// 1 - nothing to load if there's no delegate

if (self.delegate == nil) return;

// 2 - remove all subviews

[scroller.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

[obj removeFromSuperview];

}];

// 3 - xValue is the starting point of the views inside the scroller

CGFloat xValue = VIEWS_OFFSET;

for (int i=0; i<[self.delegate numberOfViewsForHorizontalScroller:self]; i++)

{

// 4 - add a view at the right position

xValue += VIEW_PADDING;

UIView *view = [self.delegate horizontalScroller:self viewAtIndex:i];

view.frame = CGRectMake(xValue, VIEW_PADDING, VIEW_DIMENSIONS, VIEW_DIMENSIONS);

[scroller addSubview:view];

xValue += VIEW_DIMENSIONS+VIEW_PADDING;

}

// 5

[scroller setContentSize:CGSizeMake(xValue+VIEWS_OFFSET, self.frame.size.height)];

// 6 - if an initial view is defined, center the scroller on it

if ([self.delegate respondsToSelector:@selector(initialViewIndexForHorizontalScroller:)])

{

int initialView = [self.delegate initialViewIndexForHorizontalScroller:self];

[scroller setContentOffset:CGPointMake(initialView*(VIEW_DIMENSIONS+(2*VIEW_PADDING)), 0) animated:YES];

}}

下面解释上面代码的注释:

1:如果没有代理。什么也不会做并且返回。

2:移除掉以前添加到UIScrollView上的子视图

3:所有的视图被定位从被给定的偏移量。当前是100.你也可以很容易的改变上面的常量的定义。

4:这个HorizontalScroller 访问他代理的视图并让他们按照之前水平布局的那样填充。

5:一旦所有的视图在合适的位置,设置内容的偏移量为UIScrollView让用户能浏览所有的专辑封面

6:这个HorizontalScroller 检测如果他的代理能响应initialViewIndexForHorizontalScroller:选择器。这个检测是必须的因为这个特殊的协议方法是可选的。如果代理不能实现这个方法。那么0就是默认的值。最后这段代码设置UIScrollView让代理定义的这个初始视图居中。

你执行这个reload当你数据需要改变的时候。你添加HorizontalScroller 到另一个视图也得调用这个方法因此在HorizontalScroller 的实现文件添加下面代码:

- (void)didMoveToSuperview

{

[self reload];

}

didMoveToSuperview 消息被发送到一个视图当他被添加到另一个视图作为子视图。这个是重新加载数据的好时机。

最后一段代码确保你的专辑视图一直在UIScrollView的中间位置,这样做的,你需要做一些计算当用手指拖拽UIScrollView的时候。

添加下面方法到HorizontalScroller实现文件:

- (void)centerCurrentView

{

int xFinal = scroller.contentOffset.x + (VIEWS_OFFSET/2) + VIEW_PADDING;

int viewIndex = xFinal / (VIEW_DIMENSIONS+(2*VIEW_PADDING));

xFinal = viewIndex * (VIEW_DIMENSIONS+(2*VIEW_PADDING));

[scroller setContentOffset:CGPointMake(xFinal,0) animated:YES];

[self.delegate horizontalScroller:self clickedViewAtIndex:viewIndex];

}

上面代码考虑当前UIScrollView的偏移量和视图的填充和尺寸目的是计算当前视图到中间的距离。最后一行很重要:一旦这个视图在中间,然后你通知代理这个选中的视图被改变了。

为了检测用户完成UIScrollView的拖拽,你必须添加

UIScrollViewDelegate的协议方法:

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

{

if (!decelerate)

{

[self centerCurrentView];

}}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

{

[self centerCurrentView];

}

scrollViewDidEndDragging:willDecelerate:通知代理当用户完成拖拽。这个decelerate参数是真如果UIScrollView没有完全停止。当滚动动作结束,系统调用

scrollViewDidEndDecelerating。这两个例子我们都应该调用新方法来使当前视图居中因为当前视图可能因为用户的拖拽而改变。

现在这个HorizontalScroller 准备好了,浏览之前写的代码。你将看到没有提及Album 和AlbumView类。这是很好的机制因为意味着你的这个新的滚动视图是独立并且可重用的。

编译运行你的项目确保代码是正确的。

现在这个HorizontalScroller 是完成了。现在打开

ViewController实现文件添加下面代码:

#import "HorizontalScroller.h"#import "AlbumView.h"

添加HorizontalScrollerDelegate到Viewcontroller里面让他符合则个协议。

@interface ViewController ()<UITableViewDataSource, UITableViewDelegate, HorizontalScrollerDelegate>

在延展里面添加Horizontal Scroller的一个实例变量。

现在你可以实现代理方法,你会发现仅仅几行就实现了很多功能在Viewcontroller实现文件里面添加下面代码:

#pragma mark - HorizontalScrollerDelegate methods- (void)horizontalScroller:(HorizontalScroller *)scroller clickedViewAtIndex:(int)index

{

currentAlbumIndex = index;

[self showDataForAlbumAtIndex:index];

}

设置这个实例变量存储当前的专辑调用showDataForAlbumAtIndex:来展示新的专辑。

注意:这是常见的用法将方法组织起来然后用#pragma mark指令。这个编译器将忽略这一行但如果你通过xcode的下拉列表的方法直接跳转到相应的方法。你会看到这个指令的一个分割线和标题。这样便于你在xcode中组织管理你的代码。

下一步添加下列代码:- (UIView*)horizontalScroller:(HorizontalScroller*)scroller viewAtIndex:(int)index

{

Album *album = allAlbums[index];

return [[AlbumView alloc] initWithFrame:CGRectMake(0, 0, 100, 100) albumCover:album.coverUrl];

}

在这你创建一个新的AlbumView 并且把他传给这个HorizontalScroller。这是他只有三个短的方法来显示一个漂亮的水平滑动视图。

在之前你也必须创建一个滑动视图并添加到主视图上添加下面的方法:

- (void)reloadScroller

{

allAlbums = [[LibraryAPI sharedInstance] getAlbums];

if (currentAlbumIndex < 0) currentAlbumIndex = 0;

else if (currentAlbumIndex >= allAlbums.count) currentAlbumIndex = allAlbums.count-1;

[scroller reload];

[self showDataForAlbumAtIndex:currentAlbumIndex];

}

这个方法通过libraryAPI加载专辑数据并且然后设置当前显示视图基于当前视图索引的当前值。

如果当前视图索引小于0,意味着没有视图被当前显示。然后第一个专辑在列表中被展示。否则最后一个专辑被展示。现在初始化滑动视图通过在ViewDidLoad添加下面方法:scroller = [[HorizontalScroller alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 120)];

scroller.backgroundColor = [UIColor colorWithRed:0.24f green:0.35f blue:0.49f alpha:1];

scroller.delegate = self;

[self.view addSubview:scroller];

[self reloadScroller];

上面仅仅创建了一个HorizontalScroller新的实例。设置他的背景颜色和代理。添加滑动视图到主视图,并且然后为滑动视图加载子视图来显示专辑数据。

注意:如果你的协议里面有一大堆的协议方法你可以分成几个小的协议方法。就像UITableViewDelegate 和UITableViewDataSource是一个很好的例子,因此他们都是UITableView的协议。试着设计你的协议使他们来处理一个特定区域的功能。

编译运行你的项目来看看你的新的滑动视图。

你现在没有添加代码来下载图片,现在还需要添加一个下载图片的方法。因为所有的方法服务是通过libraryAPI,这就是一个新的方法,然后需要有几点事情要考虑。

1:AlbumView 不能直接和libraryAPI交互。你不能混淆你的视图逻辑和通讯逻辑。

2:同样的原因libraryAPI不知道AlbumView

3:libraryAPI需要通知AlbumView 一旦图片下载并展示到视图上。

这就是我们下面介绍的观察者设计模式。

IOS设计模式第六篇之适配器设计模式相关推荐

  1. java设计模式适配器模式_Java中的适配器设计模式

    java设计模式适配器模式 适配器设计模式是一种结构设计模式 ,可以帮助我们连接到通过不同接口公开相似功能的旧版或第三方代码. 适配器的现实世界是我们用来将USB电缆连接到以太网端口的类比. 在设计一 ...

  2. 吃透设计模式第六篇-装饰者模式

    设计模式的重要性对于程序员来说,相当于盾牌对于美国队长,暴风战斧相对于雷神,内裤对于绿巨人(绿巨人最强武器,手动狗头)来说,是必不可少的. 在此,特别总结下23钟设计模式: 创建型模式:单例模式.抽象 ...

  3. Android Ap 开发 设计模式第六篇:原型模式

    Prototype Pattern 名称由来 不是利用类来产生实例对象,而是从一个对象实例产生出另一个新的对象实例 ,根据被视为原型的对象实例 ,建立起的另一个新的对象实例就称为原型模式(Ptotot ...

  4. 设计模式的1000+篇文章总结

    设计模式的1000+篇文章总结 本文收集和总结了有关设计模式的1000+篇文章,由于篇幅有限只能总结近期的内容,想了解更多内容可以访问:https://www.ai2news.com/, 其分享了有关 ...

  5. C#:适配器设计模式如何让您的生活更轻松

    目录 介绍 文章的目标 理解本文您需要知道什么 适配器设计模式的简要提示 第一个示例:静态.NET类 第二个示例:使用第三方记录器替换自定义记录器 第三个示例:使用不同的自定义记录器替换自定义记录器 ...

  6. JavaScript设计模式系列—模式篇总结(上)

    转载请注明预见才能遇见的博客:http://my.csdn.net/ 原文地址:https://blog.csdn.net/pcaxb/article/details/102517956 JavaSc ...

  7. 专题:设计模式(精华篇)(Yanlz+单一职责+里氏替换+依赖倒置+接口隔离+迪米特+开放封闭+创建类+结构类+行为类+立钻哥哥)

    <专题:设计模式(精华篇)> 版本 作者 参与者 完成日期 备注 DesignPatterns_Classics_V01_1.0 严立钻 2018.07.31 ##<专题:设计模式( ...

  8. 设计模式看这篇就够了

    设计模式的笔记学习. 设计模式与原则 设计模式的主流开发原则: 单一职责原则 开闭原则 里氏替换原则 依赖导致原则 接口隔离原则 迪米特法则 合成/聚合复用原则 主流的设计模式有23种,主要介绍以下几 ...

  9. java设计模式中不属于创建型模式_23种设计模式第二篇:java工厂模式定义:工厂模式是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式...

    23种设计模式第二篇:java工厂模式 定义: 工厂模式是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 工厂模式主要是为创建对象提供过渡接口, ...

最新文章

  1. 【基于XML方式】Spring整合Kafka
  2. jinja filter
  3. Mono喜迁新家-http://www.xamarin.com/
  4. 学习scala03 控制结构
  5. java String 详解
  6. 阿里巴巴大规模应用 Flink 的实战经验:常见问题诊断思路
  7. 【MySQL】011-多表查询
  8. 关于ie6常见浏览器兼容问题
  9. 计算机网络基本原理pdf,自考计算机网络基本原理课程学习指引.pdf
  10. oracle select into from 用法,sql select into用法
  11. 开发者必备的网站。javascript手册,css手册
  12. 再论由内而外造就自己
  13. Unix时间戳转北京时间 UTC时间转北京时间 C代码实现
  14. 解决eccube お名前.com服务器 各种请求链接 https 不生效
  15. 二叉树先序递归遍历,中序递归非递归遍历实验
  16. Swagger2由入门到实战
  17. 计算机的扩展模式,Win7双屏复制/双屏扩展设置教程
  18. 网路学员面试常见问题:
  19. GLTF格式学习:glTF介绍( 基于WebGL
  20. 使用EasyPoi导入导出Excel

热门文章

  1. “脑补”的科学依据:眼前的黑不是黑,靠得是你的大脑
  2. 美国五大科技巨头的人工智能竞赛
  3. Hinton:人类就是机器,绝妙的机器
  4. 中国研制出首款搭载寒武纪AI芯片的人工智能服务器
  5. 【业界】Facebook的基础AI算法是如何驱动社交网络的发展?
  6. 换脸火了,我用 Python 快速入门生成模型
  7. 乔布斯 18 岁求职信拍卖价 22.24 万美元,值吗?
  8. 开源代码却无奈遗弃,濒临奔溃的开源开发者们!
  9. Burpsuite学习(4)
  10. VirtualHost 的配置