我在屏幕上添加了几个UIView对象(例如5个),一个在另一个中。例如,view5.superview = view4,view4.superview = view3,view3.superview=view2,view2.superview = view1。对于所有这些UIView,我设置了uitapgesturerecognizer;对于视图1-4,我只在回调中执行NSLog(@“抽头%@",self),而对于view5抽头,我设置了以下操作:从层次结构中删除view4,然后将相同的对象view4‘放在层次结构的相同位置。这个对象还包含设置了view5‘的UITapGestureRecognizer (实际上,我用类似的标记替换了一部分标记)。
然后我开始点击view5。有一段时间,view5一直在捕捉它的点击,一切都还好,但是后来随机点击次数(每次这个数字不同),一个视图1-4对象开始捕捉这个点击,尽管我们仍然在点击view5。整个问题都是随机的--有时在第10次发射时发生,有时在第2次发射时发生。有时,错误的对象会在第一个点击时就开始捕捉水龙头。而且,我也不知道什么东西会抓住一个水龙头,当一切出错。视图框架(n+1)被设置为框架视图(N)的一半,而用于view1的帧(0,0320,460)。
上面描述的ui对象的所有操作都是在主线程中进行的,我所讲的关于iOS 4.3-6.1的所有操作都有更复杂的例子。但是iOS7把它变成了一种随机的地狱。
更新:我创建了一个示例项目,以简化调试过程。点击时没有添加/删除子视图操作。屏幕上只有4个视图,点击应用程序就可以记录被点击的视图。因此,您需要点击最小的视图(4)。如果你看到"tap 4…“在日志中--如果一切正常运行,停止并再次运行等等。在某些运行(可能在10+成功运行之后),您将不会在第一行中看到"tap 4“,您将看到"tap 1”或"tap 2“或"tap 3",并且会继续这样做--这些都是不好的情况。
示例项目可以从这里下载:http://tech.octopod.com/test/BuggySample.zip (存档中只有33 Kb )。
更新2
我们已经向苹果发布了一个bug,当我们得到一些反馈时,我会在这里发布。然而,任何好的解决办法都将不胜感激!
更新3
Yuvrajsinh提供的解决方案实际上正在处理示例项目。不幸的是,它仍然无助于解决最初出现的主要项目中出现的问题。现在的主要原因是,如果任何没有自我手势的视图都放置在可点击的内容上,那么它下面的随机视图元素将开始捕获交互(而不是使用交互手势集的顶部视图)。你有什么办法可以解决这个问题吗?更新后的示例可以从这里下载:http://tech.octopod.com/test/BuggySample2.zip
发布于 2013-11-10 16:34:14
因为问题只发生在iOS 7中,所以可以使用新的委托方法之一来解决这个问题:
– gestureRecognizer:shouldRequireFailureOfGestureRecognizer:
– gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:我通过实现gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer并“爬行”到手势视图的superview来解决这个问题,这样如果我发现superview的手势与提供的手势相等,我就可以返回“是”。我在这里详细说明了我的全部决议:https://stackoverflow.com/a/19659848/1147934。
解释
iOS 7中手势识别器的问题是,一个超级视图的手势是在它的一个子视图手势接收到它的触摸之前接收到它的触摸。这导致superview手势识别,然后取消子视图的识别器.这是(不正确的?)苹果公司也收到了多个错误文件。有人指出,苹果并不能保证手势接受触摸的顺序。我认为很多“我们”依赖于iOS 7中改变的实现细节,这就是为什么我们使用新的委托方法,这些方法似乎是为了帮助我们解决这个问题而设计的。
注意:我通过使用自己的子目录识别器进行了广泛的测试,记录了所有的触摸,并发现识别器失败的原因是superview手势在子视图手势出现在大约5%的情况下之前接收到了触摸。每次发生这种情况,都会发生失败。如果你有很多手势的“深层”层次结构,那么这种情况就会发生得更频繁。
新的委托方法可能会令人困惑,因此您需要仔细阅读它们。
我正在使用这个方法(我已经重命名了参数,以使它们更容易理解)
– gestureRecognizer:(UIGestureRecognizer *)thisRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *) otherRecognizer。
如果您返回“是”,则提供的手势识别器otherRecognizer将要求thisRecognizer在被识别之前失败。这就是为什么,在我的回答中,我爬上了superview层次结构,以检查它是否包含有otherRecognizer的superview。如果是这样的话,我希望otherRecognizer要求thisRecognizer失败,因为thisRecognizer在子视图中,并且在它的superview手势被识别之前应该会失败。这将确保子视图手势在他们的superview手势之前被识别。讲得通?
替代方案
我可以用另一种方式使用:
– gestureRecognizer:(UIGestureRecognizer *)thisRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherRecognizer
现在,我需要在整个子视图层次结构中爬行,检查otherRecognizer是否在其中,如果是,则返回YES。我不使用这种方法,因为爬行整个子视图层次结构比检查superview层次结构要困难和昂贵得多。爬行子视图层次结构必须是一个递归函数,而我可以使用一个简单的while循环来检查superview的层次结构。因此,我推荐我概述的第一种方法。
警告!
小心使用gestureRecognizer:shouldReceiveTouch:。问题是哪个手势先被触摸(取消另一个手势).这是一个解决冲突的问题。如果您实现了gestureRecognizer:shouldReceiveTouch:,如果该子视图手势失败,您将面临拒绝superview手势的风险,因为您必须在可能识别子视图手势时猜测。由于触摸之外的其他原因,子视图手势可能会失败,因此您必须知道实现细节才能正确猜测。当子视图手势失败时,您想要识别superview手势,但是您实际上并不需要知道它是否会在它实际失败之前失败。如果子视图手势失败,通常您希望superview手势能够识别。这是正常的响应链(子视图superview),如果您处理这个问题,您可能会以意外的行为结束。
发布于 2013-10-16 06:39:24
我在您的代码中做了一些修改,我也对它进行了大量的测试,并且没有生成问题。
在创建视图时,我将标记设置为每个视图以区分它:
View1234 *v1 = [[View1234 alloc] initWithId:@"1"];
v1.tag =1;
v1.backgroundColor = [UIColor redColor];
v1.frame = CGRectMake(0, 0, 320, 460);
View1234 *v2 = [[View1234 alloc] initWithId:@"2"];
v2.tag=2;
v2.backgroundColor = [UIColor greenColor];
v2.frame = CGRectMake(0, 0, 160, 230);
View1234 *v3 = [[View1234 alloc] initWithId:@"3"];
v3.tag=3;
v3.backgroundColor = [UIColor blueColor];
v3.frame = CGRectMake(0, 0, 80, 115);
View1234 *v4 = [[View1234 alloc] initWithId:@"4"];
v4.tag=4;
v4.backgroundColor = [UIColor orangeColor];
v4.frame = CGRectMake(0, 0, 40, 50);View1234.h
@interface View1234 : UIView <UIGestureRecognizerDelegate> {
NSString *vid;
}
- (id) initWithId:(NSString *)vid_;
@end以下是View1234.m的完整代码
- (id) initWithId:(NSString *)vid_ {
if (self = [super init]) {
vid = [vid_ retain];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] init];
tapGesture.delegate = self;
tapGesture.numberOfTapsRequired = 1;
tapGesture.numberOfTouchesRequired = 1;
[tapGesture addTarget:self action:@selector(handleTap:)];
[self addGestureRecognizer:tapGesture];
[tapGesture release];
}
return self;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
if (touch.view==self ) {
return YES;
}
else if ([self.subviews containsObject:touch.view] && ![touch.view isKindOfClass:[self class]])
{
return YES;
}
return NO;
}
/*- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
if (self.tag==gestureRecognizer.view.tag) {
return YES;
}
}
return NO;
}*/
- (void) handleTap:(UITapGestureRecognizer *)tap {
NSLog(@"tap %@", vid);
}
- (void) dealloc {
[vid release];
vid = nil;
[super dealloc];
}更新:为什么会出现这个问题。
当您在每个视图中添加一个UIView作为另一个带有UITapGestureRecognizer的UIView的子视图时,在一些罕见的情况下,UITapGestureRecognizer状态会以某种方式变成Failed (我已经调试了50多次,并了解到这一点)。因此,当任何视图的子视图都无法处理点击手势时,系统就会将手势传递给它的超级视图来处理该手势,这种情况还在继续。
如果您调试,那么您将了解到,根据视图的层次结构,gestureRecognizerShouldBegin被多次调用。在这种特殊情况下,如果我点击view3,那么gestureRecognizerShouldBegin将调用3次,因为view3在第3层视图层次结构中,所以view3, view2 and view1将调用gestureRecognizerShouldBegin。
因此,为了解决问题,我返回YES表单gestureRecognizerShouldBegin作为正确的视图,而NO返回给其他视图,因此它解决了问题。
更新2:我在编辑后的答案中对代码做了一些修改,希望能解决您的问题。同样感谢@masmor,我还从他的答案中找到了一些线索,以解决问题。
发布于 2013-10-18 06:50:25
在识别器上设置委托并实现gestureRecognizer:shouldReceiveTouch:。
实现基本上应该阻止对子视图的接触,但是根据实际的视图层次结构和设置,可能会有一些额外的标准。
下面的示例简单地检查点击视图是否是一个直接的子视图,以及它是否有任何手势识别器,在这种情况下,它可以看到触摸。
阻塞触摸应该比篡改识别器状态更健壮,因为任何不必要的视图都不可能触发它们的识别器。实现自定义条件的要求是一个缺点,但我再次认为,在处理未知原因的bug时,显式实现行为更加健壮,比如在这种情况下。
#import "View1234.h"
@interface View1234 () <UIGestureRecognizerDelegate>
@end
@implementation View1234
- (id) initWithId:(NSString *)vid_ {
if (self = [super init]) {
vid = [vid_ retain];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] init];
tapGesture.numberOfTapsRequired = 1;
tapGesture.numberOfTouchesRequired = 1;
[tapGesture addTarget:self action:@selector(handleTap:)];
tapGesture.delegate = self;
[self addGestureRecognizer:tapGesture];
[tapGesture release];
}
return self;
}
//- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
//
// if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
// if (self.tag==gestureRecognizer.view.tag) {
// return YES;
// }
// }
//
// return NO;
//}
- (void) handleTap:(id)tap {
NSLog(@"tap %@", vid);
}
- (void) dealloc {
[vid release];
vid = nil;
[super dealloc];
}
- (BOOL)shouldReceiveTouchOnView:(UIView *)hitView {
NSLog(@"Should view:\n%@\nreceive touch on view:\n%@", self, hitView);
// Replace this implementation with whatever you need...
// Here, we simply check if the view has a gesture recognizer and
// is a direct subview.
BOOL res = (hitView.gestureRecognizers.count == 0 &&
[self.subviews containsObject:hitView]);
NSLog(@"%@", res? @"YES":@"NO");
return res;
}
#pragma mark - Gesture Recognizer Delegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch {
UIView *hitView = [self hitTest:[touch locationInView:self.superview]
withEvent:nil];
if (hitView == self) {
NSLog(@"Touch not in subview");
return YES;
}
return [self shouldReceiveTouchOnView:hitView];
}
@endhttps://stackoverflow.com/questions/19126391
复制相似问题