ios 带有居中 UIImageView 的 UIScrollView,如照片应用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/638299/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 15:44:47  来源:igfitidea点击:

UIScrollView with centered UIImageView, like Photos app

iosobjective-ciphonecocoa-touchuiscrollview

提问by Martin Ludvik

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.

我想要带有图像内容视图的滚动视图。图像实际上是比屏幕大得多的地图。地图最初应位于滚动视图的中心,就像将 iPhone 设为横向时照片应用程序中的照片一样。

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.

当我将图像框架移动到中心时,图像仅从框架顶部向下生长。我尝试使用 mapView 变换,动态更改 imageView 的框架。到目前为止,没有什么对我有用。

回答by JosephH

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

此代码应该适用于大多数版本的 iOS(并且已经过测试,可以在 3.1 以上版本上使用)。

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

它基于 Jonah 的回答中提到的 Apple WWDC 代码。

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

将以下内容添加到 UIScrollView 的子类中,并将 tileContainerView 替换为包含图像或图块的视图:

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

回答by Shizam

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 stayscentered:

它都从居中开始,当你缩小时它保持居中:

-(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 UIImageViewyou're using.

其中 'imageView' 是UIImageView您正在使用的。

回答by Jonah

Apple has released the 2010 WWDC session videos to all members of the iphone developer program. One of the topics discussed is how they created the photos app!!! They build a very similar app step by step and have made all the code available for free.

Apple 已向 iphone 开发者计划的所有成员发布了 2010 年 WWDC 会议视频。讨论的主题之一是他们如何创建照片应用程序!!!他们逐步构建了一个非常相似的应用程序,并免费提供了所有代码。

It does not use private api either. I can't put any of the code here because of the non disclosure agreement, but here is a link to the sample code download. You will probably need to login to gain access.

它也不使用私有 api。由于保密协议,我无法将任何代码放在这里,但这里有示例代码下载的链接。您可能需要登录才能访问。

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

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

And, here is a link to the iTunes WWDC page:

而且,这里是 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

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

回答by Rog

I suspect that you need to set the UIScrollView's contentOffset.

我怀疑您需要设置UIScrollView's contentOffset

回答by Martin Ludvik

I wish it was that simple. I did some research on the net and found that it is not just my problem, but many people are struggling with the same issue not just on iPhone, but on Apple's desktop Cocoa as well. See following links:

我希望就这么简单。我在网上做了一些研究,发现这不仅仅是我的问题,而且很多人都在为同样的问题而苦苦挣扎,不仅在 iPhone 上,而且在 Apple 的桌面 Cocoa 上也是如此。请参阅以下链接:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/5740-uiimageview-uiscrollview.html
The described solution is based on the property UIViewContentModeScaleAspectFit of the image, but unfortunately it does not work very well .The image is centered and grows properly, but the bouncing area seems to be much bigger than the picture.

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

This guy did not get the answer either:
http://discussions.apple.com/thread.jspa?messageID=8322675

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

And finally, the same problem on Apple's desktop Cocoa:
http://www.cocoadev.com/index.pl?CenteringInsideNSScrollView
I suppose the solution works, but it is based on the NSClipView, which is not on iPhone...

最后,在 Apple 的桌面 Cocoa 上也有同样的问题:
http: //www.cocoadev.com/index.pl?
CenteringInsideNSScrollView 我想这个解决方案是有效的,但它基于 NSClipView,它不在 iPhone 上......

Anybody has some solution working on iPhone?

有人在 iPhone 上有一些解决方案吗?

回答by Puppet

This worked for me:

这对我有用:

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

}

}

where 160 is half the width/height of your UIScrollView.

其中 160 是UIScrollView.

Then later I set the contentoffset to the one captured here.

然后我将 contentoffset 设置为这里捕获的那个。

回答by Jonah

Note: this method sort of works. if the image is smaller than the imageView, it will scroll partially off the screen. Not a big deal, but also not as nice as the photos app.

注意:这种方法有点工作。如果图像小于 imageView,它将部分滚动到屏幕外。没什么大不了的,但也不如照片应用程序那么好。

First, it's important to understand that we are dealing with 2 views, the imageview with the image in it, and the scrollview with the imageview in it. So, first set the imageview to the size of the screen:

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

 [myImageView setFrame:self.view.frame];

Then, center your image in the imageview:

然后,在图像视图中居中您的图像:

 myImageView.contentMode = UIViewContentModeCenter;

Here's my entire code:

这是我的整个代码:

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

回答by Jonah

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.

好的,我想我已经找到了一个很好的解决这个问题的方法。诀窍是不断重新调整 imageView 的框架。我发现这比不断调整 contentInsets 或 contentOffSets 效果更好。我不得不添加一些额外的代码来容纳纵向和横向图像。

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

回答by SEQOY Development Team

One elegant way to center the content of UISCrollViewis this.

将 的内容居中的一种优雅方式UISCrollView是这样。

Add one observer to the contentSizeof your UIScrollView, so this method will be called everytime the content change...

将一个观察者添加到您的contentSizeUIScrollView,因此每次内容更改时都会调用此方法...

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

Now on your observer method:

现在在你的观察者方法上:

- (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 ); }
  • You should change the [pointer viewWithTag:100]. Replace by your content view UIView.

    • Also change imageGallerypointing to your window size.
  • 你应该改变[pointer viewWithTag:100]. 替换为您的内容视图UIView

    • 还要更改imageGallery指向您的窗口大小。

This will correct the center of the content everytime his size change.

这将在每次他的大小改变时纠正内容的中心。

NOTE:The only way this content don't works very well is with standard zoom functionality of the UIScrollView.

注意:此内容不能很好地工作的唯一方法是使用UIScrollView.

回答by ozan.aksu

In Monotouch that worked for me.

在对我有用的 Monotouch 中。

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