ios 较小时 UIScrollView 的中心内容
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1316451/
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
Center content of UIScrollView when smaller
提问by hpique
I have a UIImageView
inside a UIScrollView
which I use for zooming and scrolling. If the image / content of the scroll view is bigger than the scroll view, everything works fine. However, when the image becomes smaller than the scroll view, it sticks to the top left corner of the scroll view. I would like to keep it centered, like the Photos app.
我有一个用于缩放和滚动的UIImageView
内部 a UIScrollView
。如果滚动视图的图像/内容大于滚动视图,则一切正常。但是,当图像变得小于滚动视图时,它会粘在滚动视图的左上角。我想让它居中,就像照片应用程序一样。
Any ideas or examples about keeping the content of the UIScrollView
centered when it's smaller?
关于在UIScrollView
较小时保持居中内容的任何想法或示例?
I am working with iPhone 3.0.
我正在使用 iPhone 3.0。
The following code almost works. The image returns to the top left corner if I pinch it after reaching the minimum zoom level.
以下代码几乎有效。如果在达到最小缩放级别后捏住图像,图像将返回到左上角。
- (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)];
}
采纳答案by hpique
Currently I'm subclassing UIScrollView
and overriding setContentOffset:
to adjust the offset based on contentSize
. It works both with pinch and programatic zooming.
目前我正在子类化UIScrollView
和覆盖setContentOffset:
以调整基于contentSize
. 它适用于捏合和程序缩放。
@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 RMGallerydemo.
除了短小精悍之外,这段代码比@Erdemus 解决方案产生了更平滑的缩放效果。您可以在RMGallery演示中看到它的运行情况。
回答by Erdemus
I've got very simple solution! All you need is to update the center of your subview (imageview) while zooming in the ScrollViewDelegate. If zoomed image is smaller than scrollview then adjust subview.center else center is (0,0).
我有非常简单的解决方案!您所需要的只是在放大 ScrollViewDelegate 时更新子视图 (imageview) 的中心。如果缩放图像小于滚动视图,则调整 subview.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);
}
回答by William T.
@EvelynCordner's answerwas the one that worked best in my app. A lot less code than the other options too.
@EvelynCordner 的答案是在我的应用中效果最好的答案。代码也比其他选项少得多。
Here's the Swift version if anyone needs it:
如果有人需要,这是 Swift 版本:
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)
scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0, 0)
}
回答by Liam Jones
Okay, I've been fighting this for the past two days on and off and having finally come to a pretty reliable (so far...) solution I thought I should share it and save others some pain. :) If you do find a problem with this solution please shout!
好的,过去两天我一直在为此断断续续地解决这个问题,终于找到了一个非常可靠的(到目前为止......)解决方案,我想我应该分享它并为其他人节省一些痛苦。:) 如果您确实发现此解决方案有问题,请大喊大叫!
I've basically gone through what everyone else has: searching StackOverflow, the Apple Developer Forums, looked at the code for three20, ScrollingMadness, ScrollTestSuite, etc. I've tried enlarging the UIImageView frame, playing with the UIScrollView's offset and/or insets from the ViewController, etc. but nothing worked great (as everyone else has found out too).
我基本上已经完成了其他人所拥有的:搜索 StackOverflow、Apple 开发者论坛、查看了 Three20、ScrollingMadness、ScrollTestSuite 等的代码。我尝试放大 UIImageView 框架,使用 UIScrollView 的偏移量和/或插图来自 ViewController 等,但没有任何效果(正如其他人也发现的那样)。
After sleeping on it, I tried a couple of alternative angles:
睡在上面后,我尝试了几个不同的角度:
- Subclassing the UIImageView so it alters it's own size dynamically - this didn't work well at all.
- Subclassing the UIScrollView so it alters it's own contentOffset dynamically - this is the one that seems to be a winner for me.
- 子类化 UIImageView 以便它动态地改变它自己的大小 - 这根本不起作用。
- 对 UIScrollView 进行子类化,以便动态更改它自己的 contentOffset - 这对我来说似乎是赢家。
With this subclassing UIScrollView method I'm overriding the contentOffset mutator so it isn't setting {0,0} when the image is scaled smaller than the viewport - instead it's setting the offset such that the image will be kept centred in the viewport. So far, it always seems to work. I've checked it with wide, tall, tiny & large images and doesn't have the "works but pinch at minimum zoom breaks it" issue.
使用这个子类化 UIScrollView 方法,我覆盖了 contentOffset mutator,因此当图像的缩放比例小于视口时,它不会设置 {0,0} - 相反,它设置了偏移量,以使图像保持在视口的中心。到目前为止,它似乎总是有效。我已经用宽、高、小和大的图像检查过它,并且没有“工作但在最小缩放比例下捏会破坏它”的问题。
I've uploaded an example project to github that uses this solution, you can find it here: http://github.com/nyoron/NYOBetterZoom
我已将使用此解决方案的示例项目上传到 github,您可以在这里找到它:http: //github.com/nyoron/NYOBetterZoom
回答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 for the photoscoller.
它基于 photocoller 的 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 Evelyn Cordner
For a solution better suited for scroll views that use autolayout, use content insets of the scroll view rather than updating the frames of your scroll view's subviews.
对于更适合使用自动布局的滚动视图的解决方案,请使用滚动视图的内容插入而不是更新滚动视图子视图的框架。
- (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);
}
回答by Vicarius
I've spent a day fighting with this issue, and ended up implementing the scrollViewDidEndZooming:withView:atScale:as follows:
我花了一天的时间来解决这个问题,最终实现了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);
}
This ensures that when the image is smaller than the screen, there's still adequate space around it so you can position it to the exact place you want.
这可确保当图像小于屏幕时,其周围仍有足够的空间,以便您可以将其准确定位到所需的位置。
(assuming that your UIScrollView contains an UIImageView to hold the image)
(假设你的 UIScrollView 包含一个 UIImageView 来保存图像)
Essentially, what this does is check whether your image view's width / height is smaller that the screen's width / height, and if so, create an inset of half the screen's width / height (you could probably make this larger if you want the image to go out of the screen bounds).
本质上,这样做是检查图像视图的宽度/高度是否小于屏幕的宽度/高度,如果是,则创建一个屏幕宽度/高度一半的插图(如果您想要图像,您可以将其放大走出屏幕边界)。
Note that since this is a UIScrollViewDelegatemethod, don't forget to add it to your view controller's declaration, so to avoid getting a build issue.
请注意,由于这是一个UIScrollViewDelegate方法,请不要忘记将其添加到视图控制器的声明中,以避免出现构建问题。
回答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。由于保密协议,我无法将任何代码放在这里,但这里有示例代码下载的链接。您可能需要登录才能访问。
And, here is a link to the iTunes WWDC page:
而且,这里是 iTunes WWDC 页面的链接:
回答by AHA
Ok, this solution is working for me. I have a subclass of UIScrollView
with a reference to the UIImageView
it is displaying. Whenever the UIScrollView
zooms, the contentSize property is adjusted. It is in the setter that I scale the UIImageView
appropriately and also adjust its center position.
好的,这个解决方案对我有用。我有一个UIScrollView
引用UIImageView
它正在显示的子类。每当UIScrollView
缩放时,都会调整 contentSize 属性。我在二传手中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 %f x %f",self.zoomScale,size.width,size.height);
}
Evertime I set the image on the UIScrollView
I resize it. The UIScrollView
in the scrollview is also a custom class I created.
每次我将图像设置在UIScrollView
我调整它的大小上。将UIScrollView
在滚动视图也是我创建了一个自定义类。
-(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;
}
With these two methods I am able to have a view that behaves just like the photos app.
使用这两种方法,我可以拥有一个与照片应用程序类似的视图。
回答by hatfinch
The way I've done this is to add an extra view into the hierarchy:
我这样做的方法是在层次结构中添加一个额外的视图:
UIScrollView -> UIView -> UIImageView
Give your UIView
the same aspect ratio as your UIScrollView
, and centre your UIImageView
into that.
给你UIView
与你相同的纵横比UIScrollView
,并将你的中心UIImageView
放在那里。