使用自动布局的UIScrollView和缩放UIImageView

11

UIScrollView with UIImageView

考虑一个带有一个子视图的UIScrollView。子视图是一个UIImageView,具有以下尺寸约束:

  • 它的高度必须等于UIScrollView的高度。
  • 其宽度必须是按照UIImageView的高度比例缩放后的图像宽度。

预期UIImageView的宽度将大于UIScrollView的宽度,因此需要滚动。

图像可能在viewDidLoad中设置(如果已缓存)或异步设置。

如何使用自动布局实现上述内容,并尽可能多地利用Interface Builder?

到目前为止我做了什么

基于这个答案,我将我的nib配置如下:

  • UIScrollView固定到其父视图的边缘。
  • UIImageView固定到UIScrollView的边缘。
  • UIImageView具有占位符内在大小(以避免可滚动内容大小歧义错误)

如预期的那样,结果是UIImageView的大小与UIImage的大小相同,并且UIScrollView水平和垂直滚动(因为图像大于UIScrollView)。

然后我尝试了一些没有效果的方法:

  • 手动加载图像后设置UIImageView框架。
  • 添加一个UIImageView的宽度约束并在加载图像后修改其值。这使图像变得更大(?!)。
  • 在加载图像后设置zoomScale。没有可见的效果。

没有自动布局

以下代码完全符合我的要求,但没有使用自动布局或界面生成器。

- (void)viewDidLoad
{
    {
        UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
        scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        [self.view addSubview:scrollView];
        self.scrollView = scrollView;
    }

    {
        UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height)];
        imageView.contentMode = UIViewContentModeScaleAspectFit;
        imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        [self.scrollView addSubview:imageView];
        self.scrollView.contentSize = imageView.frame.size;
        self.imageView = imageView;
    }
}

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    [self layoutStripImageView];
}

- (void)layoutStripImageView
{ // Also called when the image finishes loading
    UIImage *image = self.imageView.image;
    if (! image) return;

    const CGSize imageSize = image.size;
    const CGFloat vh = self.scrollView.frame.size.height;
    const CGFloat scale = vh / imageSize.height;
    const CGFloat vw = imageSize.width * scale;

    CGSize imageViewSize = CGSizeMake(vw, vh);
    self.imageView.frame = CGRectMake(0, 0, imageViewSize.width, imageViewSize.height);
    self.scrollView.contentSize = imageViewSize;
}

我很努力地尝试使用自动布局,但这并不容易。

3个回答

11

在自动布局下,理想情况下,contentSize 应该只由约束条件决定,而不是在代码中显式设置。

因此,在您的情况下:

  • 创建将子视图固定到 UIScrollView 的约束条件。 约束条件必须确保子视图和滚动视图之间的边距为 0。我看到您已经尝试过这个方法。

  • 为您的子视图创建高度和宽度约束条件。 否则,UIImageView 的内在大小决定其高度和宽度。在设计时,此大小仅是一个占位符,以使 Interface Builder 正常工作。在运行时,它将被设置为实际图像大小,但这不是您想要的。

  • viewDidLayoutSubviews 中更新约束条件为实际内容大小。 您可以通过直接更改高度和宽度约束的 constant 属性来完成这一点,或者调用 setNeedsUpdateConstraints 并重写 updateConstraints 来执行相同操作。

这样可以确保系统仅通过约束条件推导出 contentSize

我已经尝试了上述方法,并在 iOS 6 和 7 上使用 UIScrollView 和自定义子视图可靠地运行,因此它也适用于 UIImageView。特别是如果您不将子视图固定到滚动视图中,在 iOS 6 中缩放会出现抖动。

您还可以尝试创建直接引用滚动视图高度和宽度倍数的高度和宽度约束条件,但我还没有尝试过这种方法。


非常感谢!设置高度和宽度约束对我很有帮助。 - Enrico Susatyo

2

translatesAutoresizingMaskIntoConstraints 只在您在代码中实例化视图时需要使用。如果您在IB中实例化它,则默认情况下已禁用。

在我看来,UIImageView 应该填充 ScrollView。稍后,我会尝试设置 scrollview 的缩放值,以适合您的需要,这样图片只能在一个方向上移动。


我尝试过了。zoomScaleUIScrollView 没有影响。 - hpique
зЉЦиЊСдЇЖйЧЃйҐШдї•еИ†йЩ§translatesAutoresizingMaskIntoConstraintsгАВ - hpique

1
在我的情况下,是一个具有定义的高度约束条件的全宽UIImageView导致了问题。
我在UIImageView上设置了另一个约束条件,使其宽度与UIScrollView在界面构建器中的宽度匹配,然后在UIViewController中添加了一个outlet。
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *imageViewWidthConstraint;

然后在viewDidLayoutSubviews中,我更新了约束条件:

- (void) viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    self.imageViewWidthConstraint.constant = CGRectGetWidth(self.scrollView.frame);
}

这似乎达到了效果。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接