使用drawInRect时的UIImage纵横比适应问题?

33

UIView提供了“等比缩放”内容模式。但是,我已经继承了UIView,并想使用drawInRect以等比例缩放的方式绘制UIImage。是否有任何方法可以让我在不使用UIImageView的情况下访问该功能?

UIView提供了“等比例缩放”(aspect fit)内容模式。但是,如果您已经子类化了UIView并希望使用drawInRect按照等比例缩放的方式绘制UIImage,则是否有任何方法可以在不使用UIImageView的情况下访问该功能呢?
6个回答

41

那个的数学计算有点复杂,但幸运的是,我之前准备过一种解决方案:

- (UIImage *)imageScaledToSize:(CGSize)size
{
    //create drawing context
    UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);

    //draw
    [self drawInRect:CGRectMake(0.0f, 0.0f, size.width, size.height)];

    //capture resultant image
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

- (UIImage *)imageScaledToFitSize:(CGSize)size
{
    //calculate rect
    CGFloat aspect = self.size.width / self.size.height;
    if (size.width / aspect <= size.height)
    {
        return [self imageScaledToSize:CGSizeMake(size.width, size.width / aspect)];
    }
    else
    {
        return [self imageScaledToSize:CGSizeMake(size.height * aspect, size.height)];
    }
}
第一个函数相当于“缩放到填充”,第二个函数(调用了第一个函数)相当于“等比例缩放适应”。这些方法被写成了UIImage的分类,所以如果要从另一个类中使用它们,您需要通过将图像作为第二个参数传递来进行调整 (或者像我一样创建一个UIImage的分类)。

1
我意识到我有点在回复一个已经死掉的帖子,但是:为什么图形上下文的比例要用0.0f? - Groxx
11
如果您使用0.0,那么它将自动选择1.0或2.0,这取决于设备是否具有Retina屏幕。 - Nick Lockwood
1
太棒了,确实如此。谢谢!我以前从未听说过这个。我想我应该更经常地阅读文档 :) - Groxx
@Antzi的答案现在不是更好吗,在那里推荐使用AVMakeRectWithAspectRatioInsideRect(_ aspectRatio,而手动调整大小是否仍然需要呢? - Crashalot
@Crashalot 哈,我从来不知道那个函数。今天我学到了。 - Nick Lockwood
1
我最喜欢的一句注释是:// 返回图像 - gblazex

37

您需要在提供的矩形中绘制。

Swift:

func AVMakeRect(aspectRatio: CGSize, insideRect boundingRect: CGRect) -> CGRect

Objective-C:

CGRect AVMakeRectWithAspectRatioInsideRect(CGSize aspectRatio, CGRect boundingRect);

请参阅文档

它能够保留纵横比并使绘图居中。

它适用于所有情况。


1
这绝对是最好的解决方案,只需记得在使用它时 import AVFoundation - Patru
这节省了我很多时间 - 谢谢!我在绘图上下文中为通知(UNNotificationAttachment)缩放SVG资源时遇到了问题;它们总是被裁剪或不能保持纵横比。我尝试了各种手动计算,但都无济于事。这个方法很有效! - Chris

14

这是解决方案

CGSize imageSize = yourImage.size;
CGSize viewSize = CGSizeMake(450, 340); // size in which you want to draw

float hfactor = imageSize.width / viewSize.width;
float vfactor = imageSize.height / viewSize.height;

float factor = fmax(hfactor, vfactor);

// Divide the size by the greater of the vertical or horizontal shrinkage factor
float newWidth = imageSize.width / factor;
float newHeight = imageSize.height / factor;

CGRect newRect = CGRectMake(xOffset,yOffset, newWidth, newHeight);
[image drawInRect:newRect];

-- 来自 https://dev59.com/oHI-5IYBdhLWcg3wu7FU#1703210 的解答

一个关于等比缩放的替代方案

-(CGSize ) getImageSize :(CGSize )imgViewSize andActualImageSize:(CGSize )actualImageSize {

     // imgViewSize = size of view in which image is to be drawn
     // actualImageSize = size of image which is to be drawn

     CGSize drawImageSize;

     if (actualImageSize.height > actualImageSize.width) {

        drawImageSize.height = imgViewSize.height;
        drawImageSize.width = actualImageSize.width/actualImageSize.height * imgViewSize.height;

     }else {

        drawImageSize.width = imgViewSize.width;
        drawImageSize.height = imgViewSize.width * actualImageSize.height /  actualImageSize.width;
     }
     return drawImageSize;
}

Swift 3.0 的代码:

func getAspectFitFrame(sizeImgView:CGSize, sizeImage:CGSize) -> CGRect{

    let imageSize:CGSize  = sizeImage
    let viewSize:CGSize = sizeImgView

    let hfactor : CGFloat = imageSize.width/viewSize.width
    let vfactor : CGFloat = imageSize.height/viewSize.height

    let factor : CGFloat = max(hfactor, vfactor)

    // Divide the size by the greater of the vertical or horizontal shrinkage factor
    let newWidth : CGFloat = imageSize.width / factor
    let newHeight : CGFloat = imageSize.height / factor

    var x:CGFloat = 0.0
    var y:CGFloat = 0.0
    if newWidth > newHeight{
        y = (sizeImgView.height - newHeight)/2
    }
    if newHeight > newWidth{
        x = (sizeImgView.width - newWidth)/2
    }
    let newRect:CGRect = CGRect(x: x, y: y, width: newWidth, height: newHeight)

    return newRect

}

4

这里有一个CGSize的扩展帮助类,您可能会发现它很有用:

extension CGSize
{
    func sizeThatFitsSize(_ aSize: CGSize) -> CGSize
    {
        let width = min(self.width * aSize.height / self.height, aSize.width)
        return CGSize(width: width, height: self.height * width / self.width)
    }
}

-drawInRect:的实现中,我们应该做以下操作:

let imageSizeThatFitsViewBounds = self.image.size.sizeThatFitsSize(self.bounds.size)
let imageRectX = (self.bounds.size.width - imageSizeThatFitsViewBounds.width) / 2
let imageRectY = (self.bounds.size.height - imageSizeThatFitsViewBounds.height) / 2
let imageRect = CGRect(origin: CGPoint(x: imageRectX, y: imageRectY), size: imageSizeThatFitsViewBounds)

2

这可能对您有所帮助,请试一试

UIImage *logoImage = [UIImage imageNamed:@"logo.png"];

float newHeight = ((logoImage.size.height / logoImage.size.width) * 100.0);
CGRect newLogoImageRect = CGRectMake(0, 0, 80.0, newHeight);
[logoImage drawInRect:newLogoImageRect];

请将100.0替换为您的最大高度,80.0替换为您的最大宽度。


1

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