【问题标题】:Scrolling, zooming UIScrollView and interface orientation rotation. How to use autoresize and more滚动、缩放 UIScrollView 和界面方向旋转。如何使用自动调整大小等
【发布时间】:2025-11-29 15:25:01
【问题描述】:

这应该是很常见的事情,但我无法让它完全正确地工作。

我有矩形内容。它通常适合 320x361:纵向模式减去状态栏减去广告减去标签栏。

我已将该内容放入 UIScrollView 并启用缩放。我也希望界面旋转工作。内容将始终是一个高矩形,但在缩放时,用户可能希望一次看到更大的宽度和更少的高度。

我需要在 Interface Builder 和代码中做什么才能完成这项工作?我应该如何在不同的视图上设置我的自动调整大小?如何设置我的 contentSize 和 contentInsets?

我尝试了很多不同的方法,但没有一个是完全正确的。在我的各种解决方案中,我遇到了一些缩放、界面旋转和滚动组合的问题,它不再可能滚动到屏幕上的整个内容。在您看到内容的边缘之前,滚动视图会弹回来。

我现在的做法是正确的大约 80%。也就是说,在它应该做的 10 件事中,它做了 8 件事。它做错的两件事是:

  1. 在纵向模式下放大时,您可以滚动到内容的边缘,然后看到黑色背景。这没什么好抱怨的。至少你可以看到所有的内容。在横向模式下,无论是否缩放,看到超出边缘的黑色背景都是正常的,因为内容没有足够的宽度以 1:1 缩放级别(最小值)填充屏幕。

  2. 在运行 iOS 3.0 的测试设备上运行时,我的内容仍然卡在边缘,但它在运行 4.x 的我的设备上运行。 - 实际上,这是与以前的解决方案。我的测试人员没有尝试过最新的解决方案。

这是我目前正在使用的解决方案。总而言之,我已将滚动视图设置为任意方向所需的宽度和高度,因为我发现手动或自动调整它的大小会增加复杂性并且很脆弱。

查看层次结构:

  • 查看
    • 滚动查看
      • 可滚动区域
        • 内容
    • 广告

视图是 320x411 并且有所有的自动调整大小选项,所以符合屏幕形状

scrollView 为 480 x 361,从原点 -80,0 开始,仅锁定到顶部并禁用拉伸

scrollableArea 为 480 x 361,并锁定在左侧和顶部。由于 scrollView 禁用了拉伸,其子视图的自动调整大小掩码无关紧要,但我还是告诉你。

内容为 320x361,从原点 80,0 开始,并锁定到顶部

我将 scrollView.contentSize 设置为 480x361。

shouldAutorotateToInterfaceOrientation 支持除纵向倒置以外的所有方向。

在 didRotateFromInterfaceOrientation 中,如果方向是横向,我将底部内容插入设置为 160,否则重置为 0。如果方向是纵向,我将左右 indicator 设置为 80,如果不是,则重置。

scrollView.minimumZoomScale = 1.0

scrollView.maximumZoomScale = 2.0

viewForZoomingInScrollView 返回可滚动区域

【问题讨论】:

    标签: iphone uiscrollview zooming landscape autoresize


    【解决方案1】:
    // in IB it would be all options activated
    scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    scrollView.contentSize = content.frame.size; // or bounds, try both
    

    scrollableArea 是什么意思?

    您的minZoomScale 设置为1.0,这对于纵向模式很好,但对于横向模式则不行。因为在横向您的高度小于纵向您需要有一个小于1.0 的值。对我来说,我使用这个实现并每次调用它,scrollView 的框架确实发生了变化:

    - (void)setMaxMinZoomScalesForCurrentBounds {
        CGSize boundsSize = self.bounds.size; // self is a UIScrollView here
        CGSize contentSize = content.bounds.size;
    
        CGFloat xScale = boundsSize.width / contentSize.width;
        CGFloat yScale = boundsSize.height / contentSize.height;
        CGFloat minScale = MIN(xScale, yScale);
    
        if (self.zoomScale < minScale) {
            [self setZoomScale:minScale animated:NO];
        }
        if (minScale<self.maximumZoomScale) self.minimumZoomScale = minScale;
        //[self setZoomScale:minScale animated:YES];
    }
    
    - (void)setFrame:(CGRect)rect { // again, this class is a UIScrollView
        [super setFrame:rect];
        [self setMaxMinZoomScalesForCurrentBounds];
    }
    

    【讨论】:

    • 谢谢,看起来很有希望。所以你有子类 UIScrollView 它看起来像?
    • scrollableArea 只是一个普通的 UIView。它除了包含其他 UIView 之外什么都不做。这是我的viewForZoomingInScrollView。我需要在 scrollView 和 content 之间使用它,因为我设置了有趣的尺寸以使事情大致正常工作。我也需要它,因为偶尔它里面除了内容之外还有一个额外的视图。
    • 我曾想过最小缩放小于 1.0,但在横向模式下确实没有理由看到内容的完整高度以及侧面的大量黑色区域。界面将变得太小而无法使用。如果要查看全高,请使用纵向模式。我实际上考虑将最小缩放设置为更像 1.5 以用于横向,以便内容始终至少填满屏幕。
    • 那么contentSize 应该是您的可滚动区域的框架。是的,我已将其子类化,因为我有一些石英渲染 pdf。
    【解决方案2】:

    我不认为我从您的帖子中理解了整个问题,但这是我所理解的答案。

    据我所知(并使用 UIScrollView),UIScrollView 内的内容不会与 UIScrollView 一起自动自动调整大小。

    将 UIScrollView 视为您的内容所在的另一个世界的窗口/门户。自动调整 UIScrollView 的大小时,您只是更改了查看窗口的形状/大小......而不是其他宇宙中内容的大小。

    但是,如果需要,您可以拦截旋转事件并手动更改您的内容(使用动画使其看起来不错)。

    要获得正确的自动调整大小,您应该更改 scrollView 的 contentSize(以便它知道您的 Universe 的大小),同时还要更改 UIView 的大小。我认为这就是您能够滚动并获得黑色内容的原因。也许您刚刚更新了 contentSize,但现在是实际的内容视图。

    就我个人而言,我还没有遇到任何需要与 UIScrollView 一起调整内容大小的情况,但我希望这能让你朝着正确的方向开始。

    【讨论】:

    • 为了澄清你的答案,这是否意味着打开 view 和 scrollView 的所有 autoresize 栏,并为 scrollableArea 关闭它们?
    • 我试过了,将我的所有尺寸设置回正常尺寸,并删除了我的自定义代码,这些代码正在执行诸如设置内容插入之类的操作。我遇到了一些问题。 1 当我旋转到横向时,我的内容很好地居中,但我无法一直滚动到内容的顶部。 2 如果我缩放,然后旋转到横向,我可以到达所有的内容,但如果我缩小内容不居中;它一直在屏幕的左侧。 3 如果我在横向放大,然后返回纵向,我无法到达所有内容。通过多个步骤,我什至将内容从屏幕上移开。
    【解决方案3】:

    如果我理解正确的话,您想要一个带有图像的滚动视图。它需要全屏开始,并且您需要能够放大。最重要的是,您希望它能够根据方向旋转。

    好吧,我过去一直在对此进行原型设计,如果以上所有内容都正确,那么以下代码应该适合您。

    我为栏/自定义栏留了一点白色区域。

    - (void)viewDidLoad
    {  
        [super viewDidLoad];
    
        //first inits and allocs
        scrollView2 = [[UIScrollView alloc] initWithFrame:self.view.frame];
        imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"someImageName"]];
        [scrollView2 addSubview:imageView];
        [self drawContent]; //refreshing the content
        [self.view addSubview:scrollView2];
    }
    
    -(void)drawContent
    {
        //this refreshes the screen to the right sizes and zoomscales.
        [scrollView2 setBackgroundColor:[UIColor blackColor]];
        [scrollView2 setCanCancelContentTouches:NO];
        scrollView2.clipsToBounds = YES;
        [scrollView2 setDelegate:self];
        scrollView2.indicatorStyle = UIScrollViewIndicatorStyleWhite;
    
        [scrollView2 setContentSize:CGSizeMake(imageView.frame.size.width, imageView.frame.size.height)];
        [scrollView2 setScrollEnabled:YES];
    
        float minZoomScale;
        float zoomHeight = imageView.frame.size.height / scrollView2.frame.size.height;
        float zoomWidth = imageView.frame.size.width / scrollView2.frame.size.width;
    
        if(zoomWidth > zoomHeight)
        {
            minZoomScale = 1.0 / zoomWidth;
        }
        else
        {
            minZoomScale = 1.0 / zoomHeight;
        }
    
        [scrollView2 setMinimumZoomScale:minZoomScale];
        [scrollView2 setMaximumZoomScale:7.5];
    }
    
    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
    {
        // Return YES for supported orientations
    
        if (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
    
            // Portrait 
            //the 88pxls is the white area that is left for the navbar etc.
    
            self.scrollView2.frame = CGRectMake(0, 88, [UIScreen mainScreen].bounds.size.width, self.view.frame.size.height - 88);
            [self drawContent];
        }
        else {
    
            // Landscape
            //the 88pxls is the white area that is left for the navbar etc.
    
            self.scrollView2.frame = CGRectMake(0, 88, [UIScreen mainScreen].bounds.size.height, self.view.frame.size.width);
            [self drawContent];
        }
        return YES;
    }
    
    - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
    {
        return self.imageView;
    }
    

    我希望这能解决您的问题。如果没有发表评论。

    【讨论】:

      【解决方案4】:

      当您想将内容(UIView 实例,我们称之为 theViewInstance )放在 UIScrollView 中,然后滚动/放大 theViewInstance 时,方法是:

      • theViewInstance 应该添加为UIScrollView 的子视图
      • UIScrollView 实例设置一个委托并实现选择器以返回应该用于缩放/滚动的视图:

        -(UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {  
              return theViewInstance;  
        } 
        
      • 默认设置UIScrollView的contentSize为theViewInstance的frame:

        scrollView.contentSize=theViewInstance.frame.size;
        

        (另外,可接受的缩放级别可以在 UIScrollView 中设置:)

        scrollView.minimumZoomScale=1.0;
        scrollView.maximumZoomScale=3.0;
        

      这是在 UIImage 上实现捏合缩放的方式:将 UIImageView 添加到 UIScrollView 并在 UIScrollViewDelegate 实现中返回 UIImageView(如 here 所述)。

      对于旋转支持,这是在 UIViewController 中完成的,它的 UIView 包含我们刚才谈到的 UIScrollView。

      【讨论】: