2013-08-05 1 views
11

2 개의 뷰가 있지만 1 개의 뷰를 (사실상) 크게 만들고 싶습니다. v1에 tapGesture를 놓으면 탭 제스쳐가 더 큰 히트 영역 인 으로 작동하지만 v2에 내 tapGesture를 배치하면 실제로 작동하지 않습니다 (실제로는 원래 경계 내에서가 아니라 tapGesture를 전혀 인식하지 못합니다.) 비록 내 TestView1 hittest 메서드를 통해 루프 및 포인트 프레임에 포함 된 얻을.hitTest : WithEvent 및 Subview

#import "ViewController.h" 

@interface TestView1 : UIView 
@end 

@implementation TestView1 

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    CGFloat radius = 100.0; 
    CGRect frame = CGRectMake(0, 0, 
           self.frame.size.width + radius, 
           self.frame.size.height + radius); 

    if (CGRectContainsPoint(frame, point)) { 
     return self; 
    } 
    return nil; 
} 

@end 

@interface TestView2 : UIView 
@end 

@implementation TestView2 

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    CGFloat radius = 100.0; 
    CGRect frame = CGRectMake(0, 0, 
           self.frame.size.width + radius, 
           self.frame.size.height + radius); 

    if (CGRectContainsPoint(frame, point)) { 
     return self; 
    } 
    return nil; 
} 
@end 

@interface ViewController() 
@end 

@implementation ViewController 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    TestView1 *v1 = [[TestView1 alloc] initWithFrame:CGRectMake(50.f, 50.f, 100.f, 100.f)]; 
    [self.view addSubview:v1]; 

    TestView2 *v2 = [[TestView2 alloc] initWithFrame:CGRectMake(0.f, 0.f, 100.f, 100.f)]; 
    v2.backgroundColor = UIColor.yellowColor; 
    [v1 addSubview:v2]; 

    UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(panGesture:)]; 
    [v2 addGestureRecognizer:gesture]; 
} 

- (void) panGesture:(UIPanGestureRecognizer *)recognizer 
{ 
    NSLog(@"tap"); 
} 
@end 

답변

10

뷰 계층 구조를 탐색하지 않습니다. 문서에서 :

이 메서드는 각 하위 뷰에 pointInside : withEvent : 메시지를 보내서 뷰 계층을 탐색하여 어떤 하위 뷰에서 터치 이벤트를 받아야하는지 결정합니다. pointInside : withEvent :가 YES를 반환하면 하위 뷰의 계층 구조가 가로 지릅니다. 그렇지 않으면 뷰 계층 구조의 해당 분기가 무시됩니다.

pointInside:withEvent: 만 구현하면됩니다. hitTest:withEvent:을 재정의해서는 안됩니다. 표준 구현이 pointInside:withEvent:이라는 구현을 호출하고 계층 구조를 탐색하는 데 필요한 모든 작업을 수행하기 때문입니다.

과 같이 구현 :

@interface TestView1 : UIView 
@end 

@implementation TestView1 

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    CGFloat radius = 100.0; 
    CGRect frame = CGRectMake(0, 0, 
           self.frame.size.width + radius, 
           self.frame.size.height + radius); 

    return (CGRectContainsPoint(frame, point)); 
} 

@end 

@interface TestView2 : UIView 
@end 

@implementation TestView2 

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    CGFloat radius = 100.0; 
    CGRect frame = CGRectMake(0, 0, 
           self.frame.size.width + radius, 
           self.frame.size.height + radius); 

    return (CGRectContainsPoint(frame, point)); 
} 

@end 

을 지금, 당신은 당신에게 달려 모두보기를해야하는지 여부. 이미 v1의 터치 가능 영역을 확장하는 데 성공했으며, 위의 코드를 사용하면 v2를 v1의 하위 뷰로 사용할 수 있습니다.

그러나 TestView1TestView2 구현은 정확히 동일합니다 (원래 게시물에서도 마찬가지입니다). 그렇다면 왜 이들을 분리해야합니까? v1과 v2를 같은 클래스의 두 인스턴스로 만들 수 있습니다 (예 : 다음과 같이 TestView, 그리고 그것들을 초기화 :

TestView *v1 = [[TestView alloc] initWithFrame:CGRectMake(50.f, 50.f, 100.f, 100.f)]; 
[self.view addSubview:v1]; 
v1.clipsToBounds = YES; 

TestView *v2 = [[TestView alloc] initWithFrame:CGRectMake(0.f, 0.f, 100.f, 100.f)]; 
v2.backgroundColor = UIColor.yellowColor; 
[v1 addSubview:v2]; 
+0

샘 코드는 터치 영역을 오른쪽 아래로 확장합니다. CGFloat 패딩 = 50; CGRect frame = CGRectMake (-padding, -padding, self.frame.size.width + padding, self.frame.size.height + padding); ' – Nuthatch

+0

@Nuthatch Andy의 원래 코드도 마찬가지입니다. 그러나 좋은 지적 – LoPoBo

2

귀하의 질문에서 알 수있는 한, 귀하는 V1 위에 더 큰 V2를 추가했습니다. 그래서 V2는 V1의 범위 내에서만 터치 할 수 있습니다. 따라서 V2의 추가 영역에서는 사용자의 제스처가 인식되지 않습니다.

+0

당신은 고칠 수 있다고 생각합니까? –

+0

죄송합니다, pls 귀하의 요구 사항을 정교하게 만드십시오?. – Vignesh

+0

나는 tapgesture가 더 큰 탭 영역을 가진 V2에 있도록하고 싶습니다. –

2

당신은 구현해야 방법 : 다음

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    CGFloat radius = 100.0; 
    CGRect frame = CGRectMake(0, 0, 
           self.frame.size.width + radius, 
           self.frame.size.height + radius); 

    if (CGRectContainsPoint(frame, point)) { 
     return YES; 
    } 

    return [super pointInside:point withEvent:event]; 
} 

그리고 : 문서

에서

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    CGFloat radius = 100.0; 
    CGRect frame = CGRectMake(0, 0, 
           self.frame.size.width + radius, 
           self.frame.size.height + radius); 

    if (CGRectContainsPoint(frame, point)) { 
     return self; 
    } 
    return [super hitTest:point withEvent:event; 
} 

이 방법은 pointInside를 전송하여 뷰 계층을 통과 : withEvent : 각 하위보기에 어떤 메시지가 표시되는지 결정하려면 하위보기에 터치 이벤트가 수신되어야합니다. pointInside : withEvent : 이 YES를 반환하면 하위 뷰의 계층 구조가 가로 지릅니다. 그렇지 않으면보기 계층의 분기가 무시됩니다. 이 메소드를 직접 호출 할 필요는 거의 없지만이 메소드를 재정 의하여 하위 뷰의 터치 이벤트를 숨길 수 있습니다.

이 방법은 숨겨져 있거나 사용자 상호 작용을 사용하지 않거나 0.01 미만의보기 수준을 가진보기 개체를 무시합니다. 이 방법 은 조회를 결정할 때보기의 콘텐츠를 고려하지 않습니다. 따라서 지정된 지점이 해당보기의 내용의 투명 부분 인 에 있더라도보기를 계속 반환 할 수 있습니다.

5

v2에 터치 이벤트가 수신되지 않습니다. 귀하의 v1 주변 지역을 클릭하면 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)eventself을 반환하기 때문에 모든 터치 이벤트의 대상인 조회 테스트보기 인 "v1"이라고 선언했기 때문입니다.
당신 V1의 만질 수 아르를 확장하는 올바른 방법은 TestView1TestView2- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event을 구현하는 것입니다 :

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    CGFloat radius = 100.0; 
    CGRect frame = CGRectMake(0, 0, 
          self.frame.size.width + radius, 
          self.frame.size.height + radius); 

    if (CGRectContainsPoint(frame, point)) { 
     return YES; 
    } 
    return [super pointInside:point withEvent:event]; 
} 

위의 코드는 당신이 당신의 V1 주변 지역을 클릭하면, 그것은 예, 당신이 "선언을 의미합니다 나를 만졌고, 누가 그걸 처리 할 수 ​​있는지 확인해 주겠다. 어쩌면 그것은 내 서브뷰 중 하나일지도 모른다. " 그래서 히트 테스트가 계속되며 v1은 서브 뷰 v2가 최상위보기이므로 v2가 클릭 이벤트의 대상임을 알게됩니다.

v2가 어떻게 v2를 알 수 있는지 물어보십시오.

@implementation UIView 
//... 
//... 

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    return CGRectContainsPoint(self.bounds, point); // Honestly tell others if the point is inside the bounds. That's the normal case. 
} 

// This method returns a hit-test view who or whose gesture recognizer is responsible for handling the events 
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
{ 
    for(UIView *aSubview in self.subviews) 
    { 
     // Ask each subview if the point falls in its area. 
     if ([aSubview pointInside:[self convertPoint:point toView:aSubview] point withEvent:event]) 
     { 
      return [aSubview hitTest:[self convertPoint:point toView:aSubview] withEvent:event]; 
     } 
    } 

    // If no one can handle the event. 
    return self; 
} 

//... 
//... 
@end 

이 코드

는 간단하게하기 위해, 계정에 userInteractionEnable, alpha 및 기타 물건을 촬영하지 않은 : 여기에 트릭을 공개 할 의사 코드입니다.
TestView1- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event에서 [super pointInside:point withEvent:event];으로 전화하면 포인트가 v2의 영역에 속하는지 묻습니다.v2의 대답이 예이고 하위보기가 없기 때문에 v2는 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event으로 되돌아갑니다.

모든 이야기입니다.

+0

"아무도 이벤트를 처리 할 수 ​​없다"면 포인트가 '자체'안에 있는지 확인해야합니다. 그렇지 않으면 우연히 전체 뷰 계층 구조에서 터치를 차단할 수 있습니다. –

2

당신이 그것을하고있는 방법은 제대로 할 수 있지만 어렵다.

나는 공통 슈퍼 뷰에 UITapGestureRecognizer을 추가 한 다음 탭 위치에 따라 수신기보기가 어떤 것인지 결정하는 것이 좋습니다. 견해가 일반적인 수퍼가없는 경우

- (void)onTap:(UITapGestureRecognizer*)tapRecognizer { 
    CGPoint touchPosition = [tapRecognizer locationInView:self]; 

    CGRect view1Frame = self.view1.frame; 
    view1Frame.width += 100; 
    view1Frame.height += 100; 

    if (CGRectContainsPoint(view1Frame, touchPosition)) { 
     [self.view1 handleTap]; 
     return; 
    } 

    ... 
} 

, 단지 더 큰 투명보기에서 각각 포함이 더 큰보기로 인식기를 추가합니다.