我有一个UIView
,上面有4个按钮,另一个UIView
位于按钮视图的顶部。最顶层的视图包含UIImageView
,其中包含UITapGestureRecognizer
。
我试图创建的行为是,当用户点击UIImageView
时,它会在屏幕右下角的小视图和动画变大之间切换。当它很大时,我想要禁用底部视图上的按钮,当它很小时,在右下角我希望触摸传递到按钮并让它们正常工作。我几乎在那里,但除非我禁用顶视图的UserInteractions
,否则无法通过按钮进行操作。
我在顶视图的initWithFrame:
中有这个:
// Add a gesture recognizer to the image view
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageTapped:)];
tapGestureRecognizer.cancelsTouchesInView = NO;
[imageView addGestureRecognizer:tapGestureRecognizer];
[tapGestureRecognizer release];
我这是我的imageTapped:
方法:
- (void) imageTapped:(UITapGestureRecognizer *) gestureRecognizer {
// Toggle between expanding and contracting the image
if (expanded) {
[self contractAnimated:YES];
expanded = NO;
gestureRecognizer.cancelsTouchesInView = NO;
self.userInteractionEnabled = NO;
self.exclusiveTouch = NO;
}
else {
[self expandAnimated:YES];
expanded = YES;
gestureRecognizer.cancelsTouchesInView = NO;
self.userInteractionEnabled = YES;
self.exclusiveTouch = YES;
}
}
使用上面的代码,当图像很大时按钮处于非活动状态,当我触摸图像时它会缩小并且按钮变为活动状态。但是,小图像不会接触到触摸,因此不会扩展。
如果我在两种情况下都设置了self.userInteractionEnabled = YES
,那么图像会在触摸时展开和收缩,但按钮永远不会接触到并且就像禁用一样。
触摸时是否有图像展开和收缩,但如果图像处于收缩状态,下面的按钮只能接收触摸?我在这里做了些蠢事并且遗漏了一些明显的东西吗?
我绝对会疯狂地试图让它工作,所以任何帮助都会受到赞赏,
戴夫
更新
为了进一步测试,我覆盖了touchesBegan:
和touchesCancelled:
方法,并在包含UIImageView的视图中调用了它们的超级实现。使用上面的代码,永远不会调用touchesCancelled:
,并始终调用touchesBegan:
。
所以看起来视图正在接触,它们只是没有传递到下面的视图。
更新
这是因为响应链的工作方式吗?我的视图层次结构如下所示:
VC - View1
-View2
-imageView1 (has tapGestureRecogniser)
-imageView2
-View3
-button1
-button2
我认为操作系统首先执行hitTest,因为View2在前面,所以应该获得所有的触摸,除非为View2将userInteractions设置为NO,否则它们永远不会传递给View3,在这种情况下,imageView1也会被阻止接收触动。它是如何工作的,View2是否有办法通过它接触到View3?
答案 0 :(得分:89)
我认为UIGestureRecognizer是一个红鲱鱼。最后为了解决这个问题,我覆盖了pointInside:withEvent:
UIView:
方法
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
BOOL pointInside = NO;
if (CGRectContainsPoint(imageView.frame, point) || expanded) pointInside = YES;
return pointInside;
}
如果您触摸imageView或设置了扩展标志,则会导致视图捕获所有触摸。如果它没有展开,那么只有当它们在imageView上时才会捕获触摸。
通过返回NO,顶级VC的View查询其视图层次结构的其余部分,寻找命中。
答案 1 :(得分:57)
在View
或Storyboard
和...
XIB
或在Swift中
view.isUserInteractionEnabled = false
答案 2 :(得分:5)
查看UIGestureRecognizerDelegate Protocol。具体而言,gestureRecognizer:shouldReceiveTouch:
您希望每个UIGestureRecognizer
属性UIViewController
,
// .h
@property (nonatomic, strong) UITapGestureRecognizer *lowerTap;
// .m
@synthesize lowerTap;
// When you are adding the gesture recognizer to the image view
self.lowerTap = tapGestureRecognizer
确保让UIViewController
成为代表,
[self.lowerTap setDelegate: self];
然后,你会有这样的事情,
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (expanded && gestureRecognizer == self.lowerTap) {
return NO;
}
else {
return YES;
}
}
当然,这不是确切的代码。但这是你想要遵循的一般模式。
答案 3 :(得分:2)
我有另一个解决方案。我有两个观点,让我们称之为重叠的CustomSubView
,他们都应该接受触摸。所以我有一个视图控制器和一个自定义的UIView类,让我们调用它在界面构建器中设置的ViewControllerView
,然后我添加了两个应该接收到该视图的触摸的视图。
所以我通过覆盖hitTest拦截ViewControllerView
中的触摸:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return self;
}
然后我在ViewControllerView
中覆盖:
- (void)touchesBegan:(NSSet *)touches
withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
for (UIView *subview in [self.subviews reverseObjectEnumerator])
{
if ([subview isKindOfClass:[CustomSubView class]])
{
[subview touchesBegan:touches withEvent:event];
}
}
}
与touchesMoved
touchesEnded
和touchesCancelled
完全相同。
答案 4 :(得分:1)
@Magic Bullet Dave的解决方案,但在Swift中
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
var pointInside = false
if commentTextField.frame.contains(point) {
pointInside = true
} else {
commentTextField.resignFirstResponder()
}
return pointInside
}
我在CameraOverlayView for ImagePickerViewController.cameraOverlay中使用它,以便用户在拍摄新照片时能够发表评论
答案 5 :(得分:0)
// this is due to userInteractionEnabled deficiency: the flag stops traversal of subviews
@objc(YourObjcPrefixHereForXibAndStoryboardUseViewTransparentToTouch)
open class ViewTransparentToTouch: UIView
{
public var daisyView: UIView?
override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, with: event)
if hitView === self {
return daisyView
}
return hitView // childView
}
}