Panning a subview of UIScrollView after zooming in - ios

I have added a subview to a UIScrollView. When I zoom into the scroll view I want to pan around the subview.
In touchesBegan: I'm getting the initial location of the touch and then touchesMoved: I am able to determine how much to move the subview. It works perfectly when zoomscale is 1.0. However, when it is zoomed the pointer "breaks out" of the subview which it is intended to move (illustration here - pointer location is ilustrated as marquee tool).
The center of the view should be on pointer location, and not in it's current position! px and py variables ensure that wherever on the subview is clicked, while dragging it postion of the pointer always stays the same. illustration
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
location.x = location.x * self.zoomScale;
location.y = location.y * self.zoomScale;
px = location.x;
py = location.y;
if ([touch view] == rotateView) {
self.scrollEnabled = NO;
return;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
location.x = location.x * self.zoomScale;
location.y = location.y * self.zoomScale;
if ([touch view] == rotateView) {
rotateView.center = CGPointMake(rotateView.center.x + (location.x - px), rotateView.center.y + (location.y - py));
px = location.x;
py = location.y;
return;
}
    }

Instead of the approach you're taking, make the subview another UIScrollView and let it handle the panning.
(You may wish to set scrollEnabled = NO on your subview until zooming has occurred.)

Related

Using collision on bounds of UIView

I have an ImageView that moves around the UIView, is it possible to detect collision of the ImageView and the view its self? For example the ImageView hits the side of the view I want it to run an action. -(void)restart {}
If this is possible can you detect which side it has collided with?
You can create a custom UIImageVIew and implement the methods touchBegan and touchMoved (don't forget to add [self setUserInteractionEnabled:YES] in your init method). Then set the rect you want to interact with :
customImageView.interactRect = myView.frame;
And in your customImageView you can add something like:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
lastPosition = [touch locationInView: self.superview];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint position = [touch locationInView:self.superview];
CGRect currentFrame = self.frame;
currentFrame.origin = CGPointMake(currentFrame.origin.x + position.x - lastPosition.x, currentFrame.origin.y + position.y - lastPosition.y);
if (CGRectIntersectsRect(currentFrame, interactRect) && !CGRectIntersectsRect(self.frame, interactRect))
{
NSLog(#"I'm in for the first time");
if(self.frame.origin.x + self.frame.size.width <= interactRect.origin.x && currentFrame.origin.x + currentFrame.size.width > interactRect.origin.x)
{
NSLog(#"Coming from the left");
}
if(self.frame.origin.x >= interactRect.origin.x + interactRect.size.width && currentFrame.origin.x < interactRect.origin.x + interactRect.size.width)
{
NSLog(#"Coming from the right");
}
}
self.frame = currentFrame;
lastPosition = position;
}

Trying to move UIImageView, moving whole Screen View

I want my UIViewImages to be movable with touch. I'm trying to use code implemented in my ViewController:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([touches count]==1) {
UITouch *touch = [touches anyObject];
CGPoint p0 = [touch previousLocationInView:self.view];
CGPoint p1 = [touch locationInView:self.view];
CGPoint center = self.view.center;
center.x += p1.x - p0.x;
center.y += p1.y - p0.y;
self.view.center = center;
}
}
When I try to drag an UIImageView, I'm dragging whole screen, which is incorrect.
Need help!
Karol
You create a gesture recognizer and add it to a view like this:
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(gestureRecognizerMethod:)];
[self.imageView addGestureRecognizer:panGestureRecognizer];
you can adjust the position of the image view
- (void)gestureRecognizerMethod:(UIPanGestureRecognizer *)recogniser
{
if (recognizer.state == UIGestureRecognizerStateBegan || recognizer.state == UIGestureRecognizerStateChanged)
{
CGPoint touchLocation = [recognizer locationInView:self.view];
self.imageView.center = touchLocation;
}
}
read this article.
If I read your code right self.view actually IS the whole screen.
Maybe you mean self.yourImageView instead?

Scaling custom UIView by CGAffineTransformMakeScale breaks dragging with touchesMoved

I have prepared and checked into GitHub a simple test case for dragging custom UIView:
It works well and dragging is implemented in the Tile.m as:
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
CGPoint previous = [touch previousLocationInView:self];
self.frame = CGRectOffset(self.frame,
(location.x - previous.x),
(location.y - previous.y));
}
However the tiles are too large for my actual game (and I will have to adjust their scaling once the game board in UIScrollView is zoomed anyway):
So I scale my custom UIViews down by calling
tile.transform = CGAffineTransformMakeScale(.5, .5);
The size changes, but the tiles are suddenly dragged "twice as quickly" as needed:
Probably the touchesMoved code (s. above) should be adjusted, but I'm not sure what has been broken there by the transform manipulation?
Nevermind, I have found the solution:
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
CGPoint previous = [touch previousLocationInView:self];
if (!CGAffineTransformIsIdentity(self.transform)) {
location = CGPointApplyAffineTransform(location, self.transform);
previous = CGPointApplyAffineTransform(previous, self.transform);
}
self.frame = CGRectOffset(self.frame,
(location.x - previous.x),
(location.y - previous.y));
}

Superview does not receive the touch action

