I've some drawings on my custom view, want to let users rotate it
viewContorller.m:
-(void)setMyView:(myView *)myView {
...
[self.faceView addGestureRecognizer:[[UIRotationGestureRecognizer alloc] initWithTarget:faceView action:#selector(rotate:)]];
...
}
faceView.m
- (void)rotate:(UIRotationGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateChanged) {
self.transform = CGAffineTransformMakeRotation(gesture.rotation);
gesture.rotation = 0;
}
}
It just not work, but quite shaking?
Use following code and also add UIGestureRecognizerDelegate in viewController.h class.
-(void)setMyView:(myView *)myView {
//...
UIRotationGestureRecognizer* rotateRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotate:)];
rotateRecognizer.delegate = self;
[self.faceView addGestureRecognizer:rotateRecognizer];
//...
}
I think it will be helpful to you.
Ok, the problem is with your rotate method, try this code,
- (void)rotate:(UIRotationGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateChanged) {
self.transform = CGAffineTransformRotate(self.transform, [gesture rotation]);
gesture.rotation = 0;
}
}
Use this code to rotate your view ,enable userinteraction and multi touch enable on you view.
- (void)viewDidLoad
{
[super viewDidLoad];
UIRotationGestureRecognizer *rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotatePiece:)];
[self.myview addGestureRecognizer:rotationGesture];
}
- (void)rotatePiece:(UIRotationGestureRecognizer *)gestureRecognizer
{
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
[gestureRecognizer view].transform = CGAffineTransformRotate([[gestureRecognizer view] transform], [gestureRecognizer rotation]);
[gestureRecognizer setRotation:0];
}
}
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
UIView *piece = gestureRecognizer.view;
CGPoint locationInView = [gestureRecognizer locationInView:piece];
CGPoint locationInSuperview = [gestureRecognizer locationInView:piece.superview];
piece.layer.anchorPoint = CGPointMake(locationInView.x / piece.bounds.size.width, locationInView.y / piece.bounds.size.height);
piece.center = locationInSuperview;
}
}
This code is from apples sample code touches.
Related
I was adding multiple gestures in one view, The view have a one close button at corner of the view, Everything is working fine but when I am zoom that view close button was also zoom with that view, Now I want to zoom only that view not that close button, Please suggest me How to do this ?
see below image for reference.
Code for pinch Zoom
-(void)addStickersWithView:(UIView*)view image:(UIImage*)image{
CGPoint center = self.imgPhoto.center;
UIImageView *imgView = [[UIImageView alloc] initWithImage:image];
UIRotationGestureRecognizer *rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotatePiece:)];
[imgView setContentMode:UIViewContentModeScaleToFill];
UIView *viewZoom = [[UIView alloc] initWithFrame:CGRectMake(center.x-45,center.y-45, 90, 90)];
// [viewZoom setBackgroundColor:[UIColor redColor]];
imgView.frame = CGRectMake(5, 5, CGRectGetWidth(viewZoom.frame)-10, CGRectGetHeight(viewZoom.frame)-10);
[viewZoom addSubview:imgView];
[viewZoom addGestureRecognizer:rotationGesture];
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(scalePiece:)];
[pinchGesture setDelegate:self];
[viewZoom addGestureRecognizer:pinchGesture];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(moveImage:)];
[panGesture setMinimumNumberOfTouches:1];
[panGesture setMaximumNumberOfTouches:1];
[viewZoom addGestureRecognizer:panGesture];
UIButton *btnCloseSticker = [UIButton buttonWithType:UIButtonTypeCustom];
[btnCloseSticker setBounds:CGRectMake(0, 0, 30,30)];
[btnCloseSticker setImage:[UIImage imageNamed:#"close1.png"] forState:UIControlStateNormal];
[btnCloseSticker addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
self.stickerCount++;
btnCloseSticker.tag = self.stickerCount;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(TapToShowClose:)];
tapGesture.numberOfTapsRequired = 2;
[viewZoom addGestureRecognizer:tapGesture];
viewZoom.layer.borderColor = [UIColor whiteColor].CGColor;
viewZoom.layer.borderWidth = 3.0;
viewZoom.tag = self.stickerCount+kTagBorder;
[viewZoom addSubview:btnCloseSticker];
[view addSubview:viewZoom];
}
-(void)hideShowBorderCloseButton{
int borderCount = self.stickerCount-kTagBorder;
for(int i=1;i<=borderCount;i++){
UIView *view = [self.viewStickers viewWithTag:i+kTagBorder];
UIView *view1 = [self.viewStickers viewWithTag:i+(kTagBorder*2)];
view.hidden = YES;
view1.layer.borderWidth = 0.0;
}
}
-(void)buttonPressed:(id)sender{
UIView *view = [sender superview];
[view removeFromSuperview];
self.stickerCount--;
}
- (void)scalePiece:(UIPinchGestureRecognizer *)gestureRecognizer {
if([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
// Reset the last scale, necessary if there are multiple objects with different scales
lastScale = [gestureRecognizer scale];
}
UIButton *btn = [self.viewStickers viewWithTag:gestureRecognizer.view.tag-kTagBorder];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ||
[gestureRecognizer state] == UIGestureRecognizerStateChanged) {
CGFloat currentScale = [[[gestureRecognizer view].layer valueForKeyPath:#"transform.scale"] floatValue];
// Constants to adjust the max/min values of zoom
const CGFloat kMaxScale = 3.0;
const CGFloat kMinScale = 1.0;
CGFloat newScale = 1 - (lastScale - [gestureRecognizer scale]);
newScale = MIN(newScale, kMaxScale / currentScale);
newScale = MAX(newScale, kMinScale / currentScale);
CGAffineTransform transform = CGAffineTransformScale([[gestureRecognizer view] transform], newScale, newScale);
[gestureRecognizer view].transform = transform;
lastScale = [gestureRecognizer scale]; // Store the previous scale factor for the next pinch gesture call
}
}
-(void)TapToShowClose:(UITapGestureRecognizer *)gestureRecognizer{
//int borderCount = self.stickerCount-kTagBorder;
UIView *view = gestureRecognizer.view; //cast pointer to the derived class if needed
view.layer.borderWidth = 2.0;
UIView *view1 = [self.viewStickers viewWithTag:view.tag-kTagBorder];
[(UIButton*)view1 setImage:[UIImage imageNamed:#"close1.png"] forState:UIControlStateNormal];
[view1 setBounds:CGRectMake(0, 0, 30,30)];
view1.hidden = NO;
}
- (void)moveImage:(UIPanGestureRecognizer *)recognizer
{
CGPoint newCenter = [recognizer translationInView:self.view];
[self hideShowBorderCloseButton];
if([recognizer state] == UIGestureRecognizerStateBegan) {
beginX = recognizer.view.center.x;
beginY = recognizer.view.center.y;
}
newCenter = CGPointMake(beginX + newCenter.x, beginY + newCenter.y);
[recognizer.view setCenter:newCenter];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
// if the gesture recognizers are on different views, don't allow simultaneous recognition
if (gestureRecognizer.view != otherGestureRecognizer.view)
return NO;
// if either of the gesture recognizers is the long press, don't allow simultaneous recognition
if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] || [otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]])
return NO;
return YES;
}
- (void)rotatePiece:(UIRotationGestureRecognizer *)gestureRecognizer {
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];
[self hideShowBorderCloseButton];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
[gestureRecognizer view].transform = CGAffineTransformRotate([[gestureRecognizer view] transform], [gestureRecognizer rotation]);
[gestureRecognizer setRotation:0];
}
}
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer {
[self hideShowBorderCloseButton];
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
UIView *piece = gestureRecognizer.view;
CGPoint locationInView = [gestureRecognizer locationInView:piece];
CGPoint locationInSuperview = [gestureRecognizer locationInView:piece.superview];
piece.layer.anchorPoint = CGPointMake(locationInView.x / piece.bounds.size.width, locationInView.y / piece.bounds.size.height);
piece.center = locationInSuperview;
}
}
In place of scaling view. it's batter to calculate the frame with your zoom level. and update the frame of the view. check out the below method.
- (IBAction)scalePiece:(UIPinchGestureRecognizer *)gestureRecognizer {
if([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
// Reset the last scale, necessary if there are multiple objects with different scales
lastScale = [gestureRecognizer scale];
}
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ||
[gestureRecognizer state] == UIGestureRecognizerStateChanged) {
CGFloat newScale = 1 - (lastScale - [gestureRecognizer scale]);
[self changeScale:newScale];
lastScale = [gestureRecognizer scale]; // Store the previous scale factor for the next pinch gesture call
}
}
-(void)changeScale :(float)newScale{
CGAffineTransform transform = CGAffineTransformScale([viewForpinch transform], newScale, newScale);
viewForpinch.transform = transform;
float scale = viewForpinch.transform.a;
float buttonScale = 1 / scale;
btnl.transform= CGAffineTransformScale(CGAffineTransformIdentity, buttonScale, buttonScale);
}
The easy way to do it is to remove the close button from that view, and add it on top of the view you're zooming with a different view.
You can use. insertSubview:aboveSubview:
My problem is that I can't use Pinch and Pan(
both functions work perfectly separate. but if I move pic - zooming have the wrong centre anchor (between 2 fingers)
code:
//fullScreenImage is UIImageView that i would like to manipulate
UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(scalePiece:)];
pinchRecognizer setDelegate:(id)self];
[fullScreenImage.superview addGestureRecognizer:pinchRecognizer];
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(move:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
[panRecognizer setDelegate:(id)self];
[self.view addGestureRecognizer:panRecognizer];
- (void)scalePiece:(UIPinchGestureRecognizer *)gestureRecognizer
{
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
[gestureRecognizer view].transform = CGAffineTransformScale([[gestureRecognizer view] transform], [gestureRecognizer scale], [gestureRecognizer scale]);
[gestureRecognizer setScale:1];
}
//Setting anchor between 2 fingers to zoom correctly
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
UIView *piece = gestureRecognizer.view;
CGPoint locationInView = CGPointMake([gestureRecognizer locationInView:piece].x,[gestureRecognizer locationInView:piece].y);
CGPoint locationInSuperview = CGPointMake([gestureRecognizer locationInView:piece.superview].x,[gestureRecognizer locationInView:piece.superview].y);
piece.layer.anchorPoint = CGPointMake(locationInView.x / piece.bounds.size.width, locationInView.y / piece.bounds.size.height);
piece.center = locationInSuperview;
}
}
- (void)move:(UIPinchGestureRecognizer *)recognizer
{
if ([(UIPanGestureRecognizer*)recognizer state] == UIGestureRecognizerStateBegan) {
position.y=[recognizer locationInView:recognizer.view].y-fullScreenImage.center.y;
position.x=[recognizer locationInView:recognizer.view].x-fullScreenImage.center.x;
}
fullScreenImage.center=CGPointMake([recognizer locationInView:fullScreenImage.superview].x-position.x, [recognizer locationInView:fullScreenImage.superview].y-position.y);
}
Try this One:
Add this method in your code :
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
UIView *piece = gestureRecognizer.view;
CGPoint locationInView = [gestureRecognizer locationInView:piece];
CGPoint locationInSuperview = [gestureRecognizer locationInView:piece.superview];
piece.layer.anchorPoint = CGPointMake(locationInView.x / piece.bounds.size.width, locationInView.y / piece.bounds.size.height);
piece.center = locationInSuperview;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
And call this method when you are using gesture.
- (void)scalePiece_image:(UIPinchGestureRecognizer *)gestureRecognizer
{
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged)
{
[gestureRecognizer view].transform = CGAffineTransformScale([[gestureRecognizer view] transform], [gestureRecognizer scale], [gestureRecognizer scale]);
[gestureRecognizer setScale:1];
}
}
SR Nayak answer is not correct because the pivot will change when you pinch and the layer will slide.
This code works:
- (IBAction)pinchLayer:(UIPinchGestureRecognizer *)gestureRecognizer {
static CGPoint lastPoint;
static CGFloat lastScale;
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
lastScale = 1.0;
lastPoint = [gestureRecognizer locationInView:self.containerView];
}
// Scale
CGFloat scale = 1.0 - (lastScale - gestureRecognizer.scale);
[[gestureRecognizer view].layer setAffineTransform:
CGAffineTransformScale([[gestureRecognizer view].layer affineTransform],
scale,
scale)];
lastScale = gestureRecognizer.scale;
// Translate
CGPoint point = [gestureRecognizer locationInView:self.containerView];
[[gestureRecognizer view].layer setAffineTransform:
CGAffineTransformTranslate([[gestureRecognizer view].layer affineTransform],
point.x - lastPoint.x,
point.y - lastPoint.y)];
lastPoint = [gestureRecognizer locationInView:self.containerView];
}
I am trying to rotate a view , with UIRotationGestureRecognizer but the problem is the rotation's angle is not center of my view !! here is my code :
UIRotationGestureRecognizer *rotationRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotate:)];
[rotationRecognizer setDelegate:self];
[self.view addGestureRecognizer:rotationRecognizer];
-(void)rotate:(id)sender {
if([(UIRotationGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded) {
_lastRotation = 0.0;
return;
}
CGFloat rotation = 0.0 - (_lastRotation - [(UIRotationGestureRecognizer*)sender rotation]);
CGAffineTransform currentTransform = textViews.transform;
CGAffineTransform newTransform = CGAffineTransformRotate(currentTransform,rotation);
_lastRotation = [(UIRotationGestureRecognizer*)sender rotation];
[textViews setTransform:newTransform];
}
// create and configure the rotation gesture
UIRotationGestureRecognizer *rotationGestureRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotationGestureDetected:)];
[rotationGestureRecognizer setDelegate:self];
[YOUR_OBJECT_HERE addGestureRecognizer:rotationGestureRecognizer];
// YOUR_OBJECT_HERE - can be any object that you want to rotate (e.g. textView as in your case)
- (void)rotationGestureDetected:(UIRotationGestureRecognizer *)recognizer
{
UIGestureRecognizerState state = [recognizer state];
if (state == UIGestureRecognizerStateBegan || state == UIGestureRecognizerStateChanged)
{
CGFloat rotation = [recognizer rotation];
[recognizer.view setTransform:CGAffineTransformRotate(recognizer.view.transform, rotation)];
[recognizer setRotation:0];
}
}
When scaling an image view using the UIPinchGestureRecognizer when the action is ongoing the image "tries" to remain at the initial frame.origin.
Even after a pan gesture has moved the image to another location a pinch promptly moves it back to the initial location.
All of the code for this blatant copy of Apple's Touches sample app that demonstrates gestures.
Below is the code for pinch, I have pushed a sample app on github demonstrating this behavior: https://github.com/atokubi/TestImageManipulation
Thanks in advance for your assistance.
- (IBAction)scaleItem:(UIPinchGestureRecognizer *)gestureRecognizer {
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
[gestureRecognizer view].transform = CGAffineTransformScale([[gestureRecognizer view] transform], [gestureRecognizer scale], [gestureRecognizer scale]);
[gestureRecognizer setScale:1];
}
}
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
UIView *piece = gestureRecognizer.view;
CGPoint locationInView = [gestureRecognizer locationInView:piece];
CGPoint locationInSuperview = [gestureRecognizer locationInView:piece.superview];
piece.layer.anchorPoint = CGPointMake(locationInView.x / piece.bounds.size.width, locationInView.y / piece.bounds.size.height);
piece.center = locationInSuperview;
}
}
If you want to properly re center the image, you can use this method :
// To re-center the image after zooming in and zooming out
- (void)centerViewContents
{
CGSize boundsSize = self.bounds.size;
CGRect contentsFrame = self.imageView.frame;
if (contentsFrame.size.width < boundsSize.width)
{
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
}
else
{
contentsFrame.origin.x = 0.0f;
}
if (contentsFrame.size.height < boundsSize.height)
{
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
}
else
{
contentsFrame.origin.y = 0.0f;
}
self.imageView.frame = contentsFrame;
}
Add this method in your "adjustAnchorPointForGestureRecognizer" ,ethod like this:
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
UIView *piece = gestureRecognizer.view;
CGPoint locationInView = [gestureRecognizer locationInView:piece];
CGPoint locationInSuperview = [gestureRecognizer locationInView:piece.superview];
piece.layer.anchorPoint = CGPointMake(locationInView.x / piece.bounds.size.width, locationInView.y / piece.bounds.size.height);
piece.center = locationInSuperview;
[self centerViewContents];
}
}
I downloaded your code and implemented this. It works.
Please let me know if it helps.
I have several UIImageViews that the user can drag, scale and rotate. I want the position, size and angle to persist when the user navigates to another view and comes back. How do i achieve this?
I have used the following code to pan, rotate and scale
- (void)panPiece:(UIPanGestureRecognizer *)gestureRecognizer
{
UIView *piece = [gestureRecognizer view];
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
CGPoint translation = [gestureRecognizer translationInView:[piece superview]];
[piece setCenter:CGPointMake([piece center].x + translation.x, [piece center].y + translation.y)];
[gestureRecognizer setTranslation:CGPointZero inView:[piece superview]];
}
}
- (void)rotatePiece:(UIRotationGestureRecognizer *)gestureRecognizer
{
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
[gestureRecognizer view].transform = CGAffineTransformRotate([[gestureRecognizer view] transform], [gestureRecognizer rotation]);
[gestureRecognizer setRotation:0];
}
}
- (void)scalePiece:(UIPinchGestureRecognizer *)gestureRecognizer
{
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
[gestureRecognizer view].transform = CGAffineTransformScale([[gestureRecognizer view] transform], [gestureRecognizer scale], [gestureRecognizer scale]);
[gestureRecognizer setScale:1];
}
}
Depending on your needs you can create method, that you'll add as another target method to all your gesture recognizers.
E.g.
[panGestureRecognizer addTarget: self action: #selector(onGestureRecognizerEnd:)];
[rotationGestureRecognizer addTarget: self action: #selector(onGestureRecognizerEnd:)];
[pinchGestureRecognizer addTarget: self action: #selector(onGestureRecognizerEnd:)];
Sample implementation of that method could be:
- (void)onGestureRecognizerEnd: (UIGestureRecognizer *)gestureRecognizer
{
switch ( gestureRecognizer.state )
{
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateFailed:
{
[self _saveViewPosition: gestureRecognizer.view];
break;
}
default:
{
break;
}
}
}
_saveViewPosition: method you can implement like this:
- (void)_saveViewPosition: (UIView *)view
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject: [NSKeyedArchiver archivedDataWithRootObject: [NSValue valueWithCGPoint: view.center]]
forKey: [self _centerKeyForView: view]];
[defaults setObject: [NSKeyedArchiver archivedDataWithRootObject: [NSValue valueWithCGAffineTransform: view.transform]]
forKey: [self _transformKeyForView: view]];
}
For more information check Xcode project.
BR