0

I'm trying to subclass UIView and design a transparent view. This view will sit on top of many other views and it's only task is to capture and record user touches (tap and pan). I have tried many different methods, explained in different questions asked by other users with no luck. This is what I have done so far in my implementation file:

#import "touchLayer.h"

@implementation touchLayer

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) [self commonInit];
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) [self commonInit];
    return self;
}

- (void)commonInit
{
    self.userInteractionEnabled = YES;
    self.alpha = 0.0;
}

- (id)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    id hitView = [super hitTest:point withEvent:event];
    if (hitView == self)
    {
        UITouch *touch = [[event allTouches] anyObject];

        if (touch.phase == UITouchPhaseBegan) NSLog(@"touchesBegan");
        else if (touch.phase == UITouchPhaseMoved) NSLog(@"touchesMoved");
        else if (touch.phase == UITouchPhaseEnded) NSLog(@"touchesEnded");

        return nil;
    }
    else
        return hitView;
}

@end

Now this code works just fine, and I see see the touches in the lower layers, but I cannot differentiate between touchBegan, touchMoved, and touchEnded. [[event allTouches] anyObject] returns nil. Do you have any idea how I can capture tap and pan on a UIView without blocking the touches? Thanks a lot.

  • So now, your problem is this view block the touches in below views. Is it right? – trungduc Nov 05 '17 at 05:41
  • No actually touches are passed to the lower view. However, I need to know what kind of touches (began, moved, ended) are captured by this UIView, but the event.allTouches.anyObject returns nil. – Babak Samareh Nov 05 '17 at 15:27
  • As i know, i’m sure that transparent view doesn’t receive touch. Try give it a color and you will see the difference – trungduc Nov 05 '17 at 15:44
  • I have also tried it with colour and alpha channel. It does receive the touch, but like I said I cannot get the UITouchPhase. It's nil. – Babak Samareh Nov 05 '17 at 18:12
  • Try to use these 3 method `func touchesBegan(touches: NSSet, withEvent event: UIEvent) `, `func touchesMoved(touches: NSSet, withEvent event: UIEvent)` and `touchesEnd` – trungduc Nov 05 '17 at 18:14
  • I have also tried those, they capture the touch properly, but do not pass the touch to the lower views. I add `[super touchesBegan:touches withEvent:event];` to send the touch to the next view, no luck. – Babak Samareh Nov 05 '17 at 18:18
  • In a nutshell, hitTest sends the touch to the lower view just fine, but does not detect the type of touch, on the other hand, touchesBegan, touchesMoved, ... detect the type of touch correctly, but do not pass the touch to the lower view. – Babak Samareh Nov 05 '17 at 18:20

1 Answers1

1

After investigating, actually i can't find solution to detect touch using hitTest method with a touchLayer. But your question is about capturing and recording user touches, so i have another for this issue.

My solution is

  • Subclass UIWindow
  • Replace window of UIAppDelegate with a new one which is created with your window class.
  • Override sendEvent method of UIWindow, capture and record user touches in this method.

This is my subclass of UIWindow to detect touch. I tried and it work.

@implementation DetectTouchWindow

- (void)sendEvent:(UIEvent *)event {
  UITouch *touch = [[event allTouches] anyObject];

  switch ([touch phase]) {
    case UITouchPhaseBegan:
      NSLog(@"Touch Began");
      break;
    case UITouchPhaseMoved:
      NSLog(@"Touch Move");
      break;
    case UITouchPhaseEnded:
      NSLog(@"Touch End");
      break;
    case UITouchPhaseCancelled:
      NSLog(@"Touch Cancelled");
      break;
    default:
      break;
  }

  [super sendEvent:event];
}

@end

For more detail and easier, i created a demo repo to check it. You can take a look at this link https://github.com/trungducc/stackoverflow/tree/recording-touch-events

Hope this helps ;)

trungduc
  • 11,926
  • 4
  • 31
  • 55
  • This is great, thank you very much for sharing this. Works like a charm. – Babak Samareh Nov 06 '17 at 04:36
  • Welcome @BabakSamareh ;) – trungduc Nov 06 '17 at 04:36
  • On a separate note, I also have to record touches in different view controllers. This method that you suggested starts capturing touches from the moment the app is opened until it's closed. How can I differentiate between touches captured for different view controllers? – Babak Samareh Nov 06 '17 at 04:37
  • You can use `UIApplication.sharedApplication.delegate.window.rootViewController` to check what is current controller ;) – trungduc Nov 06 '17 at 04:42
  • @BabakSamareh or in `DetectTouchWindow`, just use `self.rootViewCont‌​roller` – trungduc Nov 06 '17 at 04:42
  • Welcome my friend ;) – trungduc Nov 06 '17 at 04:44
  • Quick question @trungduc, since you are an expert in UITouch, and just for my own knowledge, is there anyway that I can implement this on a UIView? It will give me more flexibility in what I'm trying to achieve. Is there any sendEvent method for UIView that I can use? Or any other way to capture tap and pan and then pass them on to the lower view? I have tried hitTest, but the UIEvent returned by hitTest does not contain touch information! – Babak Samareh Nov 06 '17 at 16:19
  • For your question: 1. Is there any sendEvent method for UIView that I can use? - I'm afraid that answer is no because `sendEvent` is a method of `UIWindow` not `UIView`. 2. Any other way to capture tap and pan and then pass them on to the lower view? - I think you can use `UIGesture` or `touchBegin`, `touchMove` without `layerView` ;) – trungduc Nov 06 '17 at 16:27
  • Aren't those blocking the touches? For example, if I override touchesBegan and then touch a UIButton, will it receive the touch? – Babak Samareh Nov 06 '17 at 17:16
  • Of course, let try. I think it still receives touch. – trungduc Nov 06 '17 at 17:24
  • I'll try and update the answer here. Thank you again for your time and support :) – Babak Samareh Nov 06 '17 at 17:48