I have one base view A with size of (0,0,320,280), it contain another view B with size of
(100,100,50,50) and View B has one button and one image view(C) as sub view. image view frame is same as view B, button has added in B's top left corner.
My requirement is when we drag the B's bottom right corner its size has to increased or decreased.
if we drag the B from any other place except bottom right corner it has to move. view size should not be modified.
My problem is view B does not receive the touch action.
i added the code below. please guide me.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
//baseVw-------> view B//
if ([touch view]==baseVw)
{
touchStart = [[touches anyObject] locationInView:baseVw];
isResizingLR = (baseVw.bounds.size.width - touchStart.x < kResizeThumbSize && baseVw.bounds.size.height - touchStart.y < kResizeThumbSize);
isResizingUL = (touchStart.x <kResizeThumbSize && touchStart.y <kResizeThumbSize);
isResizingUR = (baseVw.bounds.size.width-touchStart.x < kResizeThumbSize && touchStart.y<kResizeThumbSize);
isResizingLL = (touchStart.x <kResizeThumbSize && baseVw.bounds.size.height -touchStart.y <kResizeThumbSize);
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPoint = [[touches anyObject] locationInView:baseVw];
CGPoint previous=[[touches anyObject]previousLocationInView:baseVw];
float deltaWidth = touchPoint.x-previous.x;
float deltaHeight = touchPoint.y-previous.y;
if ([touch view]==baseVw)
{
if (isResizingLR)
{
baseVw.frame = CGRectMake(baseVw.frame.origin.x, baseVw.frame.origin.y,touchPoint.x + deltaWidth, touchPoint.y + deltaWidth);
}
else
{
CGPoint activePoint = [[touches anyObject] locationInView:baseVw];
// Determine new point based on where the touch is now located
CGPoint newPoint = CGPointMake(baseVw.center.x + (activePoint.x - touchStart.x),
baseVw.center.y + (activePoint.y - touchStart.y));
//--------------------------------------------------------
// Make sure we stay within the bounds of the parent view
//--------------------------------------------------------
float midPointX = CGRectGetMidX(baseVw.bounds);
// If too far right...
if (newPoint.x > baseVw.superview.bounds.size.width - midPointX)
newPoint.x = baseVw.superview.bounds.size.width - midPointX;
else if (newPoint.x < midPointX) // If too far left...
newPoint.x = midPointX;
float midPointY = CGRectGetMidY(baseVw.bounds);
// If too far down...
if (newPoint.y > baseVw.superview.bounds.size.height - midPointY)
newPoint.y = baseVw.superview.bounds.size.height - midPointY;
else if (newPoint.y < midPointY) // If too far up...
newPoint.y = midPointY;
// Set new center location
baseVw.center = newPoint;
}
}
}
View B probably does not receive any touch events because these are absorbed by the subview C. Assuming you implemented the touch methods in B you should also make sure that you set B as the firstResponder. Implement this:
-(BOOL)canBecomeFirstResponder
{
return YES;
}
And then call [self becomeFirstResponder]; after you add C as the subview. The code inside the touchesBegan/Moved doesn't matter if they are not being called (touch events not received).

Boundary for draggable image ios

I'm trying to create a draggable image, but i'm trying to confine it's dragging to within a small square rather than the full screen. Could someone tell me where i'm going wrong?. I've placed the code that I have so far below:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
if([touch view] == dot) {
CGPoint location = [touch locationInView:self.view];
dot.center = location;
if (location.x >10) {
location.x =10;
} else if (location.x <10) {
location.x = 10;
}
if (location.y >20) {
location.y =20;
} else if (location.y < 20) {
location.y = 20;
}
}
}
You are assigning location before you are making changes to it.
Apply your limits to location first then assign it to dot.
Also, your limits you are showing would lock your position to 10,20 since you are not allowing it to be more than 10 or less than 10. Likewise with 20.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
if([touch view] == dot) {
CGPoint location = [touch locationInView:self.view];
location.x = MIN(MAX(location.x, 0),10);
location.y = MIN(MAX(location.y, 0),20);
dot.center = location;
}
}
I have implemented an image dragging function recently like this. I use the PAN Gesture to move the image which results in two CGFloats "endPointX and endPointY". In the code below between the comments "Stay on Screen Check" and "End Stay on Screen check", I check if these are on the screen. If not I adjust them to prevent the image moving off the screen.
I hope that helps. If you want to move the image within a small part of the total screen then I would add the image to a holder subview and then check the holder view .bounds.size.width/height above instead.
CGFloat endPointX = translatedPoint.x + (.35*[(UIPanGestureRecognizer*)sender
velocityInView:self.view].x);
CGFloat endPointY = translatedPoint.y + (.35*[(UIPanGestureRecognizer*)sender velocityInView:self.view].y);
// Stay on the screen check
if(endPointX < 0) {
endPointX = 0;
} else if(endPointX > self.view.bounds.size.width) {
endPointX = self.view.bounds.size.width;
}
if(endPointY < 0) {
endPointY = 0;
} else if(endPointY > self.view.bounds.size.height) {
endPointY = self.view.bounds.size.height;
}
// End of the Stay on Screen check
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:.35];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[[sender view] setCenter:CGPointMake(endPointX, endPointY)];
[UIView commitAnimations];

Resources