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.
Related
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)
in the AFOpenFlow library are two lines that says that the coverFlow is in the middle of your
iPhone - Screen:
CGPoint newPosition;
newPosition.x = halfScreenWidth + aCover.horizontalPosition;
newPosition.y = halfScreenHeight + aCover.verticalPosition;
But how could i change this line, that the coverFlow at the high range of the screen?
Marco
I don't know AFOpenFlow but this math here set the middle of the View related to the center of the screen. If you want the view to only take half of the size of your screen, you also need to change its height. it would be like :
[Updated Code]
CGSize newSize = CGSizeMake(screenWidth, swcreenHeight/2);
CGPoint newCenterPosition = CGPointMake(halfScreenWidth+aCover.horizontalPosition, halfScreenHeight/2+aCover.verticalPosition)
aCover.bounds = CGRectMake(newCenterPosition.x, newCenterPosition.y, newSize.width, newSize.height);
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);
I am loading in images with varying sizes and putting them in UIScrollViews, all the images are larger than the UIScrollView.
The user can scroll and zoom as they please, but initially I would like for the image
to be centered and scaled so the largest side of the image aligns with the edge of the scrollView, i.e. if the picture is in landscape I would like to size and scale it so that the left and right side goes all the way to the edge of the UIScrollVIew and vice versa
I found a formula in a utility function in the Programming guide but it does not quite fit my needs.
My approach is to use:
CGrect initialPos = ?
[self.scrollView zoomToRect:initialPos animated:YES];
I know the size of my scrollView and the size of my image, what I need to figure out is the scale and CGRect to apply to the scrollView to center and size my image.
Hope someone can help out:) Thanks
Edit: previous version was sizing the image to the view rather than the other way around. I think this should correct that:
double imgRatio = imageSize.width / imageSize.height;
double viewRatio = viewSize.width / viewSize.height;
if ( imgRatio >= viewRatio )
{
initialPos.size.width = imageSize.width;
initialPos.size.height = imageSize.width / viewRatio;
initialPos.origin.x = 0;
initialPos.origin.y = (imageSize.height - initialPos.size.height) / 2;
}
else
{
initialPos.size.height = imageSize.height;
initialPos.size.width = imageSize.height * viewRatio;
initialPos.origin.y = 0;
initialPos.origin.x = (imageSize.width - initialPos.size.width) / 2;
}
When somebody does a wipe gesture to scroll the content from left to right, I would like to have a background image scrolling into the same direction, but at a different speed. Much like what these classic games did do 20 years ago (remember that, anybody????)
I accomplished this by using two UIScrollView instances. The first is where the actual content is displayed, and the second (which is behind the first in z-order) is where I have my slower-moving background. From there the top UIScrollView has a delegate attached to it that gets notified when the contentOffset changes. That delegate, in turn, programatically sets the contentOffset of the background scroller, multiplied against a constant to slow the scroll down relative to the foreground. So, for instance, you might have something like:
// Defined as part of the delegate for the foreground UIScrollView
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
UIScrollView* scroll_view(static_cast<UIScrollView*>(bkg_scroller_m.view));
CGPoint offset(scrollView.contentOffset);
offset.x = offset.x / 3;
offset.y = offset.y / 3;
// Scroll the background scroll view by some smaller offset
scroll_view.contentOffset = offset;
}
You can easily do this by implementing scroll view did scroll with a UIImageView under it...
You'll end up with something like this... with the backgroundImageView being a UIImageView added to the view before the subview... you can layer as much image views as you want without performance issues
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
float factor = scrollView.contentOffset.x / (scrollView.contentSize.width - 320);
if (factor < 0) factor = 0;
if (factor > 1) factor = 1;
CGRect frame = backgroundImageView.frame;
frame.origin.x = factor * (320 - backgroundImageView.frame.size.width);
backgroundImageView.frame = frame;
}
You can do it with CoreAnimation. You'll want to hook into the scrollViewDidEndDragging:willDecelerate: and scrollViewWillBeginDecelerating: UIScrollViewDelegate methods. Then begin an Animation on your image by changing the center position. See this SO article for more on animations.
For example you have multiple scrollviews, want them scroll difference speed. here is the modification code base on Salamatizm answer:
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
float factor = scrollView.contentOffset.x / (scrollView.contentSize.width - screenSize.width);
if (factor < 0) factor = 0;
if (factor > 1) factor = 1;
CGSize parralaxSize = self.parralaxBackgroundView.contentSize;
CGPoint parallaxOffset = CGPointMake(-(factor * (screenSize.width - parralaxSize.width)), 0);
[self.parralaxBackgroundView setContentOffset:parallaxOffset animated:NO];