Uiscrollview with Centered Uiimageview, Like Photos App

Is it Possible to Center Content in a UIScrollView Like Apple's Photos App?

Here is the best solution I could come up with for this problem. The trick is to constantly readjust the imageView's frame. I find this works much better than constantly adjusting the contentInsets or contentOffSets. I had to add a bit of extra code to accommodate both portrait and landscape images.

If anyone can come up with a better way to do it, I would love to hear it.

Here's the code:

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

CGSize screenSize = [[self view] bounds].size;

if (myScrollView.zoomScale <= initialZoom +0.01) //This resolves a problem with the code not working correctly when zooming all the way out.
{
imageView.frame = [[self view] bounds];
[myScrollView setZoomScale:myScrollView.zoomScale +0.01];
}

if (myScrollView.zoomScale > initialZoom)
{
if (CGImageGetWidth(temporaryImage.CGImage) > CGImageGetHeight(temporaryImage.CGImage)) //If the image is wider than tall, do the following...
{
if (screenSize.height >= CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale]) //If the height of the screen is greater than the zoomed height of the image do the following...
{
imageView.frame = CGRectMake(0, 0, 320*(myScrollView.zoomScale), 368);
}
if (screenSize.height < CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale]) //If the height of the screen is less than the zoomed height of the image do the following...
{
imageView.frame = CGRectMake(0, 0, 320*(myScrollView.zoomScale), CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale]);
}
}
if (CGImageGetWidth(temporaryImage.CGImage) < CGImageGetHeight(temporaryImage.CGImage)) //If the image is taller than wide, do the following...
{
CGFloat portraitHeight;
if (CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale] < 368)
{ portraitHeight = 368;}
else {portraitHeight = CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale];}

if (screenSize.width >= CGImageGetWidth(temporaryImage.CGImage) * [myScrollView zoomScale]) //If the width of the screen is greater than the zoomed width of the image do the following...
{
imageView.frame = CGRectMake(0, 0, 320, portraitHeight);
}
if (screenSize.width < CGImageGetWidth (temporaryImage.CGImage) * [myScrollView zoomScale]) //If the width of the screen is less than the zoomed width of the image do the following...
{
imageView.frame = CGRectMake(0, 0, CGImageGetWidth(temporaryImage.CGImage) * [myScrollView zoomScale], portraitHeight);
}
}
[myScrollView setZoomScale:myScrollView.zoomScale -0.01];
}

Here is how I calculate the the min zoom scale in the viewDidLoad method.

CGSize photoSize = [temporaryImage size];
CGSize screenSize = [[self view] bounds].size;

CGFloat widthRatio = screenSize.width / photoSize.width;
CGFloat heightRatio = screenSize.height / photoSize.height;
initialZoom = (widthRatio > heightRatio) ? heightRatio : widthRatio;
[myScrollView setMinimumZoomScale:initialZoom];
[myScrollView setZoomScale:initialZoom];
[myScrollView setMaximumZoomScale:3.0];

Center content of UIScrollView when smaller

Currently I'm subclassing UIScrollView and overriding setContentOffset: to adjust the offset based on contentSize. It works both with pinch and programatic zooming.

@implementation HPCenteringScrollView

- (void)setContentOffset:(CGPoint)contentOffset
{
const CGSize contentSize = self.contentSize;
const CGSize scrollViewSize = self.bounds.size;

if (contentSize.width < scrollViewSize.width)
{
contentOffset.x = -(scrollViewSize.width - contentSize.width) / 2.0;
}

if (contentSize.height < scrollViewSize.height)
{
contentOffset.y = -(scrollViewSize.height - contentSize.height) / 2.0;
}

[super setContentOffset:contentOffset];
}

@end

In addition to being short and sweet, this code produces a much smoother zoom than @Erdemus solution. You can see it in action in the RMGallery demo.

swift-Can't center UIImageView in UIScrollView with AutoLayout

I experienced the same problem. I found a perfect solution for me, check out this github project: https://github.com/evgenyneu/ios-imagescroll-swift

How it essentially works is as follows: it adjusts your constraints as to center the image if it is zoomed out far enough and to enable it to zoom and scroll after the width or height exceeds the respective width or height of your container.

Found it thanks to this thread: Zooming UIImageView inside UIScrollView with autolayout (obj-c and swift solution available)!

//  ViewController.swift
// image-scroll-swift
//
// Created by Evgenii Neumerzhitckii on 4/10/2014.
// Copyright (c) 2014 Evgenii Neumerzhitckii. All rights reserved.
//

import UIKit

let imageScrollLargeImageName = "wallabi.jpg"

class ViewController: UIViewController, UIScrollViewDelegate {

@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var imageView: UIImageView!

@IBOutlet weak var imageConstraintTop: NSLayoutConstraint!
@IBOutlet weak var imageConstraintRight: NSLayoutConstraint!
@IBOutlet weak var imageConstraintLeft: NSLayoutConstraint!
@IBOutlet weak var imageConstraintBottom: NSLayoutConstraint!

var lastZoomScale: CGFloat = -1

override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)

imageView.image = UIImage(named: imageScrollLargeImageName)
scrollView.delegate = self
updateZoom()
}

// Update zoom scale and constraints
// It will also animate because willAnimateRotationToInterfaceOrientation
// is called from within an animation block
//
// DEPRECATION NOTICE: This method is said to be deprecated in iOS 8.0. But it still works.
override func willAnimateRotationToInterfaceOrientation(
toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {

super.willAnimateRotationToInterfaceOrientation(toInterfaceOrientation, duration: duration)
updateZoom()
}

func updateConstraints() {
if let image = imageView.image {
let imageWidth = image.size.width
let imageHeight = image.size.height

let viewWidth = view.bounds.size.width
let viewHeight = view.bounds.size.height

// center image if it is smaller than screen
var hPadding = (viewWidth - scrollView.zoomScale * imageWidth) / 2
if hPadding < 0 { hPadding = 0 }

var vPadding = (viewHeight - scrollView.zoomScale * imageHeight) / 2
if vPadding < 0 { vPadding = 0 }

imageConstraintLeft.constant = hPadding
imageConstraintRight.constant = hPadding

imageConstraintTop.constant = vPadding
imageConstraintBottom.constant = vPadding

// Makes zoom out animation smooth and starting from the right point not from (0, 0)
view.layoutIfNeeded()
}
}

// Zoom to show as much image as possible unless image is smaller than screen
private func updateZoom() {
if let image = imageView.image {
var minZoom = min(view.bounds.size.width / image.size.width,
view.bounds.size.height / image.size.height)

if minZoom > 1 { minZoom = 1 }

scrollView.minimumZoomScale = minZoom

// Force scrollViewDidZoom fire if zoom did not change
if minZoom == lastZoomScale { minZoom += 0.000001 }

scrollView.zoomScale = minZoom
lastZoomScale = minZoom
}
}

// UIScrollViewDelegate
// -----------------------

func scrollViewDidZoom(scrollView: UIScrollView) {
updateConstraints()
}

func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return imageView
}

}

Make sure your storyboard contains a viewcontroller. In the 'View' place a 'Scroll View' and in that 'Scroll View' place an 'Image View'.

Establish top/bottom/leading/trailing constraints of 0 (or whatever value you prefer) between the 'Scroll View' and the 'View'. Then establish the same constraints between the image and the scroll view and connect them to the @IBOutlets.

This should work fine as the code changes the constraints as you zoom.

imageView centered inside scrollView

It will be good to use auto layout as mentioned by @Douglas. However, if you prefer the traditional way, you can also make it work.

I'll first give you the answer and then explain it to you. You should first delete the image view from the storyboard ( I'll explain it later), and then add the viewWillAppear method.

    - (void)viewDidLoad
{
[super viewDidLoad];
// 1. Add the image view programatically
UIImageView * imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"portrait.jpg"]];
[_scrollView addSubview:imageView];
_imageView = imageView;

}

- (void)viewWillAppear:(BOOL)animated
{
// 2. calculate the size of the image view
CGFloat scrollViewWidth = CGRectGetWidth(_scrollView.frame);
CGFloat scrollViewHeight = CGRectGetHeight(_scrollView.frame);
CGFloat imageViewWidth = CGRectGetWidth(_imageView.frame);
CGFloat imageViewHeight = CGRectGetHeight(_imageView.frame);
CGFloat widthRatio = scrollViewWidth / imageViewWidth;
CGFloat heightRation = scrollViewHeight / imageViewHeight;
CGFloat ratio = MIN(widthRatio, heightRation);
CGRect newImageFrame = CGRectMake(0, 0, imageViewWidth * ratio, imageViewHeight * ratio);
_imageView.frame = newImageFrame;

// 3. find the position of the imageView.
CGFloat scrollViewCenterX = CGRectGetMidX(_scrollView.bounds);
CGFloat scrollViewCenterY = CGRectGetMidY(_scrollView.bounds) + _scrollView.contentInset.top / 2 ;
_imageView.center = CGPointMake(scrollViewCenterX, scrollViewCenterY);
}

Here is the explanation:

  1. You should not put the imageView in the storyboard, otherwise the frame of the imageView will be fixed by the storyboard, and will not change with the size of the image. Even if you choose UIViewContentModeScaleAspectFill, the frame of the imageView is still not changed. It just add some white space around the image.

  2. Now the imageView has the same size as your image. If you want it to be fully displayed, you need to calculate the frame yourself.

  3. Pay attention to the _scrollView.contentInset.top / 2, this is why you need to put the codes in viewWillAppear instead of viewDidLoad. The _scrollView.contentInset.top is the height of the navigation bar and is calculated automatically for you before willViewAppear.

You put your image view in a scrollView, I guess you want to zoom in and out. If this is true, add self.imageView = imageView; and the bottom of viewDidLoad. Set the delegate of _scrollView to self and add the following method:

    - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return _imageView;
}

How do I center a UIImageView within a full-screen UIScrollView?

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 for PhotoScroller.

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;
}

Align UIImageView to bottom of UIScrollView

If you are using autoresizing then you need to set left, right, bottom and flexibleWidth masks to both UIImageView and UIScrollView. Take a look at the images attached here.

If your UIScrollView covers the whole screen then set all the resizing masks for it but for UIImageView set only the ones stated above.

Hope it helps.

Sample Image
Sample Image

Output in simulator:

Portrait

Landscape



Related Topics



Leave a reply



Submit