iOS 용 이벤트 처리-hitTest : withEvent : 및 pointInside : withEvent :는 어떤 관련이 있습니까?
대부분의 Apple 문서는 잘 작성되었지만 ' iOS 용 이벤트 처리 안내서 '는 예외입니다. 거기에 설명 된 내용을 명확하게 이해하기는 어렵습니다.
문서는 말합니다
적중 테스트에서 창
hitTest:withEvent:
은보기 계층의 최상위보기를 호출 합니다. 이 메소드pointInside:withEvent:
는 YES를 리턴하는보기 계층 구조의 각보기를 재귀 적으로 호출 하여 터치가 발생한 범위 내에서 하위보기를 찾을 때까지 계층 구조를 진행합니다. 해당보기는 적중 테스트보기가됩니다.
따라서 hitTest:withEvent:
최상위 뷰만 시스템에서 호출합니다 pointInside:withEvent:
. 모든 하위 뷰를 호출하고 특정 하위 뷰에서 리턴이 YES 인 경우 pointInside:withEvent:
해당 하위 뷰의 서브 클래스 를 호출 합니까?
꽤 기본적인 질문 인 것 같습니다. 그러나 나는 당신에게 문서가 다른 문서만큼 명확하지 않다는 것에 동의합니다. 그래서 여기에 내 대답이 있습니다.
hitTest:withEvent:
UIResponder 의 구현은 다음을 수행합니다.
- 이 호출
pointInside:withEvent:
의self
- 반환 값이 NO이면를
hitTest:withEvent:
반환합니다nil
. 이야기의 끝. - 리턴이 YES 인 경우
hitTest:withEvent:
서브 뷰에 메시지를 보냅니다 . 최상위 하위 뷰에서 시작하여 하위 뷰가nil
객체 가 아닌 객체를 반환 하거나 모든 하위 뷰가 메시지를받을 때까지 다른 뷰로 계속 진행됩니다 . - 하위 뷰가
nil
처음에 객체 가 아닌 객체를hitTest:withEvent:
반환 하면 첫 번째 객체는 해당 객체를 반환합니다. 이야기의 끝. - 하위 뷰가
nil
객체 가 아닌 객체를 반환하지 않으면 첫 번째 뷰hitTest:withEvent:
는self
이 프로세스는 재귀 적으로 반복되므로 일반적으로 뷰 계층의 리프 뷰가 결국 반환됩니다.
그러나 hitTest:withEvent
다르게 수행하도록 재정의 할 수 있습니다 . 대부분의 경우 재정의 pointInside:withEvent:
가 더 간단하고 여전히 응용 프로그램에서 이벤트 처리를 조정할 수있는 충분한 옵션을 제공합니다.
서브 클래스와 뷰 계층을 혼동하고 있다고 생각합니다. 의사가 말하는 것은 다음과 같습니다. 이 뷰 계층 구조가 있다고 가정하십시오. 계층 구조로 클래스 계층 구조에 대해 이야기하는 것이 아니라 다음과 같이 뷰 계층 구조 내의 뷰를 말합니다.
+----------------------------+
|A |
|+--------+ +------------+ |
||B | |C | |
|| | |+----------+| |
|+--------+ ||D || |
| |+----------+| |
| +------------+ |
+----------------------------+
손가락을 안에 넣었다고 가정 해 봅시다 D
. 다음과 같은 일이 발생합니다.
hitTest:withEvent:
A
뷰 계층 구조의 최상위 뷰 에서에 호출됩니다 .pointInside:withEvent:
각 뷰에서 재귀 적으로 호출됩니다.pointInside:withEvent:
에 호출되어 다음A
을 반환합니다.YES
pointInside:withEvent:
에 호출되어 다음B
을 반환합니다.NO
pointInside:withEvent:
에 호출되어 다음C
을 반환합니다.YES
pointInside:withEvent:
에 호출되어 다음D
을 반환합니다.YES
- 반환 된보기
YES
에서는 계층 구조를 내려다보고 터치가 발생한 하위보기를 봅니다. 이 경우 fromA
,C
및D
이됩니다D
. D
적중 테스트보기입니다
iOS 에서이 적중 테스트 가 매우 유용하다는 것을 알았습니다.
- (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;
}
스위프트 4 편집 :
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.point(inside: point, with: event) {
return super.hitTest(point, with: event)
}
guard isUserInteractionEnabled, !isHidden, alpha > 0 else {
return nil
}
for subview in subviews.reversed() {
let convertedPoint = subview.convert(point, from: self)
if let hitView = subview.hitTest(convertedPoint, with: event) {
return hitView
}
}
return nil
}
답변 주셔서 감사합니다, 그들은 "오버레이"보기로 상황을 해결하는 데 도움이되었습니다.
+----------------------------+
|A +--------+ |
| |B +------------------+ |
| | |C X | |
| | +------------------+ |
| | | |
| +--------+ |
| |
+----------------------------+
가정 X
-사용자의 터치. pointInside:withEvent:
on B
은 리턴 NO
하므로 hitTest:withEvent:
리턴합니다 A
. UIView
가장 눈 에 잘 띄는 뷰 에서 터치를 받아야 할 때 문제를 처리하기 위해 카테고리를 작성했습니다 .
- (UIView *)overlapHitTest:(CGPoint)point withEvent:(UIEvent *)event {
// 1
if (!self.userInteractionEnabled || [self isHidden] || self.alpha == 0)
return nil;
// 2
UIView *hitView = self;
if (![self pointInside:point withEvent:event]) {
if (self.clipsToBounds) return nil;
else hitView = nil;
}
// 3
for (UIView *subview in [self.subviewsreverseObjectEnumerator]) {
CGPoint insideSubview = [self convertPoint:point toView:subview];
UIView *sview = [subview overlapHitTest:insideSubview withEvent:event];
if (sview) return sview;
}
// 4
return hitView;
}
- We should not send touch events for hidden or transparent views, or views with
userInteractionEnabled
set toNO
; - If touch is inside
self
,self
will be considered as potential result. - Check recursively all subviews for hit. If any, return it.
- Else return self or nil depending on result from step 2.
Note, [self.subviewsreverseObjectEnumerator]
needed to follow view hierarchy from top most to bottom. And check for clipsToBounds
to ensure not to test masked subviews.
Usage:
- Import category in your subclassed view.
- Replace
hitTest:withEvent:
with this
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
return [self overlapHitTest:point withEvent:event];
}
Official Apple's Guide provides some good illustrations too.
Hope this helps somebody.
It shows like this snippet!
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (self.hidden || !self.userInteractionEnabled || self.alpha < 0.01)
{
return nil;
}
if (![self pointInside:point withEvent:event])
{
return nil;
}
__block UIView *hitView = self;
[self.subViews enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
CGPoint thePoint = [self convertPoint:point toView:obj];
UIView *theSubHitView = [obj hitTest:thePoint withEvent:event];
if (theSubHitView != nil)
{
hitView = theSubHitView;
*stop = YES;
}
}];
return hitView;
}
The snippet of @lion works like a charm. I ported it to swift 2.1 and used it as an extension to UIView. I'm posting it here in case somebody needs it.
extension UIView {
func overlapHitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
// 1
if !self.userInteractionEnabled || self.hidden || self.alpha == 0 {
return nil
}
//2
var hitView: UIView? = self
if !self.pointInside(point, withEvent: event) {
if self.clipsToBounds {
return nil
} else {
hitView = nil
}
}
//3
for subview in self.subviews.reverse() {
let insideSubview = self.convertPoint(point, toView: subview)
if let sview = subview.overlapHitTest(insideSubview, withEvent: event) {
return sview
}
}
return hitView
}
}
그것을 사용하려면 다음과 같이 uiview에서 hitTest : point : withEvent를 재정의하십시오.
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
let uiview = super.hitTest(point, withEvent: event)
print("hittest",uiview)
return overlapHitTest(point, withEvent: event)
}
'IT story' 카테고리의 다른 글
Xcode : 빌드 실패, 오류 메시지 없음 (0) | 2020.06.17 |
---|---|
PHP를 사용하여 HTML에서 img src, title 및 alt를 추출하는 방법은 무엇입니까? (0) | 2020.06.17 |
SQL Server 복원 오류-액세스가 거부되었습니다 (0) | 2020.06.17 |
Android TextView에서 줄 바꿈을 추가하는 방법은 무엇입니까? (0) | 2020.06.17 |
Swift에서 Array의 처음 5 객체를 반환하는 방법은 무엇입니까? (0) | 2020.06.17 |