UIScrollView的中心内容较小时

我有一个UIImageView里面的UIScrollView我用于缩放和滚动。 如果滚动视图的图像/内容大于滚动视图,则一切正常。 但是,当图像变得小于滚动视图时,它会粘到滚动视图的左上angular。 我想保持中心,像照片应用程序。

任何关于保持UIScrollView的内容居中的想法或例子,当较小?

我正在使用iPhone 3.0。

下面的代码几乎可以工作。 如果在达到最小缩放级别后捏住图像,图像将返回到左上angular。

 - (void)loadView { [super loadView]; // set up main scroll view imageScrollView = [[UIScrollView alloc] initWithFrame:[[self view] bounds]]; [imageScrollView setBackgroundColor:[UIColor blackColor]]; [imageScrollView setDelegate:self]; [imageScrollView setBouncesZoom:YES]; [[self view] addSubview:imageScrollView]; UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"WeCanDoIt.png"]]; [imageView setTag:ZOOM_VIEW_TAG]; [imageScrollView setContentSize:[imageView frame].size]; [imageScrollView addSubview:imageView]; CGSize imageSize = imageView.image.size; [imageView release]; CGSize maxSize = imageScrollView.frame.size; CGFloat widthRatio = maxSize.width / imageSize.width; CGFloat heightRatio = maxSize.height / imageSize.height; CGFloat initialZoom = (widthRatio > heightRatio) ? heightRatio : widthRatio; [imageScrollView setMinimumZoomScale:initialZoom]; [imageScrollView setZoomScale:1]; float topInset = (maxSize.height - imageSize.height) / 2.0; float sideInset = (maxSize.width - imageSize.width) / 2.0; if (topInset < 0.0) topInset = 0.0; if (sideInset < 0.0) sideInset = 0.0; [imageScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)]; } - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { return [imageScrollView viewWithTag:ZOOM_VIEW_TAG]; } /************************************** NOTE **************************************/ /* The following delegate method works around a known bug in zoomToRect:animated: */ /* In the next release after 3.0 this workaround will no longer be necessary */ /**********************************************************************************/ - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale { [scrollView setZoomScale:scale+0.01 animated:NO]; [scrollView setZoomScale:scale animated:NO]; // END Bug workaround CGSize maxSize = imageScrollView.frame.size; CGSize viewSize = view.frame.size; float topInset = (maxSize.height - viewSize.height) / 2.0; float sideInset = (maxSize.width - viewSize.width) / 2.0; if (topInset < 0.0) topInset = 0.0; if (sideInset < 0.0) sideInset = 0.0; [imageScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)]; } 

我有非常简单的解决scheme! 所有你需要的是在放大ScrollViewDelegate时更新子视图的中心(imageview)。 如果缩放的图像比scrollview小,那么调整subview.center else center是(0,0)。

 - (void)scrollViewDidZoom:(UIScrollView *)scrollView { UIView *subView = [scrollView.subviews objectAtIndex:0]; CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0); CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0); subView.center = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX, scrollView.contentSize.height * 0.5 + offsetY); } 

好吧,过去两天我一直在为此奋斗,终于来到了一个非常可靠的(迄今为止)解决scheme,我想我应该分享它,并节省一些痛苦。 :)如果你发现这个解决scheme的问题,请大喊!

我已经基本上经历了其他人都有:searchStackOverflow,苹果开发者论坛,看着三个CodeGo.net,ScrollingMadness,ScrollTestSuite等我试图扩大UIImageView框架,玩UIScrollView的偏移量和/或插入从ViewController等等,但没有什么效果很好(其他人都已经发现了)。

在睡觉之后,我尝试了几个不同的angular度:

  1. 对UIImageView进行子类化,以便dynamic地改变它自己的大小 – 这根本不起作用。
  2. 对UIScrollView进行子类化,使其dynamic改变它自己的contentOffset – 这对我来说似乎是一个赢家。

有了这个inheritanceUIScrollView的方法,我重载了contentOffset的mutator,所以当图像的缩放比视口小时,它不会设置{0,0} – 而是设置偏移,使图像保持在视口的中心位置。 到目前为止,它似乎总是工作。 我已经检查了宽,高,微小和大的图像,并没有“的作品,但在最小变焦打破它”的问题。

