UIScrollView与居中的UIImageView,如照片应用程序

我想用图像内容视图滚动视图。 图像实际上是比屏幕大得多的地图。 地图应该最初位于滚动视图的中心,比如当您将iPhone转为横向时,照片应用中的照片。

替代文字

我没有设法在正确的缩放和滚动中心的地图。 假设地图图像从屏幕的顶部开始(纵向),代码如下所示:

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

当我将图像帧移动到中央时,图像只会从帧的顶部向下生长。 我试图玩mapView变换,dynamic变化的imageView框架。 目前为止我没有任何作品。

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

它基于Jonah的答案中提到的苹果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; } 

这就是我想的,解决scheme就像苹果的照片应用程序一样。 我一直在使用以下解决scheme:

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

但是我不喜欢这个解决scheme,因为在缩放完成之后,它会弹跳,然后快速跳到非常性感的中心。 原来,如果你几乎做了完全相同的逻辑,但在这个委托function:

 -(void)scrollViewDidZoom:(UIScrollView *)pScrollView 

它都开始居中,当你缩小它居中居中:

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

其中'imageView'是你正在使用的UIImageView

苹果已经向所有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

我怀疑你需要设置UIScrollViewcontentOffset

我希望这很简单。 我在网上做了一些研究,发现这不仅仅是我的问题,而且很多人不仅仅在​​iPhone上面,而且在苹果的桌面Cocoa上也面临同样的问题。 请参阅以下链接:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/5740-uiimageview-uiscrollview.html
所描述的解决scheme是基于图像的属性UIViewContentModeScaleAspectFit,但不幸的是,它不能很好地工作。图像居中,生长正常,但弹跳区域似乎比图片大得多。

这家伙也没有得到答案:
http://discussions.apple.com/thread.jspa?messageID=8322675

最后,苹果的桌面Cocoa也出现同样的问题:
http://www.cocoadev.com/index.pl?CenteringInsideNSScrollView
我想解决scheme的工作原理,但它是基于NSClipView,这是不是在iPhone上…

任何人有一些解决iPhone的工作?

这对我工作:

 - (void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale { CGFloat tempx = view.center.x-160; CGFloat tempy = view.center.y-160; myScrollViewOffset = CGPointMake(tempx,tempy); 

}

其中160是你的UIScrollView的宽度/高度的一半。

之后,我将contentoffset设置为这里捕获的内容。

注意:这种方法的作品。 如果图像比imageView小,它将部分地从屏幕上滚动。 没有什么大不了的,但也不如照片应用程序好。

首先,了解我们正在处理2个视图,其中包含图像的imageview和其中包含imageview的滚动视图,这一点很重要。 所以,首先将imageview设置为屏幕的大小:

  [myImageView setFrame:self.view.frame]; 

然后,将您的图像置于imageview中:

  myImageView.contentMode = UIViewContentModeCenter; 

这是我的整个代码:

 - (void)viewDidLoad { AppDelegate *appDelegate = (pAppDelegate *)[[UIApplication sharedApplication] delegate]; [super viewDidLoad]; NSString *Path = [[NSBundle mainBundle] bundlePath]; NSString *ImagePath = [Path stringByAppendingPathComponent:(@"data: %@", appDelegate.MainImageName)]; UIImage *tempImg = [[UIImage alloc] initWithContentsOfFile:ImagePath]; [imgView setImage:tempImg]; myScrollView = [[UIScrollView alloc] initWithFrame:[[self view] bounds]]; [myScrollView addSubview:myImageView]; //Set ScrollView Appearance [myScrollView setBackgroundColor:[UIColor blackColor]]; myScrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite; //Set Scrolling Prefs myScrollView.bounces = YES; myScrollView.delegate = self; myScrollView.clipsToBounds = YES; // default is NO, we want to restrict drawing within our scrollview [myScrollView setCanCancelContentTouches:NO]; [myScrollView setScrollEnabled:YES]; //Set Zooming Prefs myScrollView.maximumZoomScale = 3.0; myScrollView.minimumZoomScale = CGImageGetWidth(tempImg.CGImage)/320; myScrollView.zoomScale = 1.01; //Added the .01 to enable scrolling immediately upon view load. myScrollView.bouncesZoom = YES; [myImageView setFrame:self.view.frame];//rect];// .frame.size.height = imageHeight; myImageView.contentMode = UIViewContentModeCenter; self.view = myScrollView; [tempImg release]; } 

好吧,我想我已经find了一个很好的解决这个问题的方法。 诀窍是不断调整imageView的框架。 我发现这比不断调整contentInsets或contentOffSets更好。 我不得不添加一些额外的代码,以适应纵向和横向的图像。

代码如下:

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

一个优雅的方式来居中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。

在Monotouch中为我工作。

 this._scroll.ScrollRectToVisible(new RectangleF(_scroll.ContentSize.Width/2, _scroll.ContentSize.Height/2,1,1),false); 

这是一个替代解决scheme,类似于@ JosephH的答案,但是这个考虑了图片的实际尺寸。 所以,当用户平移/放大时,屏幕上的空白不会超出要求。 例如,在纵向屏幕上显示横向图像时,这是一个常见问题。 当整个图像在屏幕上时(Aspect Fit),图像上下会有空白。 然后,在放大时,其他解决scheme将空白视为图像的一部分,因为它在imageView中。 他们会让你平移屏幕上的大部分图像,只留下可见的空白。 这对用户来说看起来很糟糕。

有了这个类,你需要传递它正在处理的imageView。 我很想让它自动检测,但是这样做速度更快,而且您希望能够在layoutSubviews方法中获得所有的速度。

注意:这是因为这要求AutoLayout没有为scrollView启用。

 // // CentringScrollView.swift // Cerebral Gardens // // Created by Dave Wood // Copyright © 2016 Cerebral Gardens Inc. All rights reserved. // import UIKit class CentringScrollView: UIScrollView { var imageView: UIImageView? override func layoutSubviews() { super.layoutSubviews() guard let superview = superview else { return } guard let imageView = imageView else { return } guard let image = imageView.image else { return } var frameToCentre = imageView.frame let imageWidth = image.size.width let imageHeight = image.size.height let widthRatio = superview.bounds.size.width / imageWidth let heightRatio = superview.bounds.size.height / imageHeight let minRatio = min(widthRatio, heightRatio, 1.0) let effectiveImageWidth = minRatio * imageWidth * zoomScale let effectiveImageHeight = minRatio * imageHeight * zoomScale contentSize = CGSize(width: max(effectiveImageWidth, bounds.size.width), height: max(effectiveImageHeight, bounds.size.height)) frameToCentre.origin.x = (contentSize.width - frameToCentre.size.width) / 2 frameToCentre.origin.y = (contentSize.height - frameToCentre.size.height) / 2 imageView.frame = frameToCentre } }