UIScrollView的中心内容较小时

hpi*_*que 136 cocoa-touch objective-c uiscrollview uiimageview ios

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

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

我正在使用iPhone 3.0.

以下代码几乎可以使用.如果在达到最小缩放级别后将其捏住,图像将返回到左上角.

- (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)];
}
Run Code Online (Sandbox Code Playgroud)

Erd*_*mus 228

我有一个非常简单的解决方案!您只需在放大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);
}
Run Code Online (Sandbox Code Playgroud)

  • 这种调整帮我出来的时候,有滚动视图插图:`CGFloat的OFFSETX = MAX((scrollView.bounds.size.width - scrollView.contentInset.left - scrollView.contentInset.right - scrollView.contentSize.width)*0.5,0.0); CGFloat的OFFSETY = MAX((scrollView.bounds.size.height - scrollView.contentInset.top - scrollView.contentInset.bottom - scrollView.contentSize.height)*0.5,0.0);` (6认同)
  • 对我来说,这个解决方案比使用Liam的NYOBetterZoom更加生涩.也许它取决于图像大小等.道德; 使用最适合您需求的解决方案 (5认同)
  • 我注意到,与许多其他中心技术一样,使用`zoomToRect:`时,这似乎遇到了问题.如果您碰巧需要该功能,使用`contentInset`方法可以更好地工作.有关详细信息,请参阅http://petersteinberger.com/blog/2013/how-to-center-uiscrollview/. (3认同)
  • 谢谢你,Erdemus,你节省了几个小时的工作!:) (2认同)
  • Stackoverflow GOLD STAR为此.我正在努力解决这个问题,但解决方案非常简单. (2认同)
  • 简化一下:`CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width)*0.5,0.0);` (2认同)

Wil*_* T. 47

@ EvelynCordner的答案是我的应用程序中最好的答案.比其他选项少得多的代码.

如果有人需要,这是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)
}
Run Code Online (Sandbox Code Playgroud)

  • 我把这个代码放在一个函数中,并在*viewDidLayoutSubviews*中调用它,以确保它最初正确设置. (4认同)
  • 这个很棒!缩小后,视图甚至可以正常生动. (3认同)

Lia*_*nes 25

好吧,过去两天我一直在争吵,终于找到了一个非常可靠(迄今为止......)的解决方案,我认为我应该分享它并为其他人带来一些痛苦.:)如果您确实发现此解决方案的问题,请大声喊!

我基本上已经了解了其他人的所有内容:搜索StackOverflow,Apple Developer Forums,查看了three20,ScrollingMadness,ScrollTestSuite等的代码.我尝试放大UIImageView框架,使用UIScrollView的偏移和/或插入来自ViewController等,但没有什么工作得很好(正如其他人也发现的那样).

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

  1. 对UIImageView进行子类化以使其动态地改变它自己的大小 - 这根本不起作用.
  2. 对UIScrollView进行子类化以便它动态地改变它自己的contentOffset - 这对我来说似乎是一个胜利者.

使用这个子类化UIScrollView方法,我覆盖了contentOffset mutator,因此当图像缩放比视口小时,它不会设置{0,0} - 而是设置偏移量,使图像在视口中保持居中.到目前为止,似乎总是有效.我用宽,高,小和大的图像检查它,并没有"工作,但捏最小缩放打破它"问题.

我已经将一个示例项目上传到使用此解决方案的github,您可以在这里找到它:http://github.com/nyoron/NYOBetterZoom

  • 对于那些感兴趣的人 - 我已经更新了上面链接的项目,所以它更少依赖于ViewController做'正确的事情',自定义UIScrollView本身负责更多的细节. (2认同)
  • 利亚姆,你摇滚.我说这是ScrollingMadness的作者.BTW在iPad上的3.2+设置contentInset in scrollViewDidZoom(一个新的3.2+委托方法)Just Works. (2认同)

Jos*_*phH 23

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

它基于photoscoller的Apple WWDC代码.

将以下内容添加到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;
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案,因为它更简单.谢谢.你救了我的命!!!!!!:d (3认同)

小智 22

对于更适合使用自动布局的滚动视图的解决方案,请使用滚动视图的内容插入,而不是更新滚动视图的子视图的帧.

- (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);
}
Run Code Online (Sandbox Code Playgroud)


hpi*_*que 20

目前我正在进行子类化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
Run Code Online (Sandbox Code Playgroud)

除了简短和甜美之外,这段代码比@Erdemus解决方案产生更加平滑的缩放.您可以在RMGallery演示中看到它的实际效果.

  • 您不需要子类UIScrollView来实现此方法.Apple允许您在委托方法中查看滚动和缩放事件.具体来说: - (void)scrollViewDidZoom:(UIScrollView*)scrollView; (4认同)

Vic*_*ius 12

我花了一天时间来解决这个问题,最后实现了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);
}
Run Code Online (Sandbox Code Playgroud)

这可确保当图像小于屏幕时,周围仍有足够的空间,因此您可以将其放置在所需的确切位置.

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

基本上,这样做是检查你的图像视图的宽度/高度是否小于屏幕的宽度/高度,如果是这样,创建一个屏幕宽度/高度的一半的插入(如果你想要图像,你可能会把它做得更大走出屏幕边界).

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


Jon*_*nah 7

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

它也不使用私有api.由于非公开协议,我不能在此处放置任何代码,但这里是示例代码下载的链接.您可能需要登录才能获得访问权限.

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

  • 它被称为“使用滚动视图设计应用程序”。 (2认同)

Woj*_*zki 5

如果contentInset不需要其他任何东西,它可以用于将滚动视图的内容居中。

class ContentCenteringScrollView: UIScrollView {

    override var bounds: CGRect {
        didSet { updateContentInset() }
    }

    override var contentSize: CGSize {
        didSet { updateContentInset() }
    }

    private func updateContentInset() {
        var top = CGFloat(0)
        var left = CGFloat(0)
        if contentSize.width < bounds.width {
            left = (bounds.width - contentSize.width) / 2
        }
        if contentSize.height < bounds.height {
            top = (bounds.height - contentSize.height) / 2
        }
        contentInset = UIEdgeInsets(top: top, left: left, bottom: top, right: left)
    }
}
Run Code Online (Sandbox Code Playgroud)

如果这种方法是您仍然可以contentLayoutGuide用来在滚动视图中放置内容的优势

scrollView.addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    imageView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
    imageView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
    imageView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
    imageView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor)
])
Run Code Online (Sandbox Code Playgroud)

或者只是将内容拖放到 Xcode 的 Interface Builder 中。