or: “How I learned about the default value of userInteractionEnabled on UIImageViews”…
So, today I set out to create an iPad app in which my four year old son can drag some cookies around and get to do some light math.
In order to have draggable cookies, I figured the way to go would be to derive a DraggableImageView off of UIImageView that would add to itself a UIPanGestureRecognizer that would make the cookies (or any image, really) draggable.
This was my first attempt:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
class DraggableImageView : UIImageView { var dragStartPositionRelativeToCenter : CGPoint? init(image: UIImage!) { super.init(image: image) addGestureRecognizer(UIPanGestureRecognizer(target: self, action: "handlePan:")) } func handlePan(nizer: UIPanGestureRecognizer!) { if nizer.state == UIGestureRecognizerState.Began { let locationInView = nizer.locationInView(superview) dragStartPositionRelativeToCenter = CGPoint(x: locationInView.x - center.x, y: locationInView.y - center.y) return } if nizer.state == UIGestureRecognizerState.Ended { dragStartPositionRelativeToCenter = nil return } let locationInView = nizer.locationInView(superview) UIView.animateWithDuration(0.1) { self.center = CGPoint(x: locationInView.x - self.dragStartPositionRelativeToCenter!.x, y: locationInView.y - self.dragStartPositionRelativeToCenter!.y) } } } |
but for some reason I could not get it to recognize my dragging gestures! So I messed around with the code, coding various (failed) attempts at adding UIButtons and whatnot to my DraggableImageView in order to be able to do at least one single successful addTarget, but I simply could not make it work!
I loaded up some other Swift projects where my buttons worked fine, went back to the failing DraggableImageView, messed around with platform settings and other good stuff, suspecting that it was due to a bug – it is called XCode Beta, after all – and then it hit me:
- “New image view objects are configured to disregard user events by default. If you want to handle events in a custom subclass of UIImageView, you must explicitly change the value of the userInteractionEnabled property to YES after initializing the object.”
Source: UIImageView documentation, emphasis mine.
Right! So I went and added self.userInteractionEnabled = true in the middle, which resulted in the full code reading (with nifty hover shadow effect added)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
class DraggableImageView : UIImageView { var dragStartPositionRelativeToCenter : CGPoint? init(image: UIImage!) { super.init(image: image) self.userInteractionEnabled = true //< w00000t!!!1 addGestureRecognizer(UIPanGestureRecognizer(target: self, action: "handlePan:")) layer.shadowColor = UIColor.blackColor().CGColor layer.shadowOffset = CGSize(width: 0, height: 3) layer.shadowOpacity = 0.5 layer.shadowRadius = 2 } func handlePan(nizer: UIPanGestureRecognizer!) { if nizer.state == UIGestureRecognizerState.Began { let locationInView = nizer.locationInView(superview) dragStartPositionRelativeToCenter = CGPoint(x: locationInView.x - center.x, y: locationInView.y - center.y) layer.shadowOffset = CGSize(width: 0, height: 20) layer.shadowOpacity = 0.3 layer.shadowRadius = 6 return } if nizer.state == UIGestureRecognizerState.Ended { dragStartPositionRelativeToCenter = nil layer.shadowOffset = CGSize(width: 0, height: 3) layer.shadowOpacity = 0.5 layer.shadowRadius = 2 return } let locationInView = nizer.locationInView(superview) UIView.animateWithDuration(0.1) { self.center = CGPoint(x: locationInView.x - self.dragStartPositionRelativeToCenter!.x, y: locationInView.y - self.dragStartPositionRelativeToCenter!.y) } } } |
which in turn gave me these delicious draggable cookies…. Yummy!