1. NSArray 与 NSSet
关于内存
NSArray 为有序的集合,存储数据的方式是连续的,在内存空间中的后一个数据是紧接着前一个数据的;NSSet 为无序的集合,散列存储,在内存空间中并不一定是连续的。
处理效率
NSSet 底层使用hash实现,所以需要保证里面存储的对象必须唯一,而数组可以存储相同的几个对象。由于NSSet是用hash实现,所以它查询速度快,但是不能进行下标操作。而数组可以进行下标操作,查询时逐个比对,速度稍慢。
2. UIScrollView 滑动的原理
可滑动的原因
UIScrollView 继承于 UIView,UIView不能滑动,而它居然能滑动。看看它的定义,其中,有一个panGestureRecognize滑动手势:
// Change `panGestureRecognizer.allowedTouchTypes` to limit scrolling to a particular set of touch types.
@property(nonatomic, readonly) UIPanGestureRecognizer *panGestureRecognizer API_AVAILABLE(ios(5.0));
// `pinchGestureRecognizer` will return nil when zooming is disabled.
@property(nullable, nonatomic, readonly) UIPinchGestureRecognizer *pinchGestureRecognizer API_AVAILABLE(ios(5.0));
// `directionalPressGestureRecognizer` is disabled by default, but can be enabled to perform scrolling in response to up / down / left / right arrow button presses directly, instead of scrolling indirectly in response to focus updates.
@property(nonatomic, readonly) UIGestureRecognizer *directionalPressGestureRecognizer API_DEPRECATED("Configuring the panGestureRecognizer for indirect scrolling automatically supports directional presses now, so this property is no longer useful.", tvos(9.0, 11.0));
若把这个手势移除:
[theScrollView removeGestureRecognizer:theScrollView.panGestureRecognizer];
结果scrollView失去了滑动的效果,原来是panGestureRecognize来实现的滑动效果。
可滑动的原理
UIScrollView 在接收滑动手势时,其子视图是怎么达到一起移动效果的呢?首先,我们必须知道UIView的frame和bounds的区别:
frame是相对父视图坐标系来决定自己的位置和大小;
bounds是相对于自身坐标系的位置和尺寸的。
因此,bounds的origin值为基于自身的坐标系,当自身坐标系的位置被改变了,里面的子视图肯定得变化。bounds和panGestureRecognize就是实现UIScrollView滑动效果的关键技术点。
自定义一个scrollView
我们可以根据上面的原理,自定义一个MyScrollView:
![](https://img.haomeiwen.com/i1928363/700e4521072637c8.png)
![](https://img.haomeiwen.com/i1928363/5209b5fe33ba73f4.png)
![](https://img.haomeiwen.com/i1928363/09bfac41082d594e.png)
看完是不是发现UIScrollView的滑动原理很简单!是的,就这么简单!
3. 响应链及其应用
概述
当我们点击屏幕之后到应用触发响应,这个过程中发生了什么?如何理解iOS的响应链机制,我们可以通过这个机制实现那些功能?本文将着重讲解iOS响应链机制。
点击屏幕到应用捕获事件
![](https://img.haomeiwen.com/i13180946/f0039c812c7b2b12.jpg)
点击屏幕后,经过系统的一系列处理,我们的应用接收到source0
事件,并从事件队列中取出事件对象,开始寻找真正响应事件的视图。
响应者寻找过程分析
寻找相应过程主要涉及到两个方法:
//判断点击的位置是不是在视图内
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;
//返回点击的视图
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
我们构造了一个简单的视图层级,BlueView/YellowView是两个根节点视图,RedView是他们的父视图。效果如下:
![](https://img.haomeiwen.com/i13180946/1bc4204474922450.png)
我们点击一下BlueView,其事件传递效果如下图:
![](https://img.haomeiwen.com/i13180946/1fa90f9379ed7285.jpg)
我们分析一下事件的传递机制。
![](https://img.haomeiwen.com/i13180946/fa5f4aeeb4f6a7bd.jpg)
如果到第4步先找的不是YellowView而是BlueView,则不会再去检查YellowView。
找到最终点击的视图之后开始执行响应逻辑。
事件响应过程
我们点击一下BuleView。
如果BuleView没有添加手势事件,则回调用BlueView的touches事件。
输入的日志:
2020-04-25 17:06:48.992130+0800 ResponderDemo[99230:899392] -[BlueView touchesBegan:withEvent:]
2020-04-25 17:06:48.996208+0800 ResponderDemo[99230:899392] -[BlueView touchesEnded:withEvent:]
如果BuleView也没有实现touches事件,那么则会调用父视图的touches事件。
输出日志如下:
2020-04-25 17:12:10.550668+0800 ResponderDemo[99522:904959] -[RedView touchesBegan:withEvent:]
2020-04-25 17:12:10.555622+0800 ResponderDemo[99522:904959] -[RedView touchesEnded:withEvent:]
如何获取响应链
UIResponder *nextResponder = self.buleView.nextResponder;
NSMutableString *pre = [NSMutableString stringWithString:@"--"];
NSLog(@"View3");
while (nextResponder) {
NSLog(@"%@%@", pre, NSStringFromClass([nextResponder class]));
[pre appendString:@"--"];
nextResponder = nextResponder.nextResponder;
}
-
如果有父视图则nextResponder指向父视图
-
如果是控制器根视图则指向控制器
-
控制器如果在导航控制器中则指向导航控制器的相关显示视图最后指向导航控制器
-
如果是根控制器则指向UIWindow
-
UIWindow的nexResponder指向UIApplication最后指向AppDelegate。
实践
- Alpha=0、子视图超出父视图的情况、userInteractionEnabled=NO、hidden=YES 视图会被忽略,不会调用hitTest
- 父视图被忽略后其所有子视图也会被忽略
点击穿透
平行视图:重新hitTest,返回想要点击的View
父子视图: 如果子视图是Button 将button的enable改为false。如果是view不要添加手势即可。
限定点击区域
- 判定点是不是在可视坐标内。 pointInside
题目:UIView所在的UIViewController怎么查找???😄
网友评论