Hello everyone I am trying to zoom in a out a uiimageview using animation as i am scrolling down a uiscrollview. Please note its not about making a uiimageview zoom in and out in a uiscrollview.
I am able to detect how much i am scrolling down :
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
}
I need to zoom out the image depending( the scroll factor) how much i am scrolling down. Any idea how this can be done?
I suggesting changing the transform property of the image you are trying to scale
- (void)scrollViewDidScroll:(UIScrollView*)scrollView{
// this is just a demo method on how to compute the scale factor based on the current contentOffset
float scale = 1.0f + fabsf(scrollView.contentOffset.y) / scrollView.frame.size.height;
//Cap the scaling between zero and 1
scale = MAX(0.0f, scale);
// Set the scale to the imageView
imageView.transform = CGAffineTransformMakeScale(scale, scale);
}
If you also need the scrollview to go to the top one the imageview has been zoomed out, then you will need to adjust the frame of the scrollview and imageview.
Maybe you can tell us more about your desired effect.
Here's a Swift version of Andrei's answer. I am also exiting early if the user is swiping up (scrolling down), because I only wanted to show the zoom effect when they were pulling down on the table view.
// Exit early if swiping up (scrolling down)
if scrollView.contentOffset.y > 0 { return }
// this is just a demo method on how to compute the scale factor based on the current contentOffset
var scale = 1.0 + fabs(scrollView.contentOffset.y) / scrollView.frame.size.height
//Cap the scaling between zero and 1
scale = max(0.0, scale)
// Set the scale to the imageView
self.imageView.transform = CGAffineTransformMakeScale(scale, scale)
Related
I want to create a scrollView that works exactly like you pan/zoom an image in the Photo app:
-A landscape image is aspect fit on the portrait screen,
-You can zoom into the image,
-If you rotate the device zoomed (landscape), the image remains in the middle,
-And when you zoom back, the image is still aspect fit in the new landscape screen (streched full screen).
So I need aspect fit, and zooming features at once.
I have implemented a solution, where I layout the scrollView's content "by hand" in layouSubviews to have the aspect fit, but that disturbs zooming behaviour.
Is there a neat UIKit way to handle this?
Or I have to create my own implementation here?
You need to enable zooming (and set the min and max zoom scale) and then implement scrollViewDidZoom. Here's some sample code to get you started that handles centering the image. You can tweak it to do the other parts:
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
CGSize boundsSize = scrollView.bounds.size;
CGRect frameToCenter = imageView.frame;
// center horizontally
if (frameToCenter.size.width < boundsSize.width)
frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2;
else
frameToCenter.origin.x = 0;
// center vertically
if (frameToCenter.size.height < boundsSize.height)
frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2;
else
frameToCenter.origin.y = 0;
imageView.frame = frameToCenter;
}
Note: this code assumes you keep a reference to the UIImageView (imageView in this example) in your UIScrollView.
I'm using a pinch gesture to let users increase/decrease the size of an image. I temporarily manipulate that CGAffineTransform to let the user play with the scale, then just undo it and set the frame size so that image scales with quality (CGAffineTransformScale does nothing for image quality and will only stretch out the image).
-(void)handlePinch:(UIPinchGestureRecognizer *)recognizer{
if(recognizer.state == UIGestureRecognizerStateBegan){
self.alpha = 0.7;
startingTransform = self.transform;
}
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
recognizer.scale = 1;
if (recognizer.state == UIGestureRecognizerStateEnded){
self.alpha = 1.0;
CGRect endFrame = self.frame;
self.transform = startingTransform;
self.frame = endFrame;
//self.transform = CGAffineTransformRotate(self.transform, acos(startingTransform.c)); //reapply rotation?
}
}
The issue: The problem with my code is that it does not account for rotation. Some of my images start out rotated (with CGAffineTransformRotate being previously applied to them) and what happens when the pinch gesture finishes is that the imageview warps out into really thin rectangles, devoid of any rotation or scale they were before.
I thought I could carry over the original rotation and apply it after the frame was set, but I don't think that works. Any help is appreciated. Thanks
Note that the UIView documentation says that as soon as a UIViews transform is not its identity transform, the frame property becomes undefined and should not be used, so do not rely on CGRect endFrame = ... to work.
Otherwise, if you want the quality to change, just set the transform to identity, scale the frame and then reapply the transform to keep the rotation. That should work fine.
add imageview as subview of scrollview, and then specify the delegate of scrolview which asks for viewtobezoomed as the imageview. it will easi.y solve it.
http://developer.apple.com/library/ios/documentation/uikit/reference/uiscrollviewdelegate_protocol/Reference/UIScrollViewDelegate.html#//apple_ref/occ/intfm/UIScrollViewDelegate/viewForZoomingInScrollView:
I have a UIView that I rotate with this code:
helpView.transform = CGAffineTransformMakeRotation(degreesToRadians( rotationAngle ));
Where degreeToRadians just is a macro to convert from degrees to radians.
This works fine as long as the view is visible, eg alpha = 1. When I hide it (alpha = 0, which I animate) it does not rotate any more. I guess this is a smart way for the devices to "save" on drawing time, but is there any way I can force it to be drawn even when alpha is 0? Otherwise I will have to rotate it before I show it again.
Any good ideas?
Thanks
Edit: This is the code I use to show/hide the view.
-(void)showHelp
{
bool helpAlpha = !helpView.alpha;
CGFloat newScale;
if (helpView.alpha) {
newScale = kHelpSmall;
helpView.transform = CGAffineTransformMakeScale(kHelpBig, kHelpBig);
} else {
newScale = kHelpBig;
helpView.transform = CGAffineTransformMakeScale(kHelpSmall, kHelpSmall);
}
[UIView animateWithDuration:(kAnimationTimeShort / 2) animations:^(void) {
[helpView setAlpha:helpAlpha];
helpView.transform = CGAffineTransformMakeScale(newScale, newScale);
}];
}
As you see I also scale it for a nicer effect. Works perfect when visible, does not rotate when alpha = 0. Rotation is done in another method, where I would prefer to keep it as I also rotate some other views there.
You are resetting the transform every time you use CGAffineTransformMake*. If you do this, you will get either a rotated transform or a scaled one. I am assuming the scaled one is after the rotated one and hence you aren't able to see the view rotated. If you need both the effects to remain, you will have to use CGAffineTransformRotate. So a scale and rotate will be
helpView.transform = CGAffineTransformMakeScale(kHelpSmall, kHelpSmall);
helpView.transform = CGAffineTransformRotate(helpView.transform, degreesToRadians(rotationAngle));
The order might vary.
I have a UIScrollView and added a UIView inside it, when I zoom it, is it possible to get the CGRect of the zoomed frame in relation to the original frame?
E.g. i have 800x600 frame, then i zoomed to {{50, 60}, {100, 100}} is it possible to programmatically get the zoomed frame?
I usually use the following method (added to the custom UIScrollView category):
- (CGRect) visibleRect{
CGRect visibleRect;
visibleRect.origin = self.contentOffset;
visibleRect.size = self.bounds.size;
visibleRect.origin.x /= self.zoomScale;
visibleRect.origin.y /= self.zoomScale;
visibleRect.size.width /= self.zoomScale;
visibleRect.size.height /= self.zoomScale;
return visibleRect;
}
The problem with #Vladimir solution is that it displays visibleRect wrong if viewForZoom is smaller than ScrollView bounds. So I came up with this solution.
- (CGRect) zoomedFrame{
CGRect zoomedFrame;
zoomedFrame.origin = self.contentOffset;
zoomedFrame.origin.x -= zoomingView.frame.origin.x;
zoomedFrame.origin.y -= zoomingView.frame.origin.y;
zoomedFrame.size = self.contentSize;
return zoomedFrame;
}
zoomingView is a view that returns viewForZoomingInScrollView: method.
bounds are bounds of scrollView.
So there are two cases:
When the zoomingView is smaller than bounds, contentOffset reflect not the top-left corner of content view, but some strange shift of content view relative to the center of bounds. And zoomingView.frame.origin has normal values as if zoomingView were in the center of bounds. (this happens if you try to shrink the zoomingView more than minimulScale)
When the zoomingView is bigget than bounds, zoomingView.frame.origin has strange values like this:
{-6.15367e-06, 3.98168e-06}
And contentOffset shows what it should.
So all that compensate each other as I showed in my code.
I'm having difficulties getting a tiled UIScrollView to zoom in and out correctly with pinch zooming. The issue is that when a pinch-zoom occurs, the resulting view is usually not centered in the same region.
Details: The app starts with a tiled image that is 500x500. If a user zooms in, it will snap to 1000x1000 and the tiles will redraw. For all the zoom affects, etc. I am just letting the UIScrollView do it's thing. When scrollViewDidEndZooming:withView:atScale: is called, I redraw the tiles (like you can see in many examples and other questions here).
I think that I've drilled the problem down to calculating the center of the view correctly when I get to scrollViewDidEndZooming:withView:atScale: (I can center on a known point fine after I redraw).
What I'm currently using:
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
// as an example, create the "target" content size
CGSize newZoomSize = CGSizeMake(1000, 1000);
// get the center point
CGPoint center = [scrollView contentOffset];
center.x += [scrollView frame].width / 2;
center.y += [scrollView frame].height / 2;
// since pinch zoom changes the contentSize of the scroll view, translate this point to
// the "target" size (from the current size)
center = [self translatePoint:center currentSize:[scrollView contentSize] newSize:newZoomSize];
// redraw...
}
/*
Translate the point from one size to another
*/
- (CGPoint)translatePoint:(CGPoint)origin currentSize:(CGSize)currentSize newSize:(CGSize)newSize {
// shortcut if they are equal
if(currentSize.width == newSize.width && currentSize.height == newSize.height){ return origin; }
// translate
origin.x = newSize.width * (origin.x / currentSize.width);
origin.y = newSize.height * (origin.y / currentSize.height);
return origin;
}
Does this seem correct? Is there a better way? Thanks!
The way I have solved this so far is to store the initial center point of the view when the zoom starts. I initially saving this value when the scrollViewDidScroll method is called (and the scroll view is zooming). When scrollViewDidEndZooming:withView:atScale: is called, I use that center point (and reset the saved value).
The center of the scrollview can be found by adding it's center property, and it's contentOffset property.
aView.center = CGPointMake(
self.scrollView.center.x + self.scrollView.contentOffset.x,
self.scrollView.center.y + self.scrollView.contentOffset.y);