在屏幕上检测触摸操作

4

我想知道用户何时在我的应用程序屏幕上触摸任何地方。

我尝试使用-(UIResponder *)nextResponder,但不幸的是这并不起作用,因为我还要自动重新加载表格,所以这会被触发。

我还尝试了手势识别器,使用以下代码。但这只能识别视图上的触摸。而我有很多按钮供用户操作应用程序。我希望避免在每个按钮和分段控件上添加手势识别器或代码。

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapOnView:)];
[self.mainView addGestureRecognizer:tap];

- (void)tapOnView:(UITapGestureRecognizer *)sender
{
    //do something
}

我还尝试使用-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event,但这与手势识别器存在相同的问题。
我在思考是否有任何方法可以实现此任务。我希望能够从nextResponder中识别事件类型,然后我可以检测它是否为按钮等。
编辑:我之所以在处理这个问题,是因为我的应用程序需要保持活动状态,屏幕不能锁定(因此我已禁用屏幕锁定)。为避免过度使用电源,我需要将屏幕亮度降低,但一旦触摸应用程序时就恢复到原始亮度级别。我需要仅在我的1个视图控制器上出现此功能。

请参见 https://dev59.com/_nVC5IYBdhLWcg3wfxQ8 - Abdurrahman Mubeen Ali
4个回答

11

正如Ian MacDonald所提到的,使用hitTest::是一种很好的解决方案,可以检测应用程序范围内的用户交互,包括选中按钮、文本字段等。

我的解决方案是子类化UIWindow并实现hitTest方法。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

   // do your stuff here

   // return nil if you want to prevent interaction with UI elements
   return [super hitTest:point withEvent:event];
}

1
由于视图会“冒泡”触摸事件,这是我能找到的唯一可行的解决方案。谢谢! - Josh Bernfeld
2
你能提供更多关于如何使用UIWindow的子类的细节信息吗?例如初始化、在当前窗口上显示等。 - Satyam

3
你可以将UITapGestureRecognizer添加到你的[[UIApplication sharedApplication] keyWindow]上。
或者,你可以重写你的根UIViewhitTest:方法。
是否有特定的任务需要完成?可能有比分配“任何地方”手势更好的方法。 编辑:使用hitTest:
@interface PassthroughView : UIView
@property (readonly) id target;
@property (readonly) SEL selector;
@end
@implementation PassthroughView
- (void)setTarget:(id)target selector:(SEL)selector {
  _target = target;
  _selector = selector;
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
  [_target performSelector:_selector];
  return nil;
}
@end


@implementation YourUIViewController {
  PassthroughView *anytouchView;
}
- (void)viewDidLoad {
  // Add this at the end so it's above all other views.
  anytouchView = [[PassthroughView alloc] initWithFrame:self.view.bounds];
  [anytouchView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
  [anytouchView setTarget:self selector:@selector(undim)];
  [anytouchView setHidden:YES];
  [self.view addSubview:anytouchView];
}
- (void)undim {
  [anytouchView setHidden:YES];
}
- (void)dim {
  [anytouchView setHidden:NO];
}
@end

我已经在我的问题中添加了额外的细节。 - Remixed123
[[UIApplication sharedApplication] keyWindow] 不适用于按钮。 - Remixed123
我已经更新了我的答案,向您展示了如何使用hitTest:来监听触摸事件,同时仍然让您的应用程序表现正常。 - Ian MacDonald
谢谢,这个可行,但我需要研究一下另一个问题,因为它似乎更适合我的解决方案。 - Remixed123
我认为这个解决方案可能只适用于“YourUIViewController”。如何捕获整个应用程序中的点击事件?我们不能在所有视图控制器中都添加PassthroughView。有什么解决办法吗? - Satyam
@Satyam,没有太多使用场景需要应用程序范围的点击检测器,因为事件处理程序通常是上下文相关的。如果您确实需要此功能,可以将其添加到根视图控制器中。 - Ian MacDonald

2
您的修改使问题更加清晰明了。
我正在处理这个问题的原因是我的应用程序需要保持活动状态,并且屏幕不能被锁定(因此我已经禁用了屏幕锁定)。为了避免过度使用电力,我需要调暗屏幕,但是一旦触摸到应用程序就需要将亮度恢复到原始水平。
由于您正在控制屏幕亮度,因此可以在根控制器之前添加一个透明视图控制器,在调暗屏幕之前放置在其上,它只执行一个任务,即使用Tap手势监听轻击。当您点击时,您可以关闭视图控制器并将亮度调整为先前的状态。
这样做,您就不必担心按钮被点击,因为它们将位于透明视图控制器下方。由于它是一个全新的视图控制器,坐落在堆栈的顶部,因此您也不必修改现有的代码。

但这难道不意味着这个透明视图会阻止访问按钮和其他功能吗? - Remixed123
@Remixed123 这是一个新的视图控制器,它会接收屏幕上的所有触摸。 - GoodSp33d

1

好的,我之前遇到过类似的问题。

我记得我为了全屏检测而子类化了UIWindow并使其成为第一响应者

然后我从子类中重写了触摸处理

您还可以使用代码识别被触摸的控件

#import <QuartzCore/QuartzCore.h>

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.view setMultipleTouchEnabled:YES];
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    // Enumerate over all the touches 
    [touches enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
        // Get a single touch and it's location
        UITouch *touch = obj;
        CGPoint touchPoint = [touch locationInView:self.view];
        ...
    }];
}

为了禁用屏幕锁定,我使用了以下代码:
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];

我使用以下函数来 降低或增加屏幕亮度
[[UIScreen mainScreen] setBrightness:0.0f]; //and
[[UIScreen mainScreen] setBrightness:1.0f];

我希望你没有忘记通过响应者链传递事件的概念。[self.nextResponder touchesBegan:touches withEvent:event]; - bllakjakk
另外,由于[NSWindow makeFirstResponder:]是一个窗口方法,因此只有在将相应的视图添加到窗口后才有意义。 - bllakjakk
你能否在子类化的UIWindow中添加一个日志 - (void)sendEvent:(UIEvent *)event { },以验证触摸事件的接收情况。 - bllakjakk
你打错了吗?每个应用程序都有UIWindow。我的方法是基于子类化UIWindow,这样你就可以捕获全屏事件。我曾经开发过一个类似于Rise Alarm的应用程序,并使用这种方法来处理屏幕交互。 - bllakjakk
UIWindows是我的appdelegate.h中的一个属性和实例,但它没有在任何地方使用。我的appdelegate看起来像这样 - @interface SSAppDelegate:UIResponder - 也许这是一个Storyboard的问题。 - Remixed123
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接