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.
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've figured out how to drag a view from one scrollView to another. What I'm trying to figure out now is how to position the view at the specific drop point when I stop dragging the view.
Currently when I drop the view it is slightly up and to the left than where I intended to drop it.
This is how I'm adding it into my new view once I've detected that dragging has stopped:
CGPoint point = draggableView.center;
if (CGRectContainsPoint(self.myView.frame, point)) {
NSLog(#"Welp! My view is still inside side bar.");
//Do Something Eventually
} else {
NSLog(#"Nice my view is within the bounds of the new scrollView.");
//Troop is inside editing view, so add it
[self.editingView addSubview:self.myView];
}
I have an application I'm making in storyboards. I want to have a tutorial-type of view. I decided to go with a freeform view controller and am filling it with 600x600 views that count as the pages. The problem I'm having is that when I have a UI button animate to the next page, the buttons that were created outside of the visible view don't seem to work. I even moved a view over so the button was half-visible and only half the button works when I move the view over.
Here's my next page code:
- (void)nextPage {
if (scrolling) return;
scrolling = YES;
[UIView animateWithDuration:0.3 animations:^{
CGRect frame = self.tutorialView.frame;
frame.origin.x -= 50; //frame.size.width;
[self.tutorialView setFrame:frame];
}];
scrolling = NO;
}
I currently have it moving only 50px instead of the whole page for testing purposes.
For testing purposes, I've started it half-way through and only half the button works. I've started it with the second view on top of the other one halfway visible and the same thing happens (only half the button works). Otherwise, when I tap the next button on the first view the buttons on the second view don't work (buttons created outside the initial view).
Views don't receive touch events where they're outside the bounds of their superview. You'll need to increase the button's superview's size.
You can visualize this behavior by setting clipsToBounds = YES - then you'll only see the touchable area of your button.
(You can override this behavior, but you probably shouldn't.)
I have the following hierarchy:
view
scrollview(with scroll and zoom enabled)
containerview
imageview (with a map)
views for buttons
views for navigation panel
Over the image view I have to put some point of interest (depending on the category the user selects):
The problems are:
if i attach them to the container view, they getting the correct position is very easy,
but when zooming they will zoom with the image, and they should keep the same size (1)
if i attach them to the scrollview I don’t see nothing..
If I attach them to the main view, I have two problems:
1 to find their hierarchy position in between scrollview and view for buttons, etc (they get at the top of the application and should be just over the map but below the control panels, buttons, etc
2 to coordinate with the zoom and scroll- done with convertPoint:toView
If I add a view (sibling of container view under the scrollview) they show fine, but I have no user input on them (They don’t receive
touches)
If I convert that sibling subview to a CALayer, I don’t get the sublayers (POIs) to shown.
(1) Ive tried to subclass container view and override setTransform by applying CGAffineTransform invertedTransform = CGAffineTransformInvert(transform); but I had problems passing it an array with views references to it
What would you recommend to do in this case?
Thank you very much!
S.
I've finally added subviews to containerView and I've subclassed it:
- (void)setTransform:(CGAffineTransform)transform
{
[super setTransform:transform];
CGAffineTransform invertedTransform = CGAffineTransformInvert(transform);
for (UIView *view in self.subviews)
{
if ([view isKindOfClass:[MarkerClass class]]) {
[view setTransform:invertedTransform];
}
}
}
The only problem is that before I zoom the containerView, a short sized version of the markers appear on the screen.
After passing some time, I've found I would override addSubview too, to get an initial transform in this way:
-(void)addSubview:(UIView *)view {
[super addSubview:view];
if ([view isKindOfClass:[MarkerClass class]]) {
CGAffineTransform invertedTransform = CGAffineTransformInvert(self.transform);
[view setTransform:invertedTransform];
}
}
I understand how to implement UISwipeGestureRecognizer, but I do not understand how to use multiple views properly in collaboration with it.
My ultimate goal is to provide a sleek interface where buttons or swiping may be used to change views. I am hoping for a constant background rather than a background image show it is transferring to the exact same image again. I am using storyboarding currently where each view has its own controller, and I would like (if possible) to keep it as close to this format for visual clarity of what is going on.
I have read that subviews may be my best hope for this sort of scenario (keeping them to the right until a swipe is called), but that would break up the current storyboarding structure I am using, and most certainly ruin any visual clarity.
Does anyone have any suggestions?
I did same programmatically, but you can achieve same using storyboard
1. You need base view which will keep same background
2. Add 3 Views (or 2 depending on ur requirement) Left, Middle, Right with transparent backgrounds as follows in viewDidLoad of base view controller
[self addChildViewController:myChildViewController];
[myChildViewController view].frame = CGMakeRect(x,y,width,height);//Left one will have x = -320, y=0 ,Middle x= 0, y= 0, Right x = 32O etc.
/* Add new view to source view */
[self.view addSubview:[myChildViewController view]];
/* Inform 'didMoveToParentViewController' to newly added view controller */
[myChildViewController didMoveToParentViewController:self];
3. In handle Swipe method change frames of view with animation which will make it look like swiping page
e.g
//swipe left, get right view in middle
[UIView animateWithDuration:0.2 animations:^{
CGRect frame = rightViewController.view.frame;
frame.origin.x = 0;
frame.origin.y = 0;
rightViewController.view.frame = frame;
CGRect middleViewFrame = middleViewController.view.frame;
middleViewFrame.origin.x = -320;
middleViewController.view.frame = middleViewFrame;
}];
You could use a navigation to do this. If you don't want the navigation bar, you can hide it. You can connect the left swipe gesture recognizer to a segue to the next controller, and connect the right swipe gesture recognizer to an unwind segue (or call popViewControllerAnimated: from its action method). If you want a constant background, then you need to add that to the navigation controller's view (as a background color), and have the child view controller's views be transparent (or partially so). This code in the navigation controller's root view controller will add the background:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationController.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"img.tiff"]];
}
I know exactly what you're talking about and I'm actually doing the same with my current project.
I accomplished this by using Interface Builder and a single view controller. Basically I placed a UIImageView in the view and set it to the background you wish, then I put a transparent UIScrollView on top of it. Inside that scroll view I put the two views I want to scroll horizontally through.
Don't forget to enable UIScrollView paging. In my case, I had two table views. Starting from the first one, if you swiped from right to left it would scroll to the next table view, and if you swiped left to right it would scroll back to the first.