作者:ani_di
版权所有,转载务必保留此链接 http://blog.csdn.net/ani_di

庖丁解牛FPPopover

FPPopover是一个实现Popover控件的开源项目,比标准控件要强大一些。虽然如此,但是在定制边框弹框时,还是遇到了问题:border=NO并且arrowDirection = FPPopoverNoArrow时多出了一个头。

因此需要动手修改。好在这份源代码不长,修改代码的时候还可以欣赏一番。

ARCMacors.h

这个头文件定义了很多宏,基本上就是把支持arc的代码按照不支持arc的方式些,以便适应老版本。 很简单,不多说。这份头文件可以复用。

FPPopoverController

FPPopover主要就3个类,这个是Controller。里面主要由2个view:touchView和contentView。touchView和contentView分别用于事件捕获和内容展示。

init函数有两个-(id)initWithViewController:(UIViewController*)viewController-(id)initWithViewController:(UIViewController*)viewController。其实主要工作在后一个里面。

-(id)initWithViewController:(UIViewController*)viewController

init里面主要是初始化_touchView和_contentView。_touchView的初始化总结为以下

_touchView = [[FPTouchView alloc] initWithFrame:self.view.bounds];
_touchView.backgroundColor = [UIColor clearColor];
_touchView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_touchView.clipsToBounds = NO;
[_touchView setTouchedOutsideBlock:^{
[bself dismissPopoverAnimated:YES];
}];

Line 1构造FPTouchView,大小等于self.view.bounds。但这时self.view.bounds还没有大小,不过不要紧,后面setupView会重新修改。
Line 2、3设置背景色和自动调整时大小时的属性,这里是保持宽高不变。
Line 4设置不剪裁子view。默然是NO,此行意义不大。 Line 5设置点击到外面的事件,默认关闭view。touchView主要就是检测在view外和里面的touch事件。

_contentView也比较简单

self.contentSize = CGSizeMake(200, 300); //default size
_contentView = [[FPPopoverView alloc] initWithFrame:CGRectMake(0, 0,
self.contentSize.width, self.contentSize.height)];
[_contentView addContentView:_viewController.view];
_contentView.title = _viewController.title;

_contentView主要继承外部controller的title和view。在FPPopverView中,这两部分是独立开显示的。

-(void)setupView

setupView函数就是具体设置view的的大小。

self.view.frame = CGRectMake(0, 0, [self parentWidth], [self parentHeight]);
_touchView.frame = self.view.bounds;

touchView和self.view都等于parent bound。一般情况下,它的parent view是后面的AppDelegate的view,即全屏大小。后面可以看到,这样设置最后怎么区分touch inside和outside。

contentView的大小在-(CGRect)bestArrowDirectionAndFrameFromView:(UIView*)v设置,相对复杂一些。这里不深究计算方式。

setupView在很多地方都被调用过:在viewDidLoad、手动present、屏幕旋转、键盘显示等。虽然重复调用的次数比较多,但减轻了代码负担。

由于setupView调用的地方很多,必须调用setNeedsDisplay强制刷新,否则不会立即显示。

-(void)presentPopoverFromPoint:(CGPoint)fromPoint

这个函数主要的作用是设置_parentView。Line 7找到第一个window的第一个view,把自己的view加到里面。 根据事件的响应链window1->controller->views->window2...,self.view,即touchView会比现有的其他view更快响应。

[self.view removeFromSuperview];
NSArray *windows = [UIApplication sharedApplication].windows;
if(windows.count > 0)
{
_parentView=nil;
_window = [windows objectAtIndex:0];
//keep the first subview
if(_window.subviews.count > 0)
{
_parentView = [_window.subviews objectAtIndex:0];
[_parentView addSubview:self.view];
[_viewController viewDidAppear:YES];
} }

因为在init里面的bounds是0,因此没有真正显示出来。这里手动显示非常简单,设置好真实的frame即可。代码里还加了一点点小动画。

[self setupView];
self.view.alpha = 0.0;
[UIView animateWithDuration:0.2 animations:^{ self.view.alpha = self.alpha;
}];

-(void)dismissPopover

要隐藏一个view方法很多,一般有两种方案:1. 设透明值;2. removeFromSuperview。 在这里用的方案2。这样可以排除self.view潜在的影响,效率方面也会提升一点。 另外有setupView,再次显示出来也不难。

FPTouchView

FPTouchView对象只有一个作用:判断点击操作的位置。

-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *subview = [super hitTest:point withEvent:event]; if(UIEventTypeTouches == event.type)
{
BOOL touchedInside = subview != self;
if(!touchedInside)
{
for(UIView *s in self.subviews)
{
if(s == subview)
{
//touched inside
touchedInside = YES;
break;
}
}
} if(touchedInside && _insideBlock)
{
_insideBlock();
}
else if(!touchedInside && _outsideBlock)
{
_outsideBlock();
}
} return subview;
}

最重要的是下面几段代码。

BOOL touchedInside = subview != self;
if(!touchedInside)
{
for(UIView *s in self.subviews)
{
if(s == subview)
{
//touched inside
touchedInside = YES;
break;
}
}
}

如果hitTest返回的是自身,则一定是在弹窗外部;如果不是,则判断是不是自己的子view。 在 FPPopoverController 的初始化方法里面,_contentView是 _touchView的子view。

基类的hitTest返回事件位于当前View最远的view(PS:这样才能让最后添加的view最先接收到事件), 如果没有这返回nil。 _touchView本身的frame等于第一个window的第一个view(参见 presentPopoverFromPoint), 一般这个view等于窗口大小。

作者这里代码很奇怪。判断子view的条件是 touchedInside == NO;而满足此条件则 subview == self。 那么检测self在self.subviews又是为何?

FPTouchedOutsideBlock & FPTouchedInsideBlock

这两个block是作者提供出来的回调。不同于以往的delegate模式,这里用的是block。
FPTouchedOutsideBlock在 FPPopoverController 用于关闭视图。所以修改这个时要注意一下。

FPPopoverView

这里面大部分代码都是用Quartz画外面的边框。计算orign稍微复杂一点,画tint的代码比较多。

回到以前

最开始的问题是在border=NO并且arrowDirection = FPPopoverNoArrow时多出了一个头。 不知道这是一个bug还是一个特性,反正不是我想要的需求。从上面的分析,解决这个问题应该从FPPopoverView的drawRect下手。

由于边框都是在drawRect中实现的,所以我的解决方案非常简单

- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
if (self.border == NO && self.arrowDirection == FPPopoverNoArrow)
return;
// Keep others unchange
}

效果如下

庖丁解牛FPPopover的更多相关文章

  1. 庖丁解牛-----Live555源码彻底解密(RTP打包)

    本文主要讲解live555的服务端RTP打包流程,根据MediaServer讲解RTP的打包流程,所以大家看这篇文章时,先看看下面这个链接的内容; 庖丁解牛-----Live555源码彻底解密(根据M ...

  2. Gradle 庖丁解牛(构建生命周期核心托付对象创建源代码浅析)

    [工匠若水 http://blog.csdn.net/yanbober 未经同意严禁转载,请尊重作者劳动成果.私信联系我] 1 背景 上一篇<Gradle 庖丁解牛(构建源头源代码浅析)> ...

  3. ARM“庖丁解牛”之存储器管理单元MMU

    转:http://blog.sina.com.cn/s/blog_a07635070101bcbt.html 最近笔者详细地学习了由杜春雷老师编写的<ARM体系结构与编程>.对ARM存储管 ...

  4. Android基础新手教程——4.3.2 BroadcastReceiver庖丁解牛

    Android基础新手教程--4.3.2 BroadcastReceiver庖丁解牛 标签(空格分隔): Android基础新手教程 本节引言: 上节我们对BroadcastReceiver已经有了一 ...

  5. 庖丁解牛Linux内核分析 0x00:《庖丁解牛》

    庖丁解牛  吾生也有涯,而知也无涯 .以有涯随无涯,殆已!已而为知者,殆而已矣!为善无近名,为恶无近刑.缘督以为经,可以保身,可以全生,可以养亲,可以尽年. 庖丁为文惠君解牛,手之所触,肩之所倚,足之 ...

  6. Mysql性能优化全揭秘-庖丁解牛

    「为什么写」 一直想写数据库相关的文章,最直接的原因是数据库这块我们工作中每天都会用到,也是面试求职绕不开的话题,无论你是何种测试,优秀的数据库能力都会非常加分,最近我在总结数据库性能优化这块内容,性 ...

  7. 干货 | 45张图庖丁解牛18种Queue,你知道几种?

    在讲<21张图讲解集合的线程不安全>那一篇,我留了一个彩蛋,就是Queue(队列)还没有讲,这次我们重点来看看Java中的Queue家族,总共涉及到18种Queue.这篇恐怕是市面上最全最 ...

  8. 匹夫细说C#:庖丁解牛迭代器,那些藏在幕后的秘密

    0x00 前言 在匹夫的上一篇文章<匹夫细说C#:不是“栈类型”的值类型,从生命周期聊存储位置>的最后,匹夫以总结和后记的方式涉及到一部分迭代器的知识.但是觉得还是不够过瘾,很多需要说清楚 ...

  9. 匹夫细说C#:庖丁解牛聊委托,那些编译器藏的和U3D给的

    0x00 前言 由于工作繁忙所以距离上一篇博客已经过去一个多月的时间了,因此决心这个周末无论如何也得写点东西出来,既是总结也是分享.那么本文主要的内容集中在了委托的使用以及内部结构(当然还有事件了,但 ...

随机推荐

  1. ios开发之IBOutlet和IBAction的区别

    IBOutlet 输出口是使用关键字IBOutlet声明的实例变量.控制器头文件中的输出口声明应如下所示: @property (nonatomic, retain) IBOutlet UIButto ...

  2. 关于jQuery的cookies插件2.2.0版设置过期时间的说明

    欢迎转载,转载请注明作者RunningOn jQuery应该是各位用JavaScript做web开发的常用工具了,它有些插件能非常方便地操作cookie. 不过非常让人郁闷的是,网上几乎所有人对于这些 ...

  3. YII 集成jquery

  4. MySQL账号授权操作

    Mysql权限控制 - 允许用户远程连接 设置mysql root密码: mysql -u root mysql> SET PASSWORD FOR 'root'@'localhost' = P ...

  5. android 学习第一步

    今天是2015年7月24号,今年下半年的主要学习方向是android,学习的目标是做出3个或以上的有实用价值的app.

  6. iOS证书详解--再转

    一.成员介绍1.    Certification(证书)证书是对电脑开发资格的认证,每个开发者帐号有一套,分为两种:1)    Developer Certification(开发证书)安装在电脑上 ...

  7. Java中的多线程总结(转)

      1.多线程概述 当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程.主要以下几个优点: 线程之间很容易实现共享内存 创建线程代价较小 Java语言内置多线程功能支持 2.线 ...

  8. OrCAD PSpice仿真库模型

    说明:本介绍包含了\capture\library\pspice和capture\library\pspice\advanls目录下所有库,但由于作者水平有限,介绍得也比较简单,有些说明可能不一定对. ...

  9. unity3d 幻灯片效果实现

    上一篇使用的是静态方式进行的加载,采用的数据结构为 数组 该篇文章则是使用动态加载的方式实现: this.objsOfRouses = Resources.LoadAll("images&q ...

  10. c# 函数相关练习

    1.输入一个正整数,求1!+2!+3!+...+n! 2.输入姓名,年龄,工作单位   我叫**,今年**岁了,现在在****工作   要求,在Main函数中接收这三个值   传到函数中打印 3.写一 ...