做同城特价的网站,泉州北京网站建设价格,北京海淀建设中路哪打疫苗,高端设计网站源码IOS面试题(UIView) ----- 事件传递机制 - 简书 面试题#xff1a; 在以下场景中#xff0c;父视图 ParentView 上有三个子视图 ViewA、ViewB 和 ViewC。ViewA 完全位于 ParentView 的范围内#xff0c;ViewB 有一半在 ParentView 的范围内#xff0c;而 ViewC 完全位于 Par…IOS面试题(UIView) ----- 事件传递机制 - 简书 面试题 在以下场景中父视图 ParentView 上有三个子视图 ViewA、ViewB 和 ViewC。ViewA 完全位于 ParentView 的范围内ViewB 有一半在 ParentView 的范围内而 ViewC 完全位于 ParentView 的范围之外。假设用户在 ViewA、ViewB 和 ViewC 的区域上触摸屏幕请描述事件处理的顺序和机制并解释哪些视图将有机会响应触摸事件。
追问1 如果 ViewA 和 ViewB 能够响应触摸事件但 ParentView 的 clipsToBounds 属性设置为 YES那么当用户触摸 ViewB 位于 ParentView 范围之外的部分时事件的处理情况会如何变化
追问2 在同样的设置下如果 ViewA、ViewB 和 ViewC 都不处理触摸事件即它们没有重写 touchesBegan:withEvent: 方法或者在重写的方法中调用了 super请说明事件处理将如何沿着响应链传递。
追问3 如果希望当用户触摸 ViewC 时即使它位于 ParentView 范围之外ViewC 也能响应事件你将如何修改 ParentView 的代码或属性以实现这一行为
提问4: 如果 ViewD 的 userInteractionEnabled 属性被设置为 NO点击 ViewD 时会发生什么
提问5: 如果 ViewD 被部分遮挡例如被另一个视图 ViewE 遮挡点击 ViewD 被 ViewE 遮挡的部分会发生什么
提问6: 假设用户在 ViewA、ViewB 和 ViewC 的区域上触摸屏幕请描述事件处理的顺序和机制并解释哪些视图将有机会响应触摸事件。请回答每个view的传递和响应的顺序
提问7:事件的传递和响应分别是深度优先遍历还是广度优先遍历怎么证明
viewithtag呢
追问8:view上同时有事件和手势点击view之后会怎么处理
追问9:
父视图有一个单击手势它上面有一个子视图subview是UIControl绑定action事件。这个时候点击这个subview会发生什么如果是UIButton呢
//10-12尚需验证
追问10 在一个复杂的视图层级结构中ViewF覆盖在ViewG上而且ViewF的userInteractionEnabled属性被设置为NO那么当用户点击ViewF时会发生什么
追问11 如果一个视图比如ViewH重写了touchesBegan:withEvent:方法并在方法中没有调用super那么这个视图的父视图会收到这个触摸开始事件吗
追问12 如果一个视图比如ViewI的alpha属性被设置为0即完全透明那么用户点击这个视图的区域时触摸事件会被这个视图接收吗 当用户在 ViewA、ViewB 和 ViewC 的区域上触摸屏幕时iOS 系统会首先将触摸事件传递给主窗口然后由主窗口沿着视图层级结构向下传递给最顶层的视图即 ParentView。接下来
如果用户触摸在 ViewA 上系统会将事件传递给 ViewA因为 ViewA 完全位于 ParentView 内并且是位于触摸点最上层的视图。如果 ViewA 能够处理该事件它将成为第一响应者并对事件进行处理。如果用户触摸在 ViewB 的 ParentView 范围内的部分系统同样会将事件传递给 ViewB。ViewB 将有机会处理该事件。如果用户触摸在 ViewC 上因为 ViewC 完全位于 ParentView 范围之外通常情况下 ViewC 不会接收到事件除非特殊处理。事件不会传递给 ViewC因为它不在 ParentView 的边界内。
追问1回答
如果 ParentView 的 clipsToBounds 属性设置为 YES这意味着 ParentView 会裁剪掉超出其边界的子视图部分。因此即使 ViewB 能够响应触摸事件当用户触摸 ViewB 在 ParentView 范围之外的部分时触摸事件不会传递给 ViewB因为该部分被裁剪掉了对用户来说是不可见的。
追问2回答
如果 ViewA、ViewB 和 ViewC 都不处理触摸事件事件将会沿着响应链向上传递。以下是事件传递的顺序
对于 ViewA事件首先传递到 ViewA由于 ViewA 不处理该事件事件将传递给 ParentView。对于 ViewB 的 ParentView 范围内的部分情况与 ViewA 相同事件最终也会传递给 ParentView。ViewC 不会接收事件因为它在 ParentView 范围之外。如果 ParentView 也不处理触摸事件事件会继续沿响应链向上传递给包含 ParentView 的视图控制器最终可能到达应用程序对象。
追问3回答
要使位于 ParentView 范围之外的 ViewC 能够响应触摸事件可以重写 ParentView 的 hitTest:withEvent: 方法。这个方法决定了哪个子视图应该接收触摸事件。通过在 hitTest:withEvent: 中添加检查即使 ViewC 位于 ParentView 范围之外我们也可以将事件手动转发给它
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {UIView *view [super hitTest:point withEvent:event];if (view nil) {// 将触摸点转换到 ViewC 的坐标系CGPoint pointInC [self convertPoint:point toView:ViewC];// 检查触摸点是否在 ViewC 内if ([ViewC pointInside:pointInC withEvent:event]) {view ViewC;}}return view;
}这段代码首先调用 super 的 hitTest:withEvent: 方法来尝试按常规规则找到一个响应者。如果没有找到并且触摸点正好在 ViewC 的范围内则直接将 ViewC 作为响应者返回。这样即使 ViewC 不在 ParentView 的clipsToBounds 边界内它也能接收和处理触摸事件。
提问4: 如果 ViewD 的 userInteractionEnabled 属性被设置为 NO点击 ViewD 时会发生什么
当 ViewD 的 userInteractionEnabled 属性被设置为 NO 时ViewD 将不会接收任何触摸事件也就是说它会忽略所有的触摸。因此点击 ViewD 时触摸事件会穿透 ViewD 传递给它下面的视图。如果点击的是 ViewD 在 ViewA 上的半边那么触摸事件会传递给 ViewA。如果点击的是 ViewD 在 ParentView 上的半边那么触摸事件会传递给 ParentView。
提问5: 如果 ViewD 被部分遮挡例如被另一个视图 ViewE 遮挡点击 ViewD 被 ViewE 遮挡的部分会发生什么
假设 ViewE 是另一个子视图它被添加到 ParentView 上并且位于 ViewD 的上方那么点击 ViewD 被 ViewE 遮挡的部分时触摸事件首先会被 ViewE 接收因为它位于最上层。如果 ViewE 能够接收并处理该触摸事件那么事件处理到此结束。如果 ViewE 不处理事件可能是因为 userInteractionEnabled 被设置为 NO或者它选择不处理该事件那么事件将会穿透 ViewE 并传递给它下面的视图也就是 ViewD。如果 ViewD 同样选择不处理或者无法处理该触摸事件那么事件最终会传递给 ViewD 的父视图 ParentView。如果 ViewD 被 ViewE 遮挡的部分正好是在 ViewA 之上那么 ViewA 不会接收到该触摸事件因为 ViewE 和 ViewD 都位于它的上方。 上述问题中ViewD和ViewE是什么关系兄弟关系还是父子关系
在上述问题中ViewD 和 ViewE 被描述为都是 ParentView 的子视图因此它们之间是兄弟关系sibling relationship。兄弟视图是相对于共同的父视图来说的——在这个例子中ParentView 是它们共同的父视图。
视图层级结构中兄弟视图的相对层次谁在上谁在下取决于它们被添加到父视图的顺序。后添加的视图会覆盖在先添加的视图上面。如果 ViewE 被添加到 ParentView 上且在 ViewD 之后添加那么 ViewE 将会覆盖 ViewD处于更上层的位置。
当处理触摸事件时iOS 首先会将事件传递给最上层的视图即在视图层级中最接近用户的那个视图。如果 ViewE 覆盖了 ViewD点击重叠区域时系统会先把事件传递给 ViewE。只有当 ViewE 不能处理该事件时事件才会向下穿透到下一层的 ViewD或者如果 ViewD 也不处理最终传递到 ParentView。 ViewD不是ViewE的父视图为什么ViewE 不处理事件时事件将沿响应链传递给 ViewD 在iOS的事件处理系统中当一个视图比如 ViewE不处理一个事件时这个事件通常会沿着响应链向上传递。响应链是由当前响应者比如控件或视图及其上级响应者如父视图或视图控制器组成的链式结构。
如果 ViewE 和 ViewD 是兄弟视图即它们都是同一个父视图 ParentView 的子视图事件的传递会有点不同。当用户在 ViewE 上触摸屏幕而 ViewE 不处理该事件时iOS会将事件传递给 ViewE 的父视图也就是 ParentView而不是直接传递给 ViewD。
事件传递给 ParentView 后ParentView 有机会接收并处理事件。如果 ParentView 也不处理该事件那么 ParentView 的父视图或者它的视图控制器将有机会处理事件如此类推直到找到能够处理该事件的响应者。
然而如果 ViewE 被设计成不应该拦截某些事件或者在某些情况下应该把事件传递给特定的兄弟视图比如 ViewD开发者可以通过覆盖 hitTest:withEvent: 或 pointInside:withEvent: 方法来自定义事件的传递逻辑。通过这种方式开发者可以改变默认的事件传递行为使得事件在 ViewE 不处理时直接传递给 ViewD而不是传递给它们共同的父视图 ParentView。然而这种做法相对少见因为它可能会违反视图层次结构的自然流程因此需要谨慎使用。
提问6:
在iOS开发中事件的传递和响应是遵循的原则是从最底层的子视图开始然后是其父视图再到上一层的父视图依次往上。同时只有在父视图范围内的子视图才能接收到事件。具体到这个场景中 如果用户在ViewA区域触摸屏幕事件将首先传递给ViewA。ViewA作为第一响应者有机会首先处理这个触摸事件。如果ViewA不处理这个事件那么事件会向上传递由ParentView进行处理。 如果用户在ViewB区域触摸屏幕事件将首先传递给ViewB。同样ViewB作为第一响应者有机会首先处理这个触摸事件。如果ViewB不处理这个事件那么事件会向上传递由ParentView进行处理。 如果用户在ViewC区域触摸屏幕因为ViewC完全位于ParentView的范围之外所以它无法接收到触摸事件也就没有机会处理这个事件。这个事件会直接由ParentView进行处理。
所以事件的传递和响应顺序为ViewA/ViewB - ParentView。而ViewC因为完全位于ParentView的范围之外因此无法接收和处理触摸事件。
以上是基于默认情况的分析具体的事件处理还会受到具体代码的影响例如是否重写了hitTest:withEvent:或者pointInside:withEvent:方法或者是否设置了userInteractionEnabled、isHidden或者alpha属性等。
提问7:
在iOS开发中UIView的viewWithTag:方法使用的是深度优先遍历Depth-First Search。
这个方法会先检查自身的tag如果匹配就直接返回自身。如果不匹配它会遍历其所有子视图对每个子视图都调用这个视图的viewWithTag:方法。这样就形成了一个递归的过程最终形成的遍历路径是深度优先的。这个过程会一直到达视图树的最深层如果还没有找到就返回nil。
事件的传递和响应在iOS中确实是遵循深度优先遍历的原则。我们可以通过以下方式来验证这一点
首先我们可以创建一个视图层级结构比如ParentView中包含ChildView1和ChildView2ChildView1中又包含GrandChildView。然后我们在每一个视图的touchesBegan:withEvent:方法中打印一些信息。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {NSLog(%, self);[super touchesBegan:touches withEvent:event];
}当我们在GrandChildView上点击时我们会看到控制台上首先打印出GrandChildView的信息然后是ChildView1的信息最后是ParentView的信息。这就是深度优先遍历的表现。
其次深度优先遍历的原则也体现在UIView的hitTest:withEvent:方法中。在这个方法中UIView会首先检查它的所有子视图然后是它自己最后是它的父视图。这个过程就是深度优先遍历。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {if (!self.isUserInteractionEnabled || self.isHidden || self.alpha 0.01) {return nil;}if ([self pointInside:point withEvent:event]) {for (UIView *subview in [self.subviews reverseObjectEnumerator]) {CGPoint convertedPoint [subview convertPoint:point fromView:self];UIView *hitTestView [subview hitTest:convertedPoint withEvent:event];if (hitTestView) {return hitTestView;}}return self;}return nil;
}这段代码首先检查自己的所有子视图由于子视图数组是按照添加顺序排列的所以这里使用reverseObjectEnumerator进行了反序遍历确保后添加的子视图能够优先接收事件然后是自己最后返回nil表示事件需要继续向上传递给父视图。这个过程也是深度优先遍历。 追问8:
在iOS中手势识别器和触摸事件可以并存。当一个视图同时添加了触摸事件和手势识别器时当触摸开始手势识别器和视图都可以接收到触摸事件。但是手势识别器会首先接收并处理触摸事件如果手势识别器识别出了手势那么它就会“吞掉”这次触摸事件视图的触摸事件处理方法就不会被调用如果手势识别器没有识别出手势那么这次触摸事件就会被视图的触摸事件处理方法接收并处理。
要注意的是这种行为可以通过手势识别器的cancelsTouchesInView属性来改变。如果将该属性设置为NO那么即使手势识别器识别出了手势视图的触摸事件处理方法仍然会被调用。另外还可以通过delegate来更精细的控制手势识别器和触摸事件的交互行为。
追问9:
实际上uicontrol不会响应这个事件会给父视图响应但是上uibutton会响应这个事件不会给父视图响应。
在iOS中触摸事件的传递是有规律的。默认情况下触摸事件会从最底层的视图开始如果这个视图不处理事件就会传递给上一层的视图直到找到可以处理这个事件的视图为止。
UIButton是UIControl的子类它可以接收并处理触摸事件。当你在UIButton上触摸时UIButton会响应这个事件并且阻止事件向更上层的视图传递。
而UIControl本身并不会处理触摸事件它只是一个可以接收触摸事件的容器。当你在UIControl上触摸时如果它没有子视图可以处理这个事件那么这个事件就会继续向上传递由它的父视图来处理。
如果你希望UIControl能够像UIButton一样处理触摸事件那么你需要自己在UIControl中添加对触摸事件的处理。你可以通过重写UIControl的触摸事件处理方法比如touchesBegan:withEvent:来实现这个目标。
在这种情况下会先执行手势识别器的行为然后再执行UIButton的点击事件。这是因为UIButton内部有一个私有的手势识别器用于处理点击事件并且这个手势识别器的优先级要高于其他手势识别器。
当你点击UIButton时首先是UIButton的私有手势识别器捕获到这个点击事件并触发UIButton的点击行为。然后如果UIButton的点击行为没有消耗这个点击事件也就是说没有阻止这个点击事件的进一步传递这个点击事件就会继续传递给UIButton的父视图的手势识别器。
对于UIControl的情况由于UIControl本身并没有手势识别器所以当你点击UIControl时这个点击事件会直接传递给UIControl的父视图的手势识别器。
总的来说UIButton和UIControl的主要区别在于UIButton有一个私有的手势识别器用于处理点击事件而UIControl没有。因此当UIButton和UIControl的父视图都有手势识别器的时候UIButton的点击行为会优先于父视图的手势识别器而UIControl的点击事件则会直接传递给父视图的手势识别器。
//10-12尚需验证
追问10 当用户点击ViewF时由于ViewF的userInteractionEnabled属性被设置为NO所以ViewF无法接收和处理触摸事件触摸事件会直接传递给它的下一个响应者也就是ViewG。
追问11 如果ViewH重写了touchesBegan:withEvent:方法并在方法中没有调用super那么ViewH的父视图将不会收到这个触摸开始事件。因为在视图的事件处理方法中不调用super会阻止事件的继续传递。
追问12 即使视图ViewI的alpha属性被设置为0完全透明只要它的userInteractionEnabled属性为YES用户点击这个视图的区域时触摸事件仍然会被这个视图接收。在iOS中视图的透明度不影响其接收触摸事件的能力。