使UIImageView与Aspect Fit对齐


Answers:


40

[编辑-此代码从2011年开始有点发霉,除了我之外,我都加入了@ArtOfWarefare的mods]

使用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 -我在一条线上加入if (!self.image) return;layoutSubviews这样它不会与无效的几何体崩溃,如果图像ISN” t在调用时分配。通过这两个小的更改,它的运行效果惊人。
ArtOfWarfare 2013年

旁注:对于ARC,dealloc必须删除方法。
猛禽2014年

35

如果使用情节提要,则可以在限制条件下实现...

首先,UIView具有所需的最终框架/约束。将UIImageView添加到UIView。将contentMode设置为Aspect Fill。使UIImageView框架与图像具有相同的比率(这避免了以后出现任何Storyboard警告)。使用标准约束将侧面固定到UIView。使用标准约束将顶部或底部(取决于您希望对齐的位置)固定到UIView。最后,将长宽比约束添加到UIImageView(确保将长宽比作为图像)。


@Michael Platt,这听起来很令人困惑,因此这在iOS 7中不起作用,因为您需要等比例的宽高比,而这在iOS 8+中是可用的?您有示例代码吗?
Özgür的

2
我也已经实现了,它可以工作。由于该解决方案描述了自动布局约束设置,因此似乎令人困惑。对于其他开发人员,我的实现使用了从Web加载的图像,因此我不得不根据图像的宽度/高度以编程方式删除并重新添加UIImageView宽高比约束。
Michael DePhillips,2015年

3
我使用@MichaelPlatt解决方案创建了UIImageView扩展。gist.github.com/megimix/d0bbea45bd3e1e4d615fbd5c30b54da7
megimix

15

这有点棘手,因为如果您已经选择了内容模式(.scaleAspectFit),则无法设置进一步的对齐规则。

但这是一种解决方法:

首先,需要通过计算尺寸来显式地调整源图像的大小(如果它在UIImageViewwith中contentMode = .scaleAspectFit)。

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属性,即可在原始图像上调用此函数。另外,请确保contentMode在此处(甚至在Interface Builder中)设置所需的imageView !

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

4

尝试设置:

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

这对我有用。



2

我知道这是一个旧线程,但是我想与大家分享我为方便地在Interface Builder中从图像视图的顶部到底部更改剪切区域所做的工作,以防有人遇到与我相同的问题。我有一个UIImageView填充了ViewController的View,并试图使顶部保持不变,而与设备屏幕的大小无关。

  1. 我应用了视网膜4尺寸(编辑器->应用视网膜4尺寸)。

  2. 我固定了高度和宽度。

现在,当屏幕更改大小时,UIImageView实际上是相同的大小,并且视图控制器只是剪切掉屏幕上的内容。帧原点保持在0,0,因此图像的底部和右侧被裁剪,而不是顶部。

希望这可以帮助。


1

您可以先缩放然后再调整大小。这里要提到的是我受身高限制。我的意思是,无论宽度如何,我都必须拥有34px高的图像。

因此,获取实际内容高度与视图高度(34 px)之间的比率,然后再缩放宽度。

这是我的做法:

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

希望这可以帮助。


1

我想到了以下解决方案:

  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,

太棒了 多谢,伙计。我想向左对齐,这
很有用

0

我通过子类化UIImageView并重写setImage:方法解决了此问题。子类将首先存储其原始值以作为原点和大小,以便可以将原始设置的大小用作边界框。

我将内容模式设置为UIViewContentModeAspectFit。在setImage内部:我抓取图像的宽高比,然后调整图像视图的大小以适合与图像相同的比例。调整大小后,我调整了帧属性以将图像视图设置为与以前相同的位置,然后调用了超级setImage:。

这会导致对图像视图的帧进行调整以完全适合图像,因此,宽高比拟合工作正常,并且图像视图帧的属性在将图像视图放置在应具有相同效果的位置上起了很大的作用。

这是我使用的一些代码:

首先,我发现它在总体上很有用,它是UIView上的一个类别,它可以通过诸如左,右,上,下,宽度,高度等属性轻松在视图上设置框架属性。

UIImageView + FrameAdditions

@interface UIView (FrameAdditions)

@property CGFloat left, right, top, bottom, width, height;
@property CGPoint origin;

@end

@implementation UIView (FrameAdditions)

- (CGFloat)left {
    return self.frame.origin.x;
}

- (void)setLeft:(CGFloat)left {
    self.frame = CGRectMake(left, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
}

- (CGFloat)right {
    return self.frame.origin.x + self.frame.size.width;
}

- (void)setRight:(CGFloat)right {
    self.frame = CGRectMake(right - self.frame.size.width, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
}

- (CGFloat)top {
    return self.frame.origin.y;
}

- (void)setTop:(CGFloat)top {
    self.frame = CGRectMake(self.frame.origin.x, top, self.frame.size.width, self.frame.size.height);
}

- (CGFloat)bottom {
    return self.frame.origin.y + self.frame.size.height;
}

- (void)setBottom:(CGFloat)bottom {
    self.frame = CGRectMake(self.frame.origin.x, bottom - self.frame.size.height, self.frame.size.width, self.frame.size.height);
}

- (CGFloat)width {
    return self.frame.size.width;
}

- (void)setWidth:(CGFloat)width {
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, width, self.frame.size.height);
}

- (CGFloat)height {
    return self.frame.size.height;
}

- (void)setHeight:(CGFloat)height {
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, height);
}

- (CGPoint)origin {
    return self.frame.origin;
}

- (void)setOrigin:(CGPoint)origin {
    self.frame = CGRectMake(origin.x, origin.y, self.frame.size.width, self.frame.size.height);
}

@end

这是UIImageView的子类。它尚未经过全面测试,但应该可以使您理解。可以将其扩展为设置自己的新对齐方式。

BottomCenteredImageView

@interface BottomCenteredImageView : UIImageView

@end


@interface BottomCenteredImageView() {
    CGFloat originalLeft;
    CGFloat originalBottom;
    CGFloat originalHeight;
    CGFloat originalWidth;
}

@end

@implementation BottomCenteredImageView

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if(self) {
        [self initialize];
    }
    return self;
}

- (void)awakeFromNib {
    [self initialize];
}

- (void)initialize {
    originalLeft = self.frame.origin.x;
    originalHeight = CGRectGetHeight(self.frame);
    originalWidth = CGRectGetWidth(self.frame);
    originalBottom = self.frame.origin.y + originalHeight;
}

- (void)setImage:(UIImage *)image {
    if(image) {
        self.width = originalWidth;
        self.height = originalHeight;
        self.left = originalLeft;
        self.bottom = originalBottom;

        float myWidthToHeightRatio = originalWidth/originalHeight;
        float imageWidthToHeightRatio = image.size.width/image.size.height;
        if(myWidthToHeightRatio >= imageWidthToHeightRatio) {
            // Calculate my new width
            CGFloat newWidth = self.height * imageWidthToHeightRatio;
            self.width = newWidth;
            self.left = originalLeft + (originalWidth - self.width)/2;
            self.bottom = originalBottom;
        } else {
            // Calculate my new height
            CGFloat newHeight = self.width / imageWidthToHeightRatio;
            self.height = newHeight;
            self.bottom = originalBottom;
        }
        self.contentMode = UIViewContentModeScaleAspectFit;
        [super setImage:image];
    } else {
        [super setImage:image];
    }
}

@end

不要在VC中使用UIImageView,而要使用BottomCenteredImageView。
slemke

这对我不起作用...它改变了我的视图的纵横比以匹配我的图像的纵横比,这不是我想要的... XJones提供的答案与我在注解中指出的一些变化完美地结合在一起他的回答下方有评论。
ArtOfWarfare 2013年

0

如果您需要实现AspectFit并消除空白

不要忘记从情节提要中删除imageview的宽度限制并享受

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

-6

要对齐缩放比例以适合图像,请使用自动布局。在UIImageView中缩放以适合后的示例右对齐图像:

  1. 创建保存图像的UIImageView
  2. 添加右侧,顶部,底部,宽度的自动约束
  3. 设置图像:
myUIImageView.contentMode = .ScaleAspectFit
myUIImageView.translatesAutoresizingMaskIntoConstraints = false
myUIImageView.image = UIImage(named:"pizza.png")

您的宽高比缩放图像现在在UIImageView中右对齐

+----------------------------------+
|                           [IMAGE]|
+----------------------------------+

更改约束以在imageview中进行不同的对齐。


这不是很热,它行得通,如果您使用宽高比拟合,则对齐方式在很大程度上取决于图像的实际大小
Abhishek Maurya
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.