UIPanGestureRecognizer translationInView vs locationInView - swift

I'm taking the first steps in Swift
What is the best method to move UIView along X coordinate for UIPanGestureRecognizer?
Usually Programmers use translationInView for pan gesture
But I suppose the locationInView is more convenient for this purpose

translationInView provides the change in coordinates that the view has moved within the view it is contained in. That is given in the form of a CGPoint.
If you drag it to the left, it might provide the coordinates:
(-20.0, 0)
locationInView provides the location of the view in the form of a CGPoint.
If you drag it to the left and print the locationInView throughout the gesture, you will see a slur of logs with changing coordinates with the final one being its current resting place.
To move a UIView with the touch's location, you would want to set the frame property of the UIView to the gesture's locationInView in the gesture's delegate method.

A pan gesture is composed from individual touches on the screen, locationInView gives you the current position of the touch on the screen, translationInView gives you the movement relative to the previous touches.
When panning, you are usually interested more in the movement (e.g. when scrolling, you don't want to know where the scroll began and where it ended) and not in the current position. If you are using the pan recognizer for example for drag and drop, then the location will interest you in the final position (you want to know where to drop).

Related

How to make a menu with icons spinning in a circle?

I am trying to create a menu for my app with a few icons in a circle. User should be able to spin this menu, making the icons change their positions around this circle path, but not rotating themselves. I read this earlier http://www.raywenderlich.com/9864/how-to-create-a-rotating-wheel-control-with-uikit so I can see how to follow the finger movement, but I need this menu to have inertial spin after the touch ends. I have 2 questions about how to do this.
First one, what's the best way to make animation with icons moving around in a circle? It should be slowing down until it stops and, if user moves his finger fast enough, should be able to do more than one full circle.
Second, how do I measure speed of finger movement at the end of it? I tried to use locationInView and previousLocationInView and just spin it by difference of angles between them multiplied by some constant. Problem is, when I keep my finger in one place for a while and take it up, I still get inertial movement of the circle and in this case I don't want it to move at all.
You want to use a scroll view. Specifically you want a hidden scroll view where you attach the pan gesture recogniser from the scroll view to your custom menu view. Then you implement the delegate methods of the scroll view to redraw your menu. This WWDC video has a good overview of the process. The benefit of this approach is that you get true iOS style acceleration and deceleration for free. You don't need to worry about finger position or speed, only the content offset of the scroll view.
Unless you want to spin your finger round in a circle with the menu items. That's a different ball game...
In theory, the same approach as above can be used for spinning your finger in a circle. You would need to ensure that directionalLockEnabled was set to NO on the scroll view. The problem is that the calculations to determine the rotation of your menu view are a lot more complex. Probably you would want to be modal and check if the scroll view is dragging in the callbacks. If it is dragging you would use the touches on the pan gesture to find the exact finger location to set the rotation. You'd also want to maintain data on the instantaneous direction of finger movement so that when the touch is released... If it isn't dragging then you use the scroll view content offset to apply the deceleration effect on the rotation (using the scroll direction just before the touch was released to know how to use the content offset changes).

Passing a touch gesture

I'm looking for a solution to help "pass" a touch gesture along.
Basically I have a menu, and I want users to be able to drag and drop items from the menu to the canvas in a single continuous drag.
I have already achieved a draggable image via pan gestures (we'll call these instances Sprites). I can also instantiate a Sprite anywhere on the UIView using a button or UIImageView with touch gestures.
However, this currently requires two touches. One to touch down the menu item button and release, creating the Sprite. The second to touch down on the sprite, allowing the user to drag it, and then release it where they want. I would like to merge these touches so that when a user touches a menu item, the Sprite is instantiated and already within the pan gesture, or something to that affect.
I've attached a visual description if that helps. Any help is appreciated. Thanks!
There is no way of artificially forcing UIGestureRecognizer to recognize touches that are passed to a different view.
From the Gesture Recognizer docs:
Delivery of events initially follows the usual path: from operating
system to the application object to the window object representing the
window in which the touches are occurring. But before sending an event
to the hit-tested view, the window object sends it to the gesture
recognizer attached to that view or to any of that view’s subviews.
Figure 3-1 illustrates this general path, with the numbers indicating
the order in which touches are received.
Figure 3-1
Event's delivery happens automaticaly by the system and is delivered to the appropriate view.
To do what you want I would implement the UIGestureRecognizer on the subview (view that contains your UIButton) and on press create an instance of your Sprite object and manipulate that object from withing the gesture recognizer of the subview. Alternatively you could use -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)even to reposition the object yourself.

How can I "catch" a subview during a swipe gesture on an iOS device?

I'm building an iOS board game (similar to scrabble or words) that involves moving around little tiles on the screen and I'm finding the user sometimes has a hard time touching and moving the tiles around because they're too small. Due to the design of the game, I can't increase the size of the tiles, so I've had to implement some little tricks that make touching and moving the tiles easier for the user and they work well. The only issue that remains is the user sometimes touches just barely outside the tile and when the user tries to move it, the tile stays where it is.
I have two ideas for how I can fix this...
If a tile isn't touched when the user touches the screen, I can use the parent view's touch location to find the closest tile to the touch location and somehow forward the touch event to that tile's view.
If a tile isn't touched when the user touches the screen, I can somehow "catch" the tile when the user drags their finger over it as they attempt to move it.
I'd prefer to implement solution #2 since solution #1 has too many problems associated with, not to mention solution #2 is a more realistic experience. That said, how can I "catch" a tile when the user drags their finger over it and send it touch events to move it where the finger is?
Currently, my tiles are implemented as subclasses of the UIView and they handle the touch events (touchesBegain, touchesMoved, and touchesEnded) directly. So if the user touches just barely outside the view and drags their finger over the view, it doesn't receive any of the touch events since it didn't receive the initial touchesBegan event.
Thanks in advance for all your wisdom!
Maybe you should have the "board" view handle all the dragging. When a touch begins and there is a tile at that point, then start dragging it. Otherwise check whenever the touch is moved and as soon as you find a tile, start dragging it.
You could override hitTest:withEvent: in the board view so that it can still detect when a touch hits a tile, but always return itself so that touch events go to the board view (e.g. record the subview that was hit in a separate member variable, so that you know what to start dragging later on when touch events start coming in).
More Details
When handling touches, UIView will use hitTest to find the view that should receive touch events. The default implementation checks each subview so that the "deepest" subview in the hierarchy gets the touches. In order for the board view to receive touches, you would have to disable userInteraction on all of the tile views. But that means you can't use hitTest to find the tile that was touched, since it ignores views that have userInteraction disabled.
So what I am saying is leave userInteraction enabled on the touch views. But override hitTest on the board view so that it first calls the super implementation in order to find a tile (if the result is self, the board itself was hit). No need to implement your own tile searching. Something like this:
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
UIView *hitView = [super hitTest:point withEvent:event];
if ( hitView != self )
self.draggingTile = hitView;
return self;
}
Now you know what tile to move in touchesMoved. However, I don't think hitTest is called as the touch is moved, so if no tile has been picked up yet, you may have to call it manually (you can get the point and event from the touch passed to touchesMoved.
Have you looked into the UIGestureRecognizer API? I think your best option would be to add a UIPanGestureRecognizer recognizer to your board's view which would then fire back the selector to your UIViewController.
Setup in ViewDidLoad:
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:#selector(handlePan:)];
[[self view] addGestureRecognizer:panGestureRecognizer];
And then implemented the selector:
- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer{
CGPoint currentPoint = [gesture locationInView:[self view]];
}
When you set up the gesture recognizer you can set parameters to limit the callbacks (only recognize pans with 1 finger, etc. And in the callback you can check the gesture properties to see if the pan in continuing or if it's coming to an end. You can also grab the current point and determine if it's in or near a tile.

How can I take zooming into account when a user touches a UIScrollView?

I have a UIImageView inside of a UIScrollView. The parent scroll view allows zooming and panning. When the user taps a point in the scroll view, I want to find the location in the raw image inside the UIImageView - i.e. I want the point after including any zooming and panning the user has done in the scroll view.
Right now, I have a UIScrollView subclass called ForwardingScrollView that handles touch events and attempts to convert them into locations in the coordinate system of the child image view. I tried adding contentOffset to these points, tried multiplying them by zoomScale, and even tried doing both. I also tried calling [touch locationInView: self] and [touch locationInView: parent], but none of these methods correctly return the point that I clicked in the underlying image.
What's the best way to do this?
Thanks in advance.
UIView has methods for converting points and rectangles between views.
[scrollView convertPoint:somePoint toView:imageContentView];
or
[imageContentView convertPoint:somePoint fromView:scrollView];

How can i rotate an arrow image by touch on that image?

I am working on a project where i need to rotate an image by touching it.
It can be rotate faster or slower depending on how the user touches it.
Can you show me some tutorials or how this can be done?
Place your image within a UIImageView, then either subclass that view and replace touchesBegan:withEvent: or set a delegate for it and implement the same method as a delegate method. This will give you the ability to respond to touch events (the beginning of a touch, in this case, although you can do the same for ending a touch or moving of the finger).
Within this touch handling method, you can implement something similar to what I describe here in order to perform a Core-Animation-enabled rotation of your UIImageView at a given speed. To alter the speed, change the duration property on the animation I provide. As I suggest there, you may want to look into a CAKeyframeAnimation to do a smoother animation with acceleration and deceleration at the beginning and end.
An easier way is to set up an NStimer and rotate the transform everytime it fires.
I've some sample code here that coincidentally does something similar:
http://github.com/kailoa/touchsamplecode/tree/master
Using Cocos2d, you can't have 'touch enabled' sprites, 'isTouchEnabled' is at the Layer level. You'll have to receive the touch at the layer level, then check the location of the touch against the location of the touchable sprite. The CGRect* functions include a 'rect contains point' which you can pass the touch location to, with the sprite's rect to see if it was 'touched', and which point you could then say [sprite runAction:[Rotate ....]]