【问题标题】:Center content of UIScrollView when smaller较小时 UIScrollView 的中心内容
【发布时间】:2010-11-21 22:25:08
【问题描述】:

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

关于在 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)];
}

【问题讨论】:

  • 你有没有彻底解决过这个问题?我正在努力解决同样的问题。
  • 注意:如果 inset 不是 1(不缩放),则使用 initialZoom 值计算 inset。例如。使用这些行: float topInset = (maxSize.height - imageSize.height * initialZoom) / 2.0; float sideInset = (maxSize.width - imageSize.width * initialZoom) / 2.0;最后设置初始缩放值[imageScrollView setZoomScale: initialZoom];

标签: ios objective-c cocoa-touch uiscrollview uiimageview


【解决方案1】:

我有一个非常简单的解决方案! 您只需在放大 ScrollViewDelegate 时更新子视图(图像视图)的中心。 如果缩放图像小于滚动视图,则调整 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);
}

【讨论】:

  • 对我来说,这个解决方案比使用 Liam 的 NYOBetterZoom 更生涩。也许这取决于图像大小等。道德;使用最适合您需求的解决方案
  • Stackoverflow 金星。我一直在努力解决这个问题,但解决方案非常简单。
  • 简化一点:CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
  • 当滚动视图有插入时,这个调整帮助了我: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);
  • 我注意到,与许多其他居中技术一样,使用zoomToRect: 时似乎会遇到问题。如果您碰巧需要该功能,使用contentInset 方法效果更好。有关详细信息,请参阅petersteinberger.com/blog/2013/how-to-center-uiscrollview
【解决方案2】:

@EvelynCordner's answer 是在我的应用中效果最好的那个。也比其他选项少很多代码。

如果有人需要,这里是 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)
}

【讨论】:

  • 这个很好用!缩小后视图甚至可以正确动画。
  • 我把这段代码放在一个 func 中,并在 viewDidLayoutSubviews 中调用它,以确保它最初设置正确。
  • 很好的电话 @CarterMedlin 对我的 Swift 3 初始加载帮助很大。
  • 这似乎可行,但我不明白为什么,因为它似乎总是计算相同的插图:滚动视图的边界是固定的,内容大小也是如此。
  • contentSize 在缩放时改变 scrollView 尺寸只是固定的
【解决方案3】:

好的,过去两天我断断续续地与此作斗争,终于找到了一个非常可靠的(到目前为止......)解决方案,我想我应该分享它并为其他人节省一些痛苦。 :) 如果您确实发现此解决方案有问题,请大声喊叫!

我基本上已经完成了其他人所拥有的:搜索 StackOverflow、Apple 开发者论坛,查看了 three20、ScrollingMadness、ScrollTestSuite 等的代码。我尝试扩大 UIImageView 框架,使用 UIScrollView 的偏移量和/ 或来自 ViewController 的插图等,但效果不佳(其他人也发现了)。

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

  1. 对 UIImageView 进行子类化,以便它动态改变自己的大小 - 这一点都不奏效。
  2. 对 UIScrollView 进行子类化,使其动态更改自己的 contentOffset - 这对我来说似乎是赢家。

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

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

【讨论】:

  • 对于那些感兴趣的人——我已经更新了上面的链接项目,因此它对 ViewController 做“正确的事情”的依赖程度有所降低,自定义 UIScrollView 本身会处理更多的细节。跨度>
  • 利亚姆,你摇滚。我是作为 ScrollingMadness 的作者这么说的。顺便说一句,iPad 在 3.2+ 中设置 contentInset 在 scrollViewDidZoom(一个新的 3.2+ 委托方法)Just Works。
  • 非常简洁的一段代码。我一直在为此挠头。将项目添加到handyiphonecode.com
  • 这很棒。谢谢你。它并没有补偿我的 UIStatusBar 被隐藏,所以我将anOffset.y = -(scrollViewSize.height - zoomViewSize.height) / 2.0 更改为anOffset.y = (-(scrollViewSize.height - zoomViewSize.height) / 2.0) + 10;
  • 我认为 bi Erdemus 给出的解决方案要简单得多。
【解决方案4】:

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

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

【讨论】:

  • 这对我有用,但是如果图像较小以某种方式开始,则此代码(直接调用时)不会更新滚动视图。我需要做的是首先将UIScrollview 内的UIView 放在同一个中心(view.center = scrollview.center;),然后在scrollViewDidZoom 中将view.frame.origin xy 设置为0再次。
  • 对我也很有效,但我在viewWillAppear 的主队列中做了一个dispatch_async,我在主滚动视图上调用scrollViewDidZoom:。这使得视图以居中的图像显示。
  • viewDidLayoutSubviews 中调用此代码,以确保在视图第一次显示时正确设置。
【解决方案5】:

此代码应该适用于大多数 iOS 版本(并且已经过测试可在 3.1 及更高版本上运行)。

它基于 photocoller 的 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;
}

【讨论】:

  • 这应该是公认的答案,因为它更简单。谢谢。你救了我的命!!!!!! :D
  • 删除所有 iOS 3.2+ API 调用后,居中逻辑似乎只适用于 iOS 3.2+ 而不是 3.1.3 的设备(跳跃、闪烁、随机偏移)。我已经比较了 3.1.3 和 3.2+ 之间的帧原点和大小的输出,即使它们匹配,由于某种原因,子视图仍然定位不正确。很奇怪。只有利亚姆·琼斯的回答对我有用。
  • @Erdemus 和@JosephH 解决方案都可以工作,但UIScrollView 子类方法似乎更可取:它在第一次显示视图时调用 + 在缩放过程中连续调用(而 scrollViewDidZoom 仅每次滚动和事后调用一次)
【解决方案6】:

目前我正在继承 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

除了短小精悍之外,这段代码还产生了比@Erdemus 解决方案更平滑的缩放。您可以在 RMGallery 演示中看到它的实际效果。

【讨论】:

  • 你不需要继承 UIScrollView 来实现这个方法。 Apple 允许您在委托方法中查看滚动和缩放事件。具体来说: - (void)scrollViewDidZoom:(UIScrollView *)scrollView;
  • 我见过的最好的解决方案(即使在使用约束时也能工作)。
【解决方案7】:

我花了一天时间解决这个问题,最终实现了 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 来保存图像)

本质上,它的作用是检查您的图像视图的宽度/高度是否小于屏幕的宽度/高度,如果是,则创建一个屏幕宽度/高度一半的插图(如果需要,您可以将其放大图像超出屏幕边界)。

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

【讨论】:

    【解决方案8】:

    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

    【讨论】:

    • 有问题的例子是 MyImagePicker,有趣的是,它也出现了同样的问题。
    • 我应该更清楚。有问题的示例实际上是“PhotoScroller”而不是“MyImagePicker”。你是对的,“MyImagePicker”不能正常工作。但是,“PhotoScroller”可以。试试看。
    • 你还记得他们讨论 Photoscroller 的 WWDC 视频的标题吗?
    • 它被称为“设计带有滚动视图的应用程序”。
    【解决方案9】:

    如果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)
        }
    }
    

    如果这种方法的优势在于您仍然可以使用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)
    ])
    

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

    【讨论】:

      【解决方案10】:

      好的,这个解决方案对我有用。我有一个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);
      }
      

      每当我在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;
      }
      

      通过这两种方法,我可以拥有一个与照片应用程序一样的视图。

      【讨论】:

      • 这似乎可以解决问题,而且是一个相当简单的解决方案。一些缩放过渡并不完美,但我相信它可以修复。我会再做一些实验。
      • 更新前解决方案已经很清楚了。现在有点混乱。你能澄清一下吗?
      【解决方案11】:

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

      UIScrollView -> UIView -> UIImageView
      

      使您的UIView 与您的UIScrollView 具有相同的纵横比,并将您的UIImageView 居中。

      【讨论】:

      • 谢谢,哈特芬奇。也会试试这个。您能否发布示例代码或更改我的示例代码以显示您如何构建视图层次结构?
      • 这类作品。除了因为 UIView 是 UIScrollView 的大小,如果图像更小(即横向而不是纵向),您可以将部分图像滚动到屏幕外。照片应用程序不允许这样做,并且看起来更好。
      【解决方案12】:

      只是快速批准的答案,但没有使用委托进行子类化

      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)
      }
      

      【讨论】:

        【解决方案13】:

        我知道上面的一些答案是对的,但我只是想给出一些解释,cmets会让你明白我们为什么这样做。

        当我第一次加载scrollView时,我写了下面的代码让它居中,请注意我们先设置contentOffset,然后设置contentInset

            scrollView.maximumZoomScale = 8
            scrollView.minimumZoomScale = 1
        
            // set vContent frame
            vContent.frame = CGRect(x: 0,
                                    y: 0  ,
                                    width: vContentWidth,
                                    height: vContentWidth)
            // set scrollView.contentSize
            scrollView.contentSize = vContent.frame.size
        
            //on the X direction, if contentSize.width > scrollView.bounds.with, move scrollView from 0 to offsetX to make it center(using `scrollView.contentOffset`)
            // if not, don't need to set offset, but we need to set contentInset to make it center.(using `scrollView.contentInset`)
            // so does the Y direction.
            let offsetX = max((scrollView.contentSize.width - scrollView.bounds.width) * 0.5, 0)
            let offsetY = max((scrollView.contentSize.height - scrollView.bounds.height) * 0.5, 0)
            scrollView.contentOffset = CGPoint(x: offsetX, y: offsetY)
        
            let topX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0)
            let topY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0)
            scrollView.contentInset = UIEdgeInsets(top: topY, left: topX, bottom: 0, right: 0)
        

        然后,当我捏 vContent 时,我编写以下代码使其居中。

        func scrollViewDidZoom(_ scrollView: UIScrollView) {
            //we just need to ensure that the content is in the center when the contentSize is less than scrollView.size.
            let topX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0)
            let topY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0)
            scrollView.contentInset = UIEdgeInsets(top: topY, left: topX, bottom: 0, right: 0)
        }
        

        【讨论】:

          【解决方案14】:

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

          【讨论】:

          • 会试试的。是否可以使用 UIScrollViewDelegate 方法而不是观察 contentSize?
          • 最有可能;你会使用- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale(描述于developer.apple.com/iphone/library/documentation/UIKit/…),但我更喜欢观察contentSize,否则你最终会丢弃新的缩放比例并从视图中找到它。 (除非您可以根据视图的相对大小得出一些很棒的数学运算。)
          • 谢谢,蒂姆。我正在使用 scrollViewDidEndZooming 。查看有问题的代码(我在您第一次回复后添加了它)。它几乎可以工作。唯一剩下的问题是,如果我在达到 minimumZoomScale 后捏住图像,它会返回到左上角。
          • 您的意思是它可以从最小缩放比例以外的任何比例进行捏合(使图像居中),并且只有在您达到最小比例后再次尝试捏合时才会中断?或者它根本不适用于最小比例?
          • 第一个。它将图像居中以从最小缩放比例以外的任何比例进行捏合,如果您在达到最小比例后再次尝试捏合它,它会中断。此外,当它第一次达到最小缩放比例时,内容会像从顶部进入一样短暂动画,这会产生短暂的怪异效果。这至少发生在 3.0 模拟器上。
          【解决方案15】:

          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]。替换为您的 内容查看UIView

            • 同时更改imageGallery 指向您的窗口大小。

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

          注意:此内容效果不佳的唯一方法是使用UIScrollView 的标准缩放功能。

          【讨论】:

          • 无法使其正常工作。看来您正在以 scrollView 而不是其内容为中心。为什么窗口大小很重要?代码不应该也更正 x 位置吗?
          【解决方案16】:

          这是我对该问题的解决方案,它适用于滚动视图中的任何类型的视图。

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

          【讨论】:

            【解决方案17】:

            这里有很多解决方案,但我会冒险将自己的解决方案放在这里。这有两个好处:它不会弄乱缩放体验,就像更新正在进行的图像视图框架一样,并且它尊重原始滚动视图插图(例如,在 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)
                );
            }
            

            我还没有测试方向变化(即调整 UIScrollView 本身大小的正确反应),但修复它应该相对容易。

            【讨论】:

              【解决方案18】:

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

              [self scrollViewDidZoom: scrollView];
              

              在许多情况下,您可能会调用此方法两次,但这比本主题中的其他一些答案更简洁。

              【讨论】:

                【解决方案19】:

                Apple 的 Photo Scroller 示例完全符合您的要求。把它放在你的 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; 
                }
                

                Apple's Photo Scroller Sample Code

                【讨论】:

                  【解决方案20】:

                  为了使动画流畅,设置

                  self.scrollview.bouncesZoom = NO;
                  

                  并使用此功能(使用this answer的方法找到中心)

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

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

                  【讨论】:

                    【解决方案21】:

                    如果您的内部 imageView 具有初始特定宽度(例如 300)并且您只想将其宽度居中缩放小于其初始宽度,这也可能对您有所帮助。

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

                    【讨论】:

                      【解决方案22】:

                      这是我目前的工作方式。它更好,但仍然不完美。尝试设置:

                       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
                      }
                      

                      【讨论】:

                      • 你好乔纳。看来我们处理同一个问题已经有一段时间了。将检查您的两个解决方案并尽快回复。
                      • 您好 Jonah,我尝试了您的最新解决方案。但是,什么是临时图像?我试着把temporaryImage = imageView.image;但是,一旦我放大,图像就会消失。谢谢,潘纳格
                      • Pannag,temporaryImage 命名不当。它应该被称为 myImage,因为它就是您使用的任何图片。很抱歉造成混乱。
                      【解决方案23】:

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

                      代码如下:

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

                      【讨论】:

                        【解决方案24】:

                        只需禁用分页,它就可以正常工作:

                        scrollview.pagingEnabled = NO;
                        

                        【讨论】:

                          【解决方案25】:

                          我遇到了完全相同的问题。这是我的解决方法

                          这个代码应该作为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);
                          

                          【讨论】:

                            【解决方案26】:

                            虽然问题有点老了,但问题仍然存在。我在 Xcode 7 中解决了这个问题,方法是将最上面的项目(在本例中为 topLabel)到超级视图(scrollView)的垂直空间约束设置为顶部 IBOutlet,然后重新计算其每次内容根据 scrollView 的子视图(topLabelbottomLabel)的高度而变化时保持不变。

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

                            【讨论】:

                              猜你喜欢
                              • 2020-08-12
                              • 2012-02-17
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2012-03-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              相关资源
                              最近更新 更多