我已经上传了一个示例项目给github使用这个解决scheme,你可以在这里find它: http : //github.com/nyoron/NYOBetterZoom

这个代码应该可以在大多数iOS版本上运行(并且已经过testing,可以在3.1以上的版本上运行)。

它基于photoscoller的Apple WWDC代码。

将下面的内容添加到UIScrollView的子类中,并将tileContainerViewreplace为包含图像或图块的视图:

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

@ EvelynCordner的答案是在我的应用程序中效果最好的一个。 比其他选项less得多的代码。

如果有人需要,这里是快速的版本:

 func scrollViewDidZoom(scrollView: UIScrollView) { let offsetX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0) let offsetY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0) self.scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0, 0) } 

目前我正在UIScrollView并覆盖setContentOffset:根据contentSize调整偏移量。 它适用于捏和programatic缩放。

 @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 

这个代码除了简短而又甜美之外,还比@Erdemus解决scheme产生更平滑的缩放。 您可以在RMGallery演示中看到它的行动。

对于更适合使用自动布局的滚动视图的解决scheme,请使用滚动视图的内容插页,而不是更新滚动视图子视图的框架。

 - (void)scrollViewDidZoom:(UIScrollView *)scrollView { CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0); CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0); self.scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0.f, 0.f); } 

我花了一天的时间来解决这个问题,最终实现了scrollViewDidEndZooming:withView:atScale:如下:

 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale { CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width; CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height; CGFloat viewWidth = view.frame.size.width; CGFloat viewHeight = view.frame.size.height; CGFloat x = 0; CGFloat y = 0; if(viewWidth < screenWidth) { x = screenWidth / 2; } if(viewHeight < screenHeight) { y = screenHeight / 2 ; } self.scrollView.contentInset = UIEdgeInsetsMake(y, x, y, x); } 

这确保了当图像小于屏幕时,它周围仍然有足够的空间,所以你可以把它放在你想要的地方。

(假设你的UIScrollView包含一个UIImageView来保存图像)

本质上,这是做的是检查你的图像视图的宽度/高度是否小于屏幕的宽度/高度,如果是的话,创build一个屏幕宽度/高度的一半插入(如果你想要图像走出屏幕边界)。

请注意,由于这是一个UIScrollViewDelegate方法,因此不要忘记将其添加到视图控制器的声明中,以避免出现构build问题。

苹果已经向所有iphone开发者计划的成员发布了2010年WWDC会议video。 讨论的话题之一是他们如何创build照片的应用程序! 他们逐步构build了一个类似的应用程序,并免费提供所有的代码。

它也不使用私人API。 由于不公开协议,我不能在这里放置任何代码,但是这里有一个示例代码下载的链接。 您可能需要login才能访问。

http://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?code=y&source=x&bundleID=20645

而且,这里是一个链接到iTunes WWDC页面:

http://insideapple.apple.com/redir/cbx-cgi.do?v=2&la=en&lc=&a=kGSol9sgPHP%2BtlWtLp%2BEP%2FnxnZarjWJglPBZRHd3oDbACudP51JNGS8KlsFgxZto9X%2BTsnqSbeUSWX0doe%2Fzv%2FN5XV55%2FomsyfRgFBysOnIVggO%2Fn2p%2BiweDK%2F%2FmsIXj

好的,这个解决scheme正在为我工​​作。 我有一个UIScrollView的子类与它显示的UIImageView的引用。 每当UIScrollView放大,contentSize属性就会被调整。 在setter中,我适当缩放UIImageView并调整其中心位置。

 -(void) setContentSize:(CGSize) size{ CGSize lSelfSize = self.frame.size; CGPoint mid; if(self.zoomScale >= self.minimumZoomScale){ CGSize lImageSize = cachedImageView.initialSize; float newHeight = lImageSize.height * self.zoomScale; if (newHeight < lSelfSize.height ) { newHeight = lSelfSize.height; } size.height = newHeight; float newWidth = lImageSize.width * self.zoomScale; if (newWidth < lSelfSize.width ) { newWidth = lSelfSize.width; } size.width = newWidth; mid = CGPointMake(size.width/2, size.height/2); } else { mid = CGPointMake(lSelfSize.width/2, lSelfSize.height/2); } cachedImageView.center = mid; [super setContentSize:size]; [self printLocations]; NSLog(@"zoom %f setting size %fx %f",self.zoomScale,size.width,size.height); } 

