1

I have a UIView instance that I am animating using CAKeyframeAnimation. It is a looping animation and will be constantly in motion. However, I need to allow user interaction with the item (responding to taps, to be exact). Because CoreAnimation is only moving the presentation layer and not the model, I cannot use a UIButton instance, for example, to essentially get the touchUpInside events.

Consider the following basic example layout:

enter image description here

I am using the blue box for touch events by overriding the touchesEnded:withEvent: method on a UIView subclass. I am then bubbling that event through an NSNotification (I really don't want to introduce a lot of coupling to handle this event) to the containing view controller, which then does a hitTest: on the red box being animated.

All of this seems rather hacky to just be able to touch an animated UIView. What are some of the best patterns for handling touches for animated UIView/CALayer instances?

Wayne Hartman
  • 18,369
  • 7
  • 84
  • 116
  • Do you have to use CA? CA is best for smooth animation because it's lightweight, and it's lightweight because it doesn't have all of the event handling stuff (partly true). If you somehow were to add event handling to it, then you might destroy the smooth GPU-able abilities. I'm not sure there's a way around that. How smooth does it need to be? You might want to animate with some of the other animation techniques that actually move the model with the CPU. Just thinking out loud with you. – Vinnie Feb 16 '12 at 05:09
  • @Vinnie - The problem is that this example is a very simple animation. The real problem actually involves several keyframes to animate a view along a path, as well as do some rotation as it moves along the path. The animation also needs to repeat ad-infinitum, so using UIView blocks based really doesn't seem like a good fit. – Wayne Hartman Feb 16 '12 at 05:30

2 Answers2

1

Tracking the touch in the superview is correct I believe. But I would do hit testing in the superview rather than posting a notification to the view controller. Something like this:

Create a MYAnimationContainerView class (the blue box).

Give it a delegate or block callback that the owning view controller sets. For example:

__weak id weakSelf = self;
MYAnimationContainerView *view = [[MYAnimationContainerView alloc] initWithTouchCallback:^(UIView *touchedView){
  [weakSelf handleTouchToView:touchedView];
}];

In MYAnimationContainerView, override both touchesBegan:withEvent: and touchesEnded:withEvent:. In began, perform a hit test on the presentation layer of the moving view(s). Keep track of which one (if any) was touched. In ended, perform another hit test. If it's the same view, then call your callback.

The above could just as easily be implemented with a delegate protocol rather than a block. I've just been getting in the habit of preferring blocks over one-method delegate protocols.

You could also implement it with target/selector, but that pattern has gotten to be a bit of a hassle with ARC, and I tend to discourage its use today.

Community
  • 1
  • 1
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
0

If you want to handle touch for particular object in a view, say the red box on the blue, It is better to create a separate class ,say subclass of UIview, and handle touch events in it. It is clean and abstract.

In case of many such objects, the touch events are handled by itself.

I am not sure this is what you expected.

Vignesh
  • 10,205
  • 2
  • 35
  • 73
  • The touches are not handled by themselves, this is because only the presentation layer is animated, not the model layer, where touches are handled. – Wayne Hartman Feb 16 '12 at 13:35