Transforming view coordinates with CGAffineTransform - iphone

I'm fooling around with Apple's ScrollViewSuite, TapToZoom app, trying to place a view on the zoomed image. Here's what I want to do: on double tap, rotate the image by pi/2, zoom it by a factor of 8, then place a subview in the scrollView on top of the transformed image.
I know the view's desired frame in the original coordinate system. So I need to transform those coordinates to the new system after the zoom...
From the ScrollViewSuite...
- (void)handleDoubleTap:(UIGestureRecognizer *)gestureRecognizer {
// double tap zooms in
float newScale = [imageScrollView zoomScale] * 8; // my custom zoom
CGRect zoomRect = [self zoomRectForScale:newScale withCenter:[gestureRecognizer locationInView:gestureRecognizer.view]];
[imageScrollView zoomToRect:zoomRect animated:YES];
[self rotateView:imageScrollView by:M_PI_2]; // then the twist
}
My rotation code...
- (void)rotateView:(UIView *)anImageView by:(CGFloat)spin {
[UIView animateWithDuration:0.5 delay: 0.0
options: UIViewAnimationOptionAllowUserInteraction
animations:^{
anImageView.transform = CGAffineTransformMakeRotation(spin);
}
completion:^(BOOL finished){}];
}
Then, after the zoom is finished, place a view at a known location in the original coordinate system. This is where I think I need some help...
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)_scale {
CGFloat coX = scrollView.contentOffset.x;
CGFloat coY = scrollView.contentOffset.y;
// try to build the forward transformation that got us here, then invert it
CGAffineTransform rotate = CGAffineTransformMakeRotation(M_PI_2);
CGAffineTransform scale = CGAffineTransformMakeScale(_scale, _scale);
// should these be negative? some confusion here.
CGAffineTransform translate = CGAffineTransformMakeTranslation(coX, coY);
// what's the right order of concatenation on these? more confusion
CGAffineTransform aft0 = CGAffineTransformConcat(rotate, scale);
CGAffineTransform aft1 = CGAffineTransformConcat(aft0, translate);
// invert it
CGAffineTransform aft = CGAffineTransformInvert(aft1);
// this is the fixed rect in original coordinate system of the image
// the image is at 0,0 320, 300 in the view, so this will only work
// when the image is at the origin... need to fix that later,too.
CGRect rect = CGRectMake(248, 40, 90, 160);
CGRect xformRect = CGRectApplyAffineTransform(rect, aft);
// build a subview with this rect
UIView *newView = [scrollView viewWithTag:888];
if (!newView) {
newView = [[UIView alloc] initWithFrame:xformRect];
newView.backgroundColor = [UIColor yellowColor];
newView.tag = 888;
[scrollView addSubview:newView];
} else {
newView.frame = xformRect;
}
// see where it landed...
NSLog(#"%f,%f %f,%f", xformRect.origin.x, xformRect.origin.y, xformRect.size.width, xformRect.size.height);
}

Related

UIView Rounded Circular view Transition

I have implemented Circular transitions as i asked in
animating UIView transitions like expand dot to circle
using following code:
-(IBAction)nextViewOpen
{
NextViewVC *NextView= [self.storyboard instantiateViewControllerWithIdentifier:#"RBANextViewVC"];
[NextView.view.layer setCornerRadius:25.0f];
[NextView.view.layer setMasksToBounds:YES];
NextView.view.frame=CGRectMake(100,120, 1, 1);
[self.view addSubview:NextView.view];
[self setRoundedView:NextView.view toDiameter:5];
[UIView animateWithDuration:5.0 animations:^{
====== [self setRoundedView:NextView.view toDiameter:480]; ======
}
completion:^(BOOL finished)
{
NextView.view.frame=CGRectMake(0, 0, 320, 480);
NextView.view.layer.cornerRadius = 0;
}
];
}
-(void)setRoundedView:(UIView *)roundedView toDiameter:(float)newSize;
{
CGPoint saveCenter =roundedView.center;
CGRect newFrame = CGRectMake(roundedView.frame.origin.x, roundedView.frame.origin.y, newSize, newSize);
roundedView.frame = newFrame;
//roundedView.layer.cornerRadius = newSize / 2.0;
roundedView.layer.cornerRadius = roundedView.frame.size.width / 2.0;
roundedView.center = saveCenter;
roundedView.clipsToBounds = YES;
}
But it is having some issues
the view gets complete circle shape at the end of the animated transition, initially its not having rounded corners perfectly

Custom Map Callout Animation

I have to create custom MKMapView annotations, which look almost the same as the ones iOS provides, but have different color (white) and slightly different shape.
My first attempt was to prevent opening the original black UIAnnotationViews and in AnnotationView's setSelected:animated: added a new subview to the same UIAnnotationView. I have animated this view after adding it to the superview. This had only one problem: my custom button in the annotation was not clickable.
After this I've found this excellent example:
https://github.com/tochi/custom-callout-sample-for-iOS
This adds a new CalloutAnnotation to the map when the user clicks on a PinAnnotation. PinAnnotations have no AnnotationViews, only CalloutAnnotations do.
This works fine, except that I can't figure out how to do the iOS-like initial zooming animation on the annotation views when they are created.
The animation I want to use is the following (found here on SO and worked fine with my first attempt):
- (void)animateCalloutAppearance:(CalloutAnnotationView *)annotaitonView
{
self.endFrame = annotaitonView.frame;
CGFloat scale = 0.001f;
annotaitonView.transform = CGAffineTransformMake(scale, 0.0f, 0.0f, scale, [self xTransformForScale:scale andAnnotation:annotaitonView], [self yTransformForScale:scale]);
[UIView animateWithDuration:0.075f delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
CGFloat scale = 1.2f;
annotaitonView.transform = CGAffineTransformMake(scale, 0.0f, 0.0f, scale, [self xTransformForScale:scale andAnnotation:annotaitonView], [self yTransformForScale:scale]);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
CGFloat scale = 0.90;
annotaitonView.transform = CGAffineTransformMake(scale, 0.0f, 0.0f, scale, [self xTransformForScale:scale andAnnotation:annotaitonView], [self yTransformForScale:scale]);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.075 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
CGFloat scale = 1.0;
annotaitonView.transform = CGAffineTransformMake(scale, 0.0f, 0.0f, scale, [self xTransformForScale:scale andAnnotation:annotaitonView], [self yTransformForScale:scale]);
} completion:nil];
}];
}];
}
#pragma mark - The helper methods
- (CGFloat)relativeParentXPosition:(CalloutAnnotationView *)annotationView {
return annotationView.bounds.size.width / 2.0f;
}
- (CGFloat)xTransformForScale:(CGFloat)scale andAnnotation:(CalloutAnnotationView *)annotationView {
CGFloat xDistanceFromCenterToParent = self.endFrame.size.width / 2.0f - [self relativeParentXPosition:annotationView];
CGFloat transformX = (xDistanceFromCenterToParent * scale) - xDistanceFromCenterToParent;
return transformX;
}
- (CGFloat)yTransformForScale:(CGFloat)scale {
CGFloat yDistanceFromCenterToParent = self.endFrame.size.height / 2.0f;
CGFloat transformY = yDistanceFromCenterToParent - yDistanceFromCenterToParent * scale;
return transformY;
}
I've found the answer on this blog: http://blog.asynchrony.com/2010/09/building-custom-map-annotation-callouts-part-1/
The tricky part is that you have to do the animation in the CalloutAnnotationView's didMoveToSuperView :)
- (void)didMoveToSuperview
{
[self animateCalloutAppearance:self];
}
I hope it helps somebody else :)

