庖丁解牛FPPopover
作者: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的更多相关文章
- 庖丁解牛-----Live555源码彻底解密(RTP打包)
本文主要讲解live555的服务端RTP打包流程,根据MediaServer讲解RTP的打包流程,所以大家看这篇文章时,先看看下面这个链接的内容; 庖丁解牛-----Live555源码彻底解密(根据M ...
- Gradle 庖丁解牛(构建生命周期核心托付对象创建源代码浅析)
[工匠若水 http://blog.csdn.net/yanbober 未经同意严禁转载,请尊重作者劳动成果.私信联系我] 1 背景 上一篇<Gradle 庖丁解牛(构建源头源代码浅析)> ...
- ARM“庖丁解牛”之存储器管理单元MMU
转:http://blog.sina.com.cn/s/blog_a07635070101bcbt.html 最近笔者详细地学习了由杜春雷老师编写的<ARM体系结构与编程>.对ARM存储管 ...
- Android基础新手教程——4.3.2 BroadcastReceiver庖丁解牛
Android基础新手教程--4.3.2 BroadcastReceiver庖丁解牛 标签(空格分隔): Android基础新手教程 本节引言: 上节我们对BroadcastReceiver已经有了一 ...
- 庖丁解牛Linux内核分析 0x00:《庖丁解牛》
庖丁解牛 吾生也有涯,而知也无涯 .以有涯随无涯,殆已!已而为知者,殆而已矣!为善无近名,为恶无近刑.缘督以为经,可以保身,可以全生,可以养亲,可以尽年. 庖丁为文惠君解牛,手之所触,肩之所倚,足之 ...
- Mysql性能优化全揭秘-庖丁解牛
「为什么写」 一直想写数据库相关的文章,最直接的原因是数据库这块我们工作中每天都会用到,也是面试求职绕不开的话题,无论你是何种测试,优秀的数据库能力都会非常加分,最近我在总结数据库性能优化这块内容,性 ...
- 干货 | 45张图庖丁解牛18种Queue,你知道几种?
在讲<21张图讲解集合的线程不安全>那一篇,我留了一个彩蛋,就是Queue(队列)还没有讲,这次我们重点来看看Java中的Queue家族,总共涉及到18种Queue.这篇恐怕是市面上最全最 ...
- 匹夫细说C#:庖丁解牛迭代器,那些藏在幕后的秘密
0x00 前言 在匹夫的上一篇文章<匹夫细说C#:不是“栈类型”的值类型,从生命周期聊存储位置>的最后,匹夫以总结和后记的方式涉及到一部分迭代器的知识.但是觉得还是不够过瘾,很多需要说清楚 ...
- 匹夫细说C#:庖丁解牛聊委托,那些编译器藏的和U3D给的
0x00 前言 由于工作繁忙所以距离上一篇博客已经过去一个多月的时间了,因此决心这个周末无论如何也得写点东西出来,既是总结也是分享.那么本文主要的内容集中在了委托的使用以及内部结构(当然还有事件了,但 ...
随机推荐
- pkg-config相关的常用指令
pkg-config用途: 查询系统已安装库的基础信息(元信息) 1.查看所有的pkg-config库 pkg-config --list-all --list-all 列出pkg-config路径 ...
- [R] /usr/share/doc/apache2/README.Debian.gz
Contents======== Apache2 Configuration under Debian GNU/Linux Files and Directories in '/etc/apache2 ...
- while if 循环判断
temp=input("猜一下我想的那个数字吧:") guess=int(temp) while guess!=8: temp=input("诶呀错误了在输入一次吧:&q ...
- Java中的自动拆箱装箱(Autoboxing&Unboxing)
一.基本类型打包器 1.基本类型:long.int.double.float.boolean 2.类类型:Long.Integer.Double.Float.Boolean 区别:基本类型效率更高,类 ...
- ECMAScript 6 模块简介
任何平台的其中一个重要特性,除了需要支持代码库外就是模块.直到现在,Javascript还不支持原生的模块化.结果是,各种解决方案都将模块添加到类库中,比如CommonJS modules(部分由no ...
- Arbitrage
Description Arbitrage is the use of discrepancies in currency exchange rates to transform one unit o ...
- .net ref关键字在引用类型上的使用
只接上干货. namespace ConsoleApplication1 { class Person { public string UserName { get; set; } } class P ...
- SQL SERVER大数据分页
select * from (select rownum r, a.* from (select * from table_name order by ndatetime desc ) a wher ...
- 【转】Android:Layout_weight的深刻理解
原文网址:http://mobile.51cto.com/abased-375428.htm 本文详细介绍了Android布局中Layout_weight的属性,它是用来分配属于空间的一个属性,你可以 ...
- 数据结构(堆):POJ 1442 Black Box
Black Box Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 10658 Accepted: 4390 Descri ...