2013-08-05 1 views
11

У меня есть 2 просмотра, но я хочу сделать 1 вид (фактически) больше. , если я поместил свой tapGesture на v1, жест нажатия будет работать с более крупной областью удалений , но если я поместил свой tapGesture на v2, он не сработает (на самом деле он не распознает tapGesture вообще, даже не в исходных границах), хотя я прохожу через мой метод testView1 hittest и точки попадают в рамку.hitTest: WithEvent и Subviews

#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, тогда проходит иерархия subview; в противном случае его ветвь иерархии представлений игнорируется.

Вам нужно всего лишь реализовать 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.

Однако, TestView1 и TestView2 реализации точно такие же (даже в вашем оригинальном посте), так зачем вам их разделять? Вы можете сделать 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 padding = 50; CGRect frame = CGRectMake (-padding, -padding, self.frame.size.width + padding, self.frame.size.height + padding); ' – Nuthatch

+0

@Nuthatch Так будет и оригинальный код Andy. Но хорошая точка – LoPoBo

2

Насколько я понимаю из вашего вопроса, вы добавили больше V2 поверх V1. поэтому V2 будет осязаемым только в пределах V1. Таким образом, ваш жест не распознается в дополнительной области V2.

+0

Как вы думаете, его можно исправить? –

+0

Извините, не могли бы вы разработать свое требование ?. – 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: сообщение для каждого подсмотра, чтобы определить, какие subview должен получить событие касания. Если pointInside: withEvent: возвращает YES, тогда проходит иерархия subview; в противном случае его ветвь игнорируется. Вам редко нужно называть этот метод самостоятельно, но вы можете переопределить его, чтобы скрыть события касания от subviews.

Этот метод игнорирует скрытые объекты вида, которые отключили пользовательские взаимодействия или имеют уровень альфа менее 0,01. Этот метод не учитывает содержание представления при определении удара. Таким образом, представление все равно можно вернуть, даже если указанная точка находится в прозрачной части содержимого этого представления .

5

Ваш v2 не получит никаких событий касания. потому что, когда вы нажимаете область вокруг своего v1, она возвращает self в своем - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event, что означает, что вы заявили, что это «v1» - это тестовое представление, которое является местом назначения всех событий касания.
Правильный способ расширить осязаемый Арес вашего v1 является реализация - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event в вашем TestView1 и 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); 

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

Код выше означает, что при нажатии на области вокруг v1, он заявляет: «Да, у вас есть коснулся меня, и я проверю, кто с этим справится. Возможно, это я, может быть, это одно из моих подходов ». Таким образом, тест-ответ продолжается, и v1 найдет, что его subview v2 является самым большим видом, таким образом, v2 является местом назначения вашего события click.

Вы можете спросить, как может v1 знать, что 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 и другие вещи во внимание, для простоты.
Когда вы звоните [super pointInside:point withEvent:event]; в TestView1- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)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; 
    } 

    ... 
} 

Если мнения не имеет общее SuperView, просто вставлять каждый из них в большем прозрачном зрения и добавить распознаватель к этому большему зрению.

 Смежные вопросы

  • Нет связанных вопросов^_^