How to zoom while keeping the previous rotation transform as it is?

I have an imageView, to which, I have added UIPinchGestureRecognizer and UIRotationGestureRecognizer.
in pinch gesture, I transform and scale the View and in rotation gesture I apply rotation transform to it.
The problem is when I rotate the imageView and then start zooming. Zooming always begins from the normal state.
So What i want is when I rotate it to say 30 degree clockwise and then zoom it. it should zoom while remaining that 30 degree on the clockwise direction.
Here is the code:
- (void)viewDidLoad{
[super viewDidLoad];
//setting up the image view
mTotalRotation = 0.0;
self.imageView.image = self.photo;
self.imageView.userInteractionEnabled = YES;
UIRotationGestureRecognizer *twoFingersRotate =
[[[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(twoFingersRotate:)] autorelease];
[self.imageView addGestureRecognizer:twoFingersRotate];
UIPinchGestureRecognizer *pinchGesture = [[[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(pinchZoom:)] autorelease];
[self.imageView addGestureRecognizer:pinchGesture];
// Do any additional setup after loading the view from its nib.
}
// Rotation gesture handler
- (void)twoFingersRotate:(UIRotationGestureRecognizer *)recognizer
{
if ([recognizer state] == UIGestureRecognizerStateEnded) {
mTotalRotation += recognizer.rotation;
return;
}
self.imageView.transform = CGAffineTransformMakeRotation(mTotalRotation + recognizer.rotation);
}
// Pinch Gesture
-(void)pinchZoom:(UIPinchGestureRecognizer*)recognizer{
self.imageView.transform = CGAffineTransformMakeScale(recognizer.scale, recognizer.scale) ;
}
Change the line:
self.imageView.transform = CGAffineTransformMakeRotation(mTotalRotation + recognizer.rotation);
with:
self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, recognizer.rotation);
And the line:
self.imageView.transform = CGAffineTransformMakeScale(recognizer.scale, recognizer.scale);
with:
self.imageView.transform = CGAffineTransformScale(self.imageView.transform, recognizer.scale, recognizer.scale);
Edit
To limit the scale, you can do the following:
CGAffineTransform transform = self.imageView.transform;
float newScale = recognizer.scale * sqrt(transform.a*transform.a + transform.c*transform.c);
if (newScale > scaleLimit) {
self.imageView.transform = CGAffineTransformScale(transform, recognizer.scale, recognizer.scale);
}
Maybe you could have your scale and rotate view as a subview of a view that you zoom?

How to zoomIn ZoomOut inside UIView

I have an ImageView which is add to UIView. Now i need to zoomIn ZoomOut the Image which is added to imageView which again add subview UIView.
now to get zooming effect to the image.
I have try not lucky today.
You will want to use a scroll view. Add a scrollview to your nib (or do it programmatically. Then load an UIImage, add it to an UIImageView, then add the UIImageView to the scroll view :
//inside you viewDidLoad
UIImage * image = //set image here
previewImageView = [[UIImageView alloc] initWithImage:image];
scrollView.contentSize = CGSizeMake(320, 480);
[scrollView addSubview:previewImageView];
scrollView.minimumZoomScale = 0.4;
scrollView.maximumZoomScale = 4.0;
scrollView.delegate = self;
[scrollView setZoomScale:scrollView.minimumZoomScale];
Then add your gesture methods:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollview {
return self.previewImageView;
}
- (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center {
CGRect zoomRect;
zoomRect.size.height = [self.scrollView frame].size.height / scale;
zoomRect.size.width = [self.scrollView frame].size.width / scale;
zoomRect.origin.x = center.x - (zoomRect.size.width / 2.0);
zoomRect.origin.y = center.y - (zoomRect.size.height / 2.0);
return zoomRect;
}
- (void)zoomAction:(UIGestureRecognizer *)gestureRecognizer {
NSLog(#"Hit the gestureRecognizer");
float newScale = [self.scrollView zoomScale] * 2;
CGRect zoomRect = [self zoomRectForScale:newScale withCenter:[gestureRecognizer locationInView:gestureRecognizer.view]];
[self.scrollView zoomToRect:zoomRect animated:YES];
}
EDIT - I had my height and width mixed in my CGSizeMake
Try to add the imageview in a web view or on a scroll view then the zooming will be easy.
I would maybe use a scrollview:
http://idevzilla.com/2010/10/04/uiscrollview-and-zoom/
http://forums.pragprog.com/forums/83/topics/1488
Try this,
UITouch *first = twoTouches objectAtIndex:0;
UITouch *second = twoTouches objectAtIndex:1;
CGPoint firstPoint = first locationInView:self;
CGPoint secondPoint = second locationInView:self;
CGFloat initialDistance = distanceBetweenPoints(firstPoint, secondPoint);
and put self.multipleTouchEnabled = YES;

(iphone) uiScrollView sensitivity?

I have 2 questions on uiscrollview.
First question:
I have imageviews on uiscrollview(at bottom of screen) and let users to drag the image view out of uiscroll view.
I place fingers on top of the imageView to drag it out but uiscrollview takes the touch and scrolls.
I guess I need to let the imageView gets the touch event first (if the touch event occurred on imageView)
If finger direction is almost horizontal, give the touch event to scrollview so that it can scroll, otherwise(finger direction is not horizontal) let the user drag the imageView.
(I have scrollView.directionalLockEnabled = YES)
I wonder how this can be done?
Second Question:
User can send the imageView back to uiScrollView by double tapping the imageView.
When I do that, the scrollview is not scrollable until I drag out any imageViews in the scrollView.
Don't know what's causing this problem..
Below is my code to send the imageView back to uiscrollview.
aDragView.userInteractionEnabled = NO;
CGPoint contentOffset = self.puzzlePieceScrollView.contentOffset;
float x = contentOffset.x;
int addIndex = x / widthWithPadding;
aDragView.scrollContainerIndex = addIndex;
CGRect newFrame;
int puzzlePieceCount = 0;
[UIView beginAnimations:#"addDragView" context: [aDragView retain]];
for(UIView* subview in [self.puzzlePieceScrollView subviews])
{
if([subview isKindOfClass: [DragView class]])
{
DragView* dragView = (DragView*)subview;
if(dragView.scrollContainerIndex >= addIndex)
{
dragView.scrollContainerIndex += 1;
newFrame = dragView.frame;
newFrame.origin.x += widthWithPadding;
dragView.frame = newFrame;
}
++puzzlePieceCount;
}
}
newFrame = aDragView.frame;
newFrame.origin.x = [self getOriginXForScrollIndex: addIndex];
newFrame.origin.y = self.frame.origin.y + upperPadding;
aDragView.frame = newFrame;
[UIView setAnimationDuration:0.3];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationFinished:finished:context:)];
[UIView commitAnimations];
float newWidth = std::max((puzzlePieceCount+1) * widthWithPadding, [[UIScreen mainScreen] bounds].size.width );
CGSize newContentSize = self.puzzlePieceScrollView.contentSize;
newContentSize.width = newWidth;
self.puzzlePieceScrollView.contentSize = newContentSize;
- (void) animationFinished:(NSString*)animationId finished:(BOOL)finished context:(void*)context
{
if ([animationId isEqualToString:#"addDragView"])
{
DragView* aDragView = (DragView*)context;
// add to puzzlePieceScrollView
CGPoint p;
p = CGPointMake(aDragView.bounds.size.width * 0.5, aDragView.bounds.size.height * 0.5);
CGPoint newCenter = [aDragView convertPoint: p toView: self.puzzlePieceScrollView];
aDragView.center = newCenter;
[self.puzzlePieceScrollView addSubview: aDragView];
aDragView.userInteractionEnabled = YES;
[aDragView release];
}
self.puzzlePieceScrollView.userInteractionEnabled = YES;
}