Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIScrollView with centered UIImageView, like Photos app

I would like to have scroll view with an image content view. The image is actually map which is much bigger than the screen. The map should be initially in the center of the scroll view, like photos in Photos app when you turn iPhone to landscape orientation.

alt text

I did not manage to have the map in the center with correct zooming and scrolling at the same time. Provided that the map image starts from the top of the screen (in portrait orientation), the code looks something like:

- (void)loadView {
    mapView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"map.jpg"]];
    CGFloat mapHeight = MAP_HEIGHT * SCREEN_WIDTH / MAP_WIDTH;
    mapView.frame = CGRectMake(0, 0, SCREEN_WIDTH, mapHeight);
    scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
    scrollView.delegate = self;
    scrollView.contentSize = mapView.frame.size;
    scrollView.maximumZoomScale = MAP_WIDTH / SCREEN_WIDTH;
    scrollView.minimumZoomScale = 1;
    [scrollView addSubview:mapView];
    self.view = scrollView;
}

When I move the image frame to the center, the image grows only from the top of its frame down. I tried to play around with mapView transform, with dynamically changing frame of the imageView. Nothing works for me so far.

like image 615
Martin Ludvik Avatar asked Mar 12 '09 11:03

Martin Ludvik


People also ask

How to make UIView scrollable?

You cannot make a UIView scrollable. That's what UIScrollView is for. However if you are using storyboards you can try to add constraints to the view so when you rotate the device the content remains inside the viewable area.

What is Ui scroll view?

UIScrollView is the superclass of several UIKit classes, including UITableView and UITextView . A scroll view is a view with an origin that's adjustable over the content view. It clips the content to its frame, which generally (but not necessarily) coincides with that of the app's main window.


2 Answers

This code should work on most versions of iOS (and has been tested to work on 3.1 upwards).

It's based on the Apple WWDC code mentioned in Jonah's answer.

Add the below to your subclass of UIScrollView, and replace tileContainerView with the view containing your image or tiles:

- (void)layoutSubviews {     [super layoutSubviews];      // center the image as it becomes smaller than the size of the screen     CGSize boundsSize = self.bounds.size;     CGRect frameToCenter = tileContainerView.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;      tileContainerView.frame = frameToCenter; } 
like image 172
JosephH Avatar answered Sep 20 '22 09:09

JosephH


Here is what I'd consider, the solution as in it behaves exactly like apple's photo app. I had been using solutions that used:

-(void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale

to recenter but I didn't like that solution because after the zooming was done, it'd bounce then quick 'jump' into the center which was very un-sexy. Turns out if you pretty much do the exact same logic but in this delegate function:

-(void)scrollViewDidZoom:(UIScrollView *)pScrollView

it both starts off centered and when you zoom out it stays centered:

-(void)scrollViewDidZoom:(UIScrollView *)pScrollView {
CGRect innerFrame = imageView.frame;
CGRect scrollerBounds = pScrollView.bounds;

if ( ( innerFrame.size.width < scrollerBounds.size.width ) || ( innerFrame.size.height < scrollerBounds.size.height ) )
{
    CGFloat tempx = imageView.center.x - ( scrollerBounds.size.width / 2 );
    CGFloat tempy = imageView.center.y - ( scrollerBounds.size.height / 2 );
    CGPoint myScrollViewOffset = CGPointMake( tempx, tempy);

    pScrollView.contentOffset = myScrollViewOffset;

}

UIEdgeInsets anEdgeInset = { 0, 0, 0, 0};
if ( scrollerBounds.size.width > innerFrame.size.width )
{
    anEdgeInset.left = (scrollerBounds.size.width - innerFrame.size.width) / 2;
    anEdgeInset.right = -anEdgeInset.left;  // I don't know why this needs to be negative, but that's what works
}
if ( scrollerBounds.size.height > innerFrame.size.height )
{
    anEdgeInset.top = (scrollerBounds.size.height - innerFrame.size.height) / 2;
    anEdgeInset.bottom = -anEdgeInset.top;  // I don't know why this needs to be negative, but that's what works
}
pScrollView.contentInset = anEdgeInset;
}

Where 'imageView' is the UIImageView you're using.

like image 21
Shizam Avatar answered Sep 19 '22 09:09

Shizam