I have created a relatively simple app which allows the user to drag an imageview from a 'tray' UIView at the bottom of the screen, and when the view is dropped, it adds itself to a subview of a 'target' UIView.
My problem is, when I drop the imageview into the target view, the frame is totally wrong, and the imageview jumps (seemingly randomly) to another position in the target view.
I've torn my hair out over this, and am having a massive brainfart. Is there a way to just get the coordinates of the dragged view in the superview, and then translate that into coordinates of the target view?
As you can see, i'm taking the icon out of the 'tray' view, and into the main view when the dragging begins. When it ends, I simply add the icon into the target view.
if (gesture.state == UIGestureRecognizerStateBegan){
if (icon.superview == viewTray) {
[self closeTray:nil];
[self.view addSubview:icon];
}
}
if (gesture.state == UIGestureRecognizerStateEnded) {
[viewTarget addSubview:icon];
}
An image of the basics of the xib:
use the gesture recognizer to figure out where the finger touches in the viewTarget's frame by:
CGPoint p = [gesture locationInView:viewTarget];
then set the icon's center to be that point, and you are done:
icon.center = p;
[icon removeFromSuperView];
[viewTarget addSubview:icon];
The problem is that the frame is in the bounds of its superView. So when you drop it into a new superView its not the same. You need to convert it.
This code will convert its from from its superView coordinate system to its targetView coordinate system. You should probably do this right after [viewTarget addSubview:icon];
[viewTarget convertRect:icon.frame fromView:icon.superView];
Related
I have a UIScrollView which is able to contains many view. To allow a good scrolling (without the content going outside the view while scrolling), on my Main.sotryboard , I've clicked on my UIScrollView and then in the attribute inspector I have allowed the Clip Subviews property:
My problem: all the views which are in my UIScrollViews are draggable (because they all have a UIPanGestureRecognizer.
So, when I try to drag them OUTSIDE my UIScrollView, they just disappear.
In fact they're just going behind every other view
To give you an exemple, I have others components which allow the drop of a view form the precedent UIScrollView. So when I begin the drag'n'drop from it, it disappear, and then reappear in the second component on which I have dropped the view.
What I have tried: I have a special UIPanGestureRecognizer for te drag'n'drop of a view coming from this UIScrollView. So, I actually have this (which, obviously, doesn't work, otherwise I would not be here):
//Here recognizer is the `UIPanGestureRecognizer`
//selectpostit is the name of the view I want to drag
if(recognizer.state == UIGestureRecognizerStateBegan){
selectpostit.clipsToBounds = NO;
}
Any Ideas on how I can improve that?
Thanks in advance.
You could try to reset scrollView.clipsToBounds to NO every time gesture starts, but that would lead to side effect when other content outside scroll view would become visible when dragging is in the progress.
I would recommend to take snapshot of the the draggable view when panning starts, place it on the scrollview's parent, and move it. Such approach should solve your problem.
Here is the code:
- (void)onPanGesture:(UIPanGestureRecognizer*)panRecognizer
{
if(panRecognizer.state == UIGestureRecognizerStateBegan)
{
//when gesture recognizer starts, making snapshot of the draggableView and hiding it
//will move shapshot that's placed on the parent of the scroll view
//that helps to prevent cutting by the scroll view bounds
self.draggableViewSnapshot = [self.draggableView snapshotViewAfterScreenUpdates: NO];
self.draggableView.hidden = YES;
[self.scrollView.superview addSubview: self.draggableViewSnapshot];
}
//your code that updates position of the draggable view
//updating snapshot center, by converting coordinates from draggable view
CGPoint snapshotCenter = [self.draggableView.superview convertPoint:self.draggableView.center toView: self.scrollView.superview];
self.draggableViewSnapshot.center = snapshotCenter;
if(panRecognizer.state == UIGestureRecognizerStateEnded ||
panRecognizer.state == UIGestureRecognizerStateCancelled ||
panRecognizer.state == UIGestureRecognizerStateFailed)
{
//when gesture is over, cleaning up the snapshot
//and showing draggable view back
[self.draggableViewSnapshot removeFromSuperview];
self.draggableViewSnapshot = nil;
self.draggableView.hidden = NO;
}
}
I recommend you look at this article by ray wenderlich Moving Table View Cells with a Long Press Gesture
It explains how to create snapshots
I have a view in an iOS application (Obj-C) which has an image view in the centre, and immediately below that a slider.
The image view shows album artwork, and the slider can be used to adjust the now-playing track position.
There is also a pair of left and right Swipe Gesture Recognizers. These are used to skip to the next or previous tracks.
The problem is that the swipe gesture recognizers seem to over-ride the users moving the slider thumb.
In my gesture recognizer code I check that the point touched was inside the image view, but it still stops the slider from being moved. (The thumb moves, but jumps back to it's original position when you remove your finger).
This is the code I use to reject the gesture if it's not inside the image view.
- (IBAction)swipeLeftGestureAction:(UISwipeGestureRecognizer *)sender {
// Get the location of the gesture.
CGPoint tapPoint = [sender locationInView:_artworkImageView];
// Make sure tap was INSIDE the artwork image frame.
if( (tapPoint.x <0)
|| (tapPoint.y < 0 )
|| (tapPoint.x > _artworkImageView.frame.size.width)
|| (tapPoint.y>_artworkImageView.frame.size.height))
{
NSLog(#"Outside!");
return;
}
NSLog(#"Swipe LEFT");
[_mediaController skipNext];
}
So my question is, how do I limit the gesture to work ONLY when swiped across the image view?
Try to put the code that restricts the gesture's area in gestureRecognizer:shouldReceiveTouch: and return NO in case you don't want the gesture to receive this touch. It should prevent the gesture from over taking the slider interaction.
If you're only interested in swipes that are inside the image view, then you should add the swipe gesture recognizer to the image view instead of adding it to your entire view. Then you won't need any special logic.
There is a dedicated method for checking if a point is inside a view
BOOL isPointInsideView = [_artworkImageViewpointInside:tapPoint withEvent:nil];
But I think what is happening is that if you will look at the tapPoint is that it actually outside of your imageView
And if your slider is really close to the imageView so the slider captures the movement, what you should do is check on the slider if it is intended for the slider and propagate on to the imageView if needed
Or inherit the UISlider and reduce its response rect
I am creating a gesture recognizer to handle zooming in a scroll view (scrolls along only one on axis at a time). I have the gesture recognizer working but I'm having trouble using the data from the gesture recognizer to transform my views properly. This is what I do right now:
- (void)handlePinch:(GPinchGestureRecognizer *)pinchRecognizer
{
CGSize contentSize = [[self scrollView] contentSize];
if (pinchRecognizer.pinchType == VerticalPinchZoomIn || pinchRecognizer.pinchType == VerticalPinchZoomOut)
{
[[self scrollView] containerView].transform = CGAffineTransformScale([pinchRecognizer transformBeforeTouches], 1, [pinchRecognizer scale]);
[[self scrollView] setContentSize:CGSizeMake(contentSize.width, [pinchRecognizer initialContentSize].height * [pinchRecognizer scale])];
}
...
...
}
This resizes the view nicely, however, it lets it get to strange positions on the screen, and sometimes the scroll view won't be able to scroll all the way to the edge of it (it thinks its boundary is in the middle of the view somewhere and bounces back).
So basically, my question is, what other properties of what other objects and whatnot do I need to operate on to get this working right? View.transform, ScrollView.contentSize, what else? If only Apple's UIScrollView implementation file was open source xD
U do need to add UIPinchGestureRecognizer or neither maintain it.
Just follow this link to add image in scrollview for zooming
I have two UIScrollViews on my screen and I need to be able to drag a UIView from one scrollview to the other.
At the moment, I have a UILongGestureRecognizer on the UIView that I want to move, such that when the user starts dragging it, I make the view follow the touch:
- (void)dragChild:(UILongPressGestureRecognizer *)longPress {
[[longPress view] setCenter:[longPress locationInView:[[longPress view] superview]]];
}
But when I get the boundary of the starting UIScrollView, the view disappears because it's locked into that scrollview's bounds.
Is there a way to "pop" it out of the scrollview when I start dragging such that I can carry it over to the other scrollview?
Also, how do I test for the "drop" location? I want to know if it's been dropped over a certain other view.
Or am I going about this all the wrong way?
Thanks guys
If you will need to drag it from a scroll view to another do the following (pseudo code)
when you start dragging do the following
//scrollView1 is the scroll view that currently contains the view
//Your view is the view you want to move
[scrollView1.superView addSubView:yourView];
now when dropping the view, you will need to know if it is inside the other scrollview
//scrollView2 is the second scroll view that you want to move it too
//Your view is the view you want to move
CGPoint point = yourView.center;
CGRect rect = scrollView2.frame;
if(CGRectContainsPoint(rect, point))
{
//Yes Point is inside add it to the new scroll view
[scrollView2 addSubView:yourView];
}
else
{
//Point is outside, return it to the original view
[scrollView1 addSubView:yourView];
}
Here is an untested idea:
When the drag begins, move the dragging-view out of the scroll view (as a subview) and into the mutual superview of both scroll views.
When the drag ends, move the dragging-view out of the superview and into the new scroll view.
You'll probably have to be careful with coordinate systems, using things like [UIView convertPoint:toView:] to convert between the views' different perspectives when moving things around.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
how to drag an uiimage from scrollview to another uiimageview in iphone sdk
In my iPad I have a view and inside it, on the left, I have a scrollview with 10 imageViews; so I should drag and drop these images from scrollview to my big subview; how can I do it?
I did something like what you are describing once. I remember to pull it off I created a new UIGestureRecgonizer that I dubbed UIDownwardDragGestureRecognizer. I think I then iterated through the scrollviews gesture recognizers to require them to wait for the UIDownwardGestureRecognizer to fail like:
UIDownwardDragGestureRecognizer *downwardGesture = [UIDownwardGestureRecognizer alloc] initWithTarget:self action:#selector(downwardGestureChanged:)];
[myScrollview addGestureRecognizer:downwardGesture];
for (UIGestureRecognizer *gestureRecognizer in myScrollview.gestureRecognizers)
{
[gestureRecognizer requireGestureRecognizerToFail:myDownwardGesture];
}
Once you have this setup, you should be able to do something like:
- (void) downwardGestureChanged:(UIDownwardDragGestureRecognizer*)gesture
{
CGPoint point = [gesture locationInView:myScrollView];
if (gesture.state == UIGestureRecognizerStateBegan)
{
UIView *draggedView = [myScrollView hitTest:point withEvent:nil];
if ([draggedView isTypeOfClass:[UIImageView class]])
{
self.imageBeingDragged = (UIImageView*)draggedView;
}
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
self.imageBeingDragged.center = point;
}
else if (gesture.state == UIGestureRecognizerStateEnded ||
gesture.state == UIGestureRecognizerStateCancelled ||
gesture.state == UIGestureRecognizerStateFailed)
{
// Determine if dragged view is in an OK drop zone
// If so, then do the drop action, if not, return it to original location
self.imageBeingDragged = nil;
}
}
In the UIGestureRecognizerStateBegan, once you find a UIImageView you may want to remove it from the superview (scrollview) and add it as a subview to some other container. You will need to translate the point into the new coordinate space if you do this. If you want the original image to stay in the scrollview, then make a copy of it and add that to an outside container.
To try out the above example and see it working, you may need to turn off clipping on the scrollview since the example I gave above is dragging the UIImageView from inside the scrollview (though normally you'd add it to some containing view).
On way I have seen this done is by using an overlay layer to capture the touches and pass them through. This allows you to intercept the clicks before the UIScrollView consumes them.
Here is the demo video that I looked at when trying to construct a UITableView (inside a scroll view) that could have cells dragged and dropped on to it.
Also I am not sure how applicable this is but here is a similar post I made a few days ago.
Having reviewed this issue some more I built something that allows you to drag and drop cells between UITableViews similar to the video I mentioned previously. My tutorial can be found here and the result can be seen in this YouTube video. This method uses the latest gesture features in iOS 5 and pops cells off using a long press.
Hope you can find something in here that helps.