Evertime我设置UIScrollView上的图像我调整它的大小。 scrollview中的UIScrollView也是我创build的自定义类。

 -(void) resetSize{ if (!scrollView){//scroll view is view containing imageview return; } CGSize lSize = scrollView.frame.size; CGSize lSelfSize = self.image.size; float lWidth = lSize.width/lSelfSize.width; float lHeight = lSize.height/lSelfSize.height; // choose minimum scale so image width fits screen float factor = (lWidth<lHeight)?lWidth:lHeight; initialSize.height = lSelfSize.height * factor; initialSize.width = lSelfSize.width * factor; [scrollView setContentSize:lSize]; [scrollView setContentOffset:CGPointZero]; scrollView.userInteractionEnabled = YES; } 

有了这两种方法,我可以有一个观点,行为就像照片应用程序。

您可以观察UIScrollView的contentSize属性(使用键值观察或类似方法),并在contentSize更改为小于滚动视图的大小时自动调整contentInset

一个优雅的方式来居中UISCrollView的内容是这样的。

添加一个观察者到你的UIScrollViewcontentSize ,所以这个方法将在每次内容改变时被调用…

 [myScrollView addObserver:delegate forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL]; 

现在你的观察者方法:

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // Correct Object Class. UIScrollView *pointer = object; // Calculate Center. CGFloat topCorrect = ([pointer bounds].size.height - [pointer viewWithTag:100].bounds.size.height * [pointer zoomScale]) / 2.0 ; topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect ); topCorrect = topCorrect - ( pointer.frame.origin.y - imageGallery.frame.origin.y ); // Apply Correct Center. pointer.center = CGPointMake(pointer.center.x, pointer.center.y + topCorrect ); } 
  • 你应该改变[pointer viewWithTag:100] 。 用你的内容视图replaceUIView

    • 还改变imageGallery指向你的窗口大小。

每当他改变尺寸时,这将改正内容的中心。

注意:这个内容不能很好地工作的唯一方法是使用UIScrollView标准缩放function。

这是我对这个问题的解决scheme,对于scrollview中的任何types的视图来说都非常好。

 -(void)scrollViewDidZoom:(__unused UIScrollView *)scrollView { CGFloat top; CGFloat left; CGFloat bottom; CGFloat right; if (_scrollView.contentSize.width < scrollView.bounds.size.width) { DDLogInfo(@"contentSize %@",NSStringFromCGSize(_scrollView.contentSize)); CGFloat width = (_scrollView.bounds.size.width-_scrollView.contentSize.width)/2.0; left = width; right = width; }else { left = kInset; right = kInset; } if (_scrollView.contentSize.height < scrollView.bounds.size.height) { CGFloat height = (_scrollView.bounds.size.height-_scrollView.contentSize.height)/2.0; top = height; bottom = height; }else { top = kInset; right = kInset; } _scrollView.contentInset = UIEdgeInsetsMake(top, left, bottom, right); if ([self.tiledScrollViewDelegate respondsToSelector:@selector(tiledScrollViewDidZoom:)]) { [self.tiledScrollViewDelegate tiledScrollViewDidZoom:self]; } } 

这里有很多解决scheme,但我有冒险把自己的。 这样做有两个好处:不会缩小缩放体验,正如更新正在进行的图像视图框架一样,还会考虑原始滚动视图插图(例如,在xib或故事板中定义的优雅处理半透明工具栏等) 。

首先,定义一个小帮手:

 CGSize CGSizeWithAspectFit(CGSize containerSize, CGSize contentSize) { CGFloat containerAspect = containerSize.width / containerSize.height, contentAspect = contentSize.width / contentSize.height; CGFloat scale = containerAspect > contentAspect ? containerSize.height / contentSize.height : containerSize.width / contentSize.width; return CGSizeMake(contentSize.width * scale, contentSize.height * scale); } 

为了保留原有的插页,定义字段:

 UIEdgeInsets originalScrollViewInsets; 

并在viewDidLoad填充它:

 originalScrollViewInsets = self.scrollView.contentInset; 

将UIImageView放入UIScrollView(假设UIImage本身在loadedImage var中):

 CGSize containerSize = self.scrollView.bounds.size; containerSize.height -= originalScrollViewInsets.top + originalScrollViewInsets.bottom; containerSize.width -= originalScrollViewInsets.left + originalScrollViewInsets.right; CGSize contentSize = CGSizeWithAspectFit(containerSize, loadedImage.size); UIImageView *imageView = [[UIImageView alloc] initWithFrame:(CGRect) { CGPointZero, contentSize }]; imageView.autoresizingMask = UIViewAutoresizingNone; imageView.contentMode = UIViewContentModeScaleAspectFit; imageView.image = loadedImage; [self.scrollView addSubview:imageView]; self.scrollView.contentSize = contentSize; [self centerImageViewInScrollView]; 

scrollViewDidZoom:从该滚动视图的UIScrollViewDelegate:

 - (void)scrollViewDidZoom:(UIScrollView *)scrollView { if (scrollView == self.scrollView) { [self centerImageViewInScrollView]; } } 

终于,集中本身:

 - (void)centerImageViewInScrollView { CGFloat excessiveWidth = MAX(0.0, self.scrollView.bounds.size.width - self.scrollView.contentSize.width), excessiveHeight = MAX(0.0, self.scrollView.bounds.size.height - self.scrollView.contentSize.height), insetX = excessiveWidth / 2.0, insetY = excessiveHeight / 2.0; self.scrollView.contentInset = UIEdgeInsetsMake( MAX(insetY, originalScrollViewInsets.top), MAX(insetX, originalScrollViewInsets.left), MAX(insetY, originalScrollViewInsets.bottom), MAX(insetX, originalScrollViewInsets.right) ); } 

我还没有testing方向的变化(即适当调整UIScrollView本身的反应),但修复应该是相对容易的。

您会发现Erdemus发布的解决scheme确实有效,但是…在某些情况下,scrollViewDidZoom方法不会被调用,并且您的图像被卡在左上angular。 一个简单的解决scheme是在最初显示图像时显式调用该方法,如下所示:

 [self scrollViewDidZoom: scrollView]; 

在很多情况下,您可能会调用这个方法两次,但这是比本主题中其他答案更清晰的解决scheme。

苹果的照片卷轴示例正是你正在寻找。 把它放在你的UIScrollView子类中,并将_zoomView改为你的UIImageView。

 -(void)layoutSubviews{ [super layoutSubviews]; // center the zoom view as it becomes smaller than the size of the screen CGSize boundsSize = self.bounds.size; CGRect frameToCenter = self.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; } self.imageView.frame = frameToCenter; } 

苹果的照片滚轮样本代码

我这样做的方法是在层次结构中添加一个额外的视图:

 UIScrollView -> UIView -> UIImageView 

给你的UIView和你的UIScrollView相同的宽高比,然后把你的UIImageView放在那里。

为了使animationstream畅地设置,

 self.scrollview.bouncesZoom = NO; 

并使用此function(使用此答案中的方法查找中心)

 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale { [UIView animateWithDuration:0.2 animations:^{ float offsetX = MAX((scrollView.bounds.size.width-scrollView.contentSize.width)/2, 0); float offsetY = MAX((scrollView.bounds.size.height-scrollView.contentSize.height)/2, 0); self.imageCoverView.center = CGPointMake(scrollView.contentSize.width*0.5+offsetX, scrollView.contentSize.height*0.5+offsetY); }]; } 

这产生了弹跳的效果,但不涉及任何突然的动作。

只需在swift中批准的答案,但没有使用委托的子类

 func centerScrollViewContents(scrollView: UIScrollView) { let contentSize = scrollView.contentSize let scrollViewSize = scrollView.frame.size; var contentOffset = scrollView.contentOffset; 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 } scrollView.setContentOffset(contentOffset, animated: false) } // UIScrollViewDelegate func scrollViewDidZoom(scrollView: UIScrollView) { centerScrollViewContents(scrollView) } 

如果你的内部imageView具有初始特定的宽度(例如300),你只是想中心其宽度在缩放比其初始宽度小,这可能也帮助你。

  func scrollViewDidZoom(scrollView: UIScrollView){ if imageView.frame.size.width < 300{ imageView.center.x = self.view.frame.width/2 } } 

这是目前我做这个工作的方式。 这更好,但还不完美。 尝试设置:

  myScrollView.bouncesZoom = YES; 

minZoomScale时解决视图不居中的问题。

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGSize screenSize = [[self view] bounds].size;//[[UIScreen mainScreen] bounds].size;// CGSize photoSize = [yourImage size]; CGFloat topInset = (screenSize.height - photoSize.height * [myScrollView zoomScale]) / 2.0; CGFloat sideInset = (screenSize.width - photoSize.width * [myScrollView zoomScale]) / 2.0; if (topInset < 0.0) { topInset = 0.0; } if (sideInset < 0.0) { sideInset = 0.0; } [myScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)]; ApplicationDelegate *appDelegate = (ApplicationDelegate *)[[UIApplication sharedApplication] delegate]; CGFloat scrollViewHeight; //Used later to calculate the height of the scrollView if (appDelegate.navigationController.navigationBar.hidden == YES) //If the NavBar is Hidden, set scrollViewHeight to 480 { scrollViewHeight = 480; } if (appDelegate.navigationController.navigationBar.hidden == NO) //If the NavBar not Hidden, set scrollViewHeight to 360 { scrollViewHeight = 368; } imageView.frame = CGRectMake(0, 0, CGImageGetWidth(yourImage)* [myScrollView zoomScale], CGImageGetHeight(yourImage)* [myScrollView zoomScale]); [imageView setContentMode:UIViewContentModeCenter]; } 

此外,我做了以下操作,以防止图像在缩小后粘在一边。

 - (void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale { myScrollView.frame = CGRectMake(0, 0, 320, 420); //put the correct parameters for your scroll view width and height above } 

Okay, I think I've found a pretty good solution to 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.

代码如下:

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

Just disable the pagination, so it'll work fine:

 scrollview.pagingEnabled = NO; 

我有完全相同的问题。 Here is how I solved

This code should get called as the result of scrollView:DidScroll:

 CGFloat imageHeight = self.imageView.frame.size.width * self.imageView.image.size.height / self.imageView.image.size.width; BOOL imageSmallerThanContent = (imageHeight < self.scrollview.frame.size.height) ? YES : NO; CGFloat topOffset = (self.imageView.frame.size.height - imageHeight) / 2; // If image is not large enough setup content offset in a way that image is centered and not vertically scrollable if (imageSmallerThanContent) { topOffset = topOffset - ((self.scrollview.frame.size.height - imageHeight)/2); } self.scrollview.contentInset = UIEdgeInsetsMake(topOffset * -1, 0, topOffset * -1, 0); 

Although the question is a bit old yet the problem still exists. I solved it in Xcode 7 by making the vertical space constraint of the uppermost item (in this case the topLabel ) to the superViews (the scrollView ) top an IBOutlet and then recalculating its constant every time the content changes depending on the height of the scrollView's subviews ( topLabel and bottomLabel ).

 class MyViewController: UIViewController { @IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var topLabel: UILabel! @IBOutlet weak var bottomLabel: UILabel! @IBOutlet weak var toTopConstraint: NSLayoutConstraint! override func viewDidLayoutSubviews() { let heightOfScrollViewContents = (topLabel.frame.origin.y + topLabel.frame.size.height - bottomLabel.frame.origin.y) // In my case abs() delivers the perfect result, but you could also check if the heightOfScrollViewContents is greater than 0. toTopConstraint.constant = abs((scrollView.frame.height - heightOfScrollViewContents) / 2) } func refreshContents() { // Set the label's text … self.view.layoutIfNeeded() } }