UIImageView的Aspect Fit对齐方式

86

对于我的UIImageView,我选择了Aspect Fit(在InterfaceBuilder中),但是如何更改垂直对齐方式呢?


1
尝试使用ImageView的top和bottom属性。 - rithik
当你设置了Aspect Fit时,它无法正常工作。 - Ethan Allen
7
有一个在GitHub上的项目叫做UIImageViewAligned,它完美地实现了这一点。 - Daniel Storm
12个回答

41

[编辑 - 这段代码有些陈旧,因为它是2011年的,但我已经合并了@ArtOfWarefare的修改]

你不能使用UIImageView来完成这个操作。我创建了一个简单的UIView子类MyImageView,其中包含一个UIImageView。下面是代码。

// MyImageView.h
#import <UIKit/UIKit.h>

@interface MyImageView : UIView {
    UIImageView *_imageView;
}

@property (nonatomic, assign) UIImage *image;

@end

// MyImageView.m

#import "MyImageView.h"

@implementation MyImageView

@dynamic image;

- (id)initWithCoder:(NSCoder*)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        self.clipsToBounds = YES;
        _imageView = [[UIImageView alloc] initWithFrame:self.bounds];
        _imageView.contentMode = UIViewContentModeScaleAspectFill;
        [self addSubview:_imageView];
    }
    return self;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.clipsToBounds = YES;
        _imageView = [[UIImageView alloc] initWithFrame:self.bounds];
        _imageView.contentMode = UIViewContentModeScaleAspectFill;
        [self addSubview:_imageView];
    }
    return self;
}

- (id)initWithImage:(UIImage *)anImage
{
    self = [self initWithFrame:CGRectZero];
    if (self) {
        _imageView.image = anImage;
        [_imageView sizeToFit];

        // initialize frame to be same size as imageView
        self.frame = _imageView.bounds;        
    }
    return self;
}

// Delete this function if you're using ARC
- (void)dealloc
{
    [_imageView release];
    [super dealloc];
}

- (UIImage *)image
{
    return _imageView.image;
}

- (void)setImage:(UIImage *)anImage
{
    _imageView.image = anImage;
    [self setNeedsLayout];
}

- (void)layoutSubviews
{
    if (!self.image) return;

    // compute scale factor for imageView
    CGFloat widthScaleFactor = CGRectGetWidth(self.bounds) / self.image.size.width;
    CGFloat heightScaleFactor = CGRectGetHeight(self.bounds) / self.image.size.height;

    CGFloat imageViewXOrigin = 0;
    CGFloat imageViewYOrigin = 0;
    CGFloat imageViewWidth;
    CGFloat imageViewHeight;


    // if image is narrow and tall, scale to width and align vertically to the top
    if (widthScaleFactor > heightScaleFactor) {
        imageViewWidth = self.image.size.width * widthScaleFactor;
        imageViewHeight = self.image.size.height * widthScaleFactor;
    }

    // else if image is wide and short, scale to height and align horizontally centered
    else {
        imageViewWidth = self.image.size.width * heightScaleFactor;
        imageViewHeight = self.image.size.height * heightScaleFactor;
        imageViewXOrigin = - (imageViewWidth - CGRectGetWidth(self.bounds))/2;
    }

    _imageView.frame = CGRectMake(imageViewXOrigin,
                                  imageViewYOrigin,
                                  imageViewWidth,
                                  imageViewHeight);
}

- (void)setFrame:(CGRect)frame
{
    [super setFrame:frame];
    [self setNeedsLayout];
}

@end

3
为了使代码正常工作,我需要做两件小事情:1- 我实现了 initWithCoder 方法(基本上是复制了您的 initWithFrame 方法,但将 [super initWithFrame:] 替换为 [super initWithCoder:]); 2- 在 layoutSubviews 中添加了一行代码 if (!self.image) return;,这样在调用时如果没有分配图像,它就不会因为无效的几何形状而崩溃。通过这两个小改动,它能够惊人地工作。 - ArtOfWarfare
附注:使用ARC后,必须删除dealloc方法。 - Raptor

36
如果使用Storyboard,可以通过约束来实现...
首先创建一个UIView,并设置最终帧/约束。在UIView中添加UIImageView,并将其contentMode设置为Aspect Fill。使UIImageView的帧与图像具有相同的宽高比(这样可以避免后来的Storyboard警告)。使用标准约束将其固定在UIView上的两侧。将其顶部或底部(取决于对齐位置)使用标准约束固定到UIView上。最后,在UIImageView中添加一个宽高比约束(确保其与图像的宽高比相同)。

@Michael Platt,听起来很混乱,所以这在iOS 7中无法工作,因为您需要等效的纵横比,这仅适用于iOS 8+?您有任何示例代码吗? - Özgür
2
我也已经实现了这个功能,它可以正常工作。可能会感到困惑,因为解决方案描述了自动布局约束设置。对于其他开发人员,我的实现使用从网络加载的图像,因此我必须根据图像的宽度/高度以编程方式删除并重新添加UIImageView纵横比约束。 - Michael DePhillips
3
我使用了@MichaelPlatt的解决方案创建了UIImageView扩展。https://gist.github.com/megimix/d0bbea45bd3e1e4d615fbd5c30b54da7 - megimix

21

由于已选择了内容模式(.scaleAspectFit),因此没有设置进一步对齐规则的选项,这有点棘手。

但是,这里有一个解决方法:

首先,需要通过计算尺寸(如果它在具有contentMode = .scaleAspectFitUIImageView中)来明确调整源图像的大小。

extension UIImage {

    func aspectFitImage(inRect rect: CGRect) -> UIImage? {
        let width = self.size.width
        let height = self.size.height
        let aspectWidth = rect.width / width
        let aspectHeight = rect.height / height
        let scaleFactor = aspectWidth > aspectHeight ? rect.size.height / height : rect.size.width / width

        UIGraphicsBeginImageContextWithOptions(CGSize(width: width * scaleFactor, height: height * scaleFactor), false, 0.0)
        self.draw(in: CGRect(x: 0.0, y: 0.0, width: width * scaleFactor, height: height * scaleFactor))

        defer {
            UIGraphicsEndImageContext()
        }

        return UIGraphicsGetImageFromCurrentImageContext()
    }
}

然后,您只需要通过传递imageView的框架来调用此函数,并将结果分配给UIImageView.image属性。另外,请确保在此处设置您的imageView所需的contentMode甚至可以在Interface Builder中设置)!

let image = UIImage(named: "MySourceImage")
imageView.image = image?.aspectFitImage(inRect: imageView.frame)
imageView.contentMode = .left

3
尝试设置:

imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.clipsToBounds = YES;

这对我很有效。

3
我使用了UIImageViewAligned来改变图像的对齐方式,这要感谢开发者。

UIImageViewAligned


2
我知道这是一个旧的帖子,但我想分享一下我在界面生成器中轻松更改图像视图剪裁区域的方法,以防有人遇到与我相同的问题。我有一个UIImageView填充了我的ViewController的视图,并试图使其顶部保持不变,独立于设备屏幕的大小。
1. 我应用了Retina 4形式因子(Editor->应用Retina 4形式因子)。 2. 我固定了高度和宽度。
现在,当屏幕大小发生变化时,UIImageView实际上是相同的大小,视图控制器只是剪切屏幕外的内容。框架原点保持在0,0处,因此图像的底部和右侧被剪切,而不是顶部。
希望这可以帮助你。

2
我想到了以下解决方案:
  1. UIImageView的内容模式设置为topimageView.contentMode = .top
  2. 调整图像大小以适应UIImageView的边界

我使用Kingfisher来加载和调整图像大小:

let size = imageView.bounds.size
let processor = ResizingImageProcessor(referenceSize: size, mode: .aspectFit)
imageView.kf.setImage(with: URL(string: imageUrl), options: [.processor(processor), .scaleFactor(UIScreen.main.scale)])

这个可以工作,但图像会变得模糊。我通过使用设备屏幕大小和设备缩放因子来修复它,像这样:let processor = ResizingImageProcessor(referenceSize: view.frame.size, mode: .aspectFit); imageView.kf.setImage(with: url, options:[.processor(processor), .scaleFactor(UIScreen.main.scale)]) - Alexander Maslew
太棒了。谢谢,伙计。我想要左对齐,这个方法起作用了。 - Kunal Gupta

1

如果您需要实现aspectFit并去除空白区域

不要忘记从storyboard中删除图像视图的宽度约束,然后享受它吧

class SelfSizedImageView :UIImageView {
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        guard let imageSize = image?.size else {
            return
        }
        
        let viewBounds = bounds
        let imageFactor = imageSize.width / imageSize.height
        let newWidth = viewBounds.height * imageFactor
        
        let myWidthConstraint = self.constraints.first(where: { $0.firstAttribute == .width })
        myWidthConstraint?.constant = min(newWidth, UIScreen.main.bounds.width / 3)
        
        layoutIfNeeded()
    }}

最好的情况是当设置“.image”时,您也会这样做,并且非常理想的情况是正确地操作“IntrinsicContentSize”。 - Fattie

1

你可以先进行缩放,然后再调整大小。

需要注意的是,我是按照高度来设定的。也就是说,无论宽度如何,我都必须将图像的高度设置为34像素。

因此,需要获取实际内容高度与视图高度(34像素)之间的比例,然后同时缩放宽度。

以下是我的做法:

CGSize size = [imageView sizeThatFits:imageView.frame.size];

CGSize actualSize;
actualSize.height = imageView.frame.size.height;
actualSize.width = size.width / (1.0 * (size.height / imageView.frame.size.height));

CGRect frame = imageView.frame;
frame.size = actualSize;
[imageView setFrame:frame];

希望这能有所帮助。

0

感谢 @michael-platt

关键点

  • imageView.contentMode = .scaleAspectFit
  • let width = Layout.height * image.size.width / image.size.height
  • imageView.widthAnchor.constraint(equalToConstant: width).isActive = true

代码:

func setupImageView(for image: UIImage) {
    let imageView = UIImageView()
    imageView.backgroundColor = .orange

    //Content mode
    imageView.contentMode = .scaleAspectFill
    
    view.addSubview(imageView)

    //Constraints
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    imageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true

    //Feel free to set the height to any value
    imageView.heightAnchor.constraint(equalToConstant: 150).isActive = true

    //ImageView width calculation
    let width = Layout.height * image.size.width / image.size.height
    imageView.widthAnchor.constraint(equalToConstant: width).isActive = true
}

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