自定义 UIControl 和手势识别器
Posted
技术标签:
【中文标题】自定义 UIControl 和手势识别器【英文标题】:Custom UIControl and Gesture Recognizer 【发布时间】:2013-09-02 15:37:31 【问题描述】:我正在尝试创建一个类似于滑块的自定义 UIControl。
此控件将成为一个视图的子视图,该视图还附加了一个点击手势识别器。
现在的问题是这个点击手势识别器取消了发送到我的控件的触摸。有没有办法可以在我的控件代码中覆盖它?
如果我查看 ios 中的标准控件,它看起来好像 UIButton 有一种覆盖点击手势识别器的方法,但 UISlider 没有。因此,如果我用 UIButton 替换我的自定义控件,点击手势识别器不会触发它的动作,但如果我用滑块替换它会触发它。
编辑:我在 Xcode 中制作了一个小项目来玩。在这里下载 https://dl.dropboxusercontent.com/u/165243/TouchConcept.zip 并尝试更改它,以便
UICustomControl 不知道点击手势识别器 当用户点击黄色框时,UICustomControl 不会被取消 UICustomControl 没有从 UIButton 继承(这是一个感觉不对的解决方案,以后可能会让我更头疼)代码:
// inherit from UIButton will give the wanted behavior, inherit from UIView (or UIControl) gives
// touchesCancelled by the gesture recognizer
@interface UICustomControl : UIView
@end
@implementation UICustomControl
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
NSLog(@"touchesBegan");
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
NSLog(@"touchesMoved");
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
NSLog(@"touchesEnded");
-(void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
NSLog(@"touchesCancelled");
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(logTap:)];
[self.view addGestureRecognizer:tapRecognizer];
UIView *interceptingView = [[UICustomControl alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
interceptingView.userInteractionEnabled = YES;
interceptingView.backgroundColor = [UIColor yellowColor];
[self.view addSubview: interceptingView];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
- (void) logTap: (id) sender
NSLog(@"gesture recognizer fired");
- (void)didReceiveMemoryWarning
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
@end
【问题讨论】:
当我覆盖 UIControl 时,我必须通过覆盖 UIResponder 的 touchesBegan/Moved/Ended/Canceled:withEvent: 来滚动我自己的跟踪,而不是调用 super。我放弃了使用 begin/continue/endTrackingWithTouch:withEvent: 因为它有问题 - 幕后发生的事情太多了。 HTH。 示例项目工作正常:touchesBegan,Moved,Ended 不受手势识别器的干扰(因此您可以跟踪滑块的移动),但不移动的触摸会被点击识别器吞噬。 “成为一个视图的子视图,该视图也附加了一个点击手势识别器”意味着无论子视图如何,视图中的每个点击都必须被识别为点击,但这显然不是你想要的。为什么不使用点击手势识别器,而不是使用 touchesBegan/Moved/Ended/Canceled:withEvent: 在您的视图控制器(=响应者链中的超级)来捕获其他未处理的点击? 我再次验证,对我来说(iPad 模拟器 6.1)它没有按预期工作:点击手势识别器每次都会触发,每次都会调用 touchesCancelled,但我从未得到 touchesEnded。您提出的解决方案是一种解决方法。目前我正在使用另一种解决方法:从 UIButton 继承。 2013-09-04 10:20:27.287 TouchConcept[12033:c07] touchesBegan 2013-09-04 10:20:31.135 TouchConcept[12033:c07] touchesMoved 2013-09-04 10:20 :31.184 TouchConcept[12033:c07] touchesMoved 2013-09-04 10:20:31.999 TouchConcept[12033:c07] touchesMoved 2013-09-04 10:20:32.959 TouchConcept[12033:c07] touchesMoved 2013-09-04 10: 20:36.167 TouchConcept[12033:c07] touchesEnded 行为不稳定且不可靠:如果您快速移动并触摸 GR,则会触发,但如果您缓慢移动并缓慢触摸,则不会触发。 【参考方案1】:您可以使用“取消视图中的触摸”属性将手势识别器配置为不取消它所附加的视图中的触摸:
myGestureRecognizer.cancelsTouchesInView = NO;
【讨论】:
我不认为这是一个解决方案,因为手势识别器仍然会触发它的动作。最重要的是,它需要更改控件的某些超级视图中的代码才能使控件正常运行。 @KristofVanLandschoot 您可以成为这两个识别器的代表,并且可以细粒度地控制它们是应该同时识别还是只在另一个识别器失败后才识别。 另一天,另一个故事,但这是我错过的事情【参考方案2】:我有点晚了,但是对于那些(像我一样)绊倒这个问题的人,我使用了另一种解决方案:
使用手势识别器的代理。
UITapGestureRecognizer *tapGestRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissInfoBox:)];
tapGestRec.delegate = self;
[self.view addGestureRecognizer:tapGestRec];
然后当手势识别器想要处理/吞下触摸时,在 shouldReceiveTouch 委托函数中进行一种命中测试。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
CGPoint location = [touch locationInView:self.view];
return !CGRectContainsPoint(self.myCustomControl.frame, location) && !CGRectContainsPoint(self.myOtherCustomControl.frame, location);
我在我的 ViewController 中完成了所有这些操作,这样 UIControl 不必知道任何同级视图,并且手势识别器不会从我的自定义控件中“窃取”点击,而只会处理“未捕获”的点击。
此外,这样您就不会同时触发手势识别器和自定义控件,而 cancelsTouchesInView 会发生这种情况。
顺便说一句,也许它可以与 UIButton 一起使用,因为 UIButton 在内部使用手势识别器?我认为他们相互理解,而 UIControls 和识别器则不。不过不确定。
【讨论】:
我试过这个,但无法让它工作。手势识别器委托方法仅在触摸落在与手势关联的 subview 内时才被调用。我的设置有一个带有轻按手势的标签,我的自定义控件作为主视图的两个子视图。 哦,等等,整个视图附加了一个额外的点击手势。我应该瞄准那个【参考方案3】:在您的 UIControl
子类中覆盖 gestureRecognizerShouldBegin(_:)
。
public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool
if gestureRecognizer.isKind(of: UITapGestureRecognizer.self)
return false
else
return super.gestureRecognizerShouldBegin(gestureRecognizer)
【讨论】:
【参考方案4】:您应该按照本教程进行操作
http://www.raywenderlich.com/1768/uiview-tutorial-for-ios-how-to-make-a-custom-uiview-in-ios-5-a-5-star-rating-view
它向您展示了如何制作自定义 uiview。
然后按照这个 http://www.raywenderlich.com/29474/ipad-for-iphone-developers-101-in-ios-6-custom-input-view-tutorial
【讨论】:
这太笼统了,无法接受,抱歉 :) 感谢您提供的链接,我会看看它们,看看我的问题的答案是否在那里。跨度>以上是关于自定义 UIControl 和手势识别器的主要内容,如果未能解决你的问题,请参考以下文章
是否可以自定义触发 UIScrollView 滚动的滑动手势识别?