En boy oranını ve genişliğini tutarak UIImage'ı yeniden boyutlandırma


136

En boy oranını koruyarak görüntüyü yeniden boyutlandırmak için birçok gönderide gördüm. Bu işlevler yeniden boyutlandırırken RECT için sabit noktaları (Genişlik ve Yükseklik) kullanır. Ancak projemde, görünümü yalnızca Genişliğe göre yeniden boyutlandırmam gerekiyor, Yükseklik en boy oranına göre otomatik olarak alınmalıdır. bunu başarmam için bana yardım eden var.

Yanıtlar:


228

Her iki yüksekliğini biliyorsanız Srikar yöntemi, çok iyi çalışıyor ve yeni Boyutu genişliğini. Örneğin, yalnızca ölçeklemek istediğiniz genişliği biliyorsanız ve yüksekliğe önem vermiyorsanız, öncelikle yüksekliğin ölçek faktörünü hesaplamanız gerekir.

+(UIImage*)imageWithImage: (UIImage*) sourceImage scaledToWidth: (float) i_width
{
    float oldWidth = sourceImage.size.width;
    float scaleFactor = i_width / oldWidth;

    float newHeight = sourceImage.size.height * scaleFactor;
    float newWidth = oldWidth * scaleFactor;

    UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight));
    [sourceImage drawInRect:CGRectMake(0, 0, newWidth, newHeight)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();
    return newImage;
}

22
newWidthGerçi burada gerçekten değişkene ihtiyacınız yok , zaten i_width.
Matthew Quiros

2
Yüksekliğe de bölmeniz ve ölçekleme faktörlerini 0'a yakın olanı kullanarak karşılaştırmanız gerekir. Yukarıdaki kod yalnızca yüksek olduklarından daha geniş resimlerle çalışacaktır
IceForge

1
Yükseklik oranımı da bilmek istersem yüksekliğe bölmem ve genişliği veya yüksekliği ölçeklendirmem gerekir. ancak TO açıkça genişlik veya yükseklik değil oran ve genişliği korumasını istediğinden, cevabım tamamen doğrudur.
Maverick1st

12
Harika cevap, teşekkürler. Bir şey daha: Bu yöntemi kullanarak görüntü kalitesi kaybı yaşarsanız, o zaman kullanmak UIGraphicsBeginImageContextWithOptions(CGSizeMake(newWidth, newHeight), NO, 0);yerineUIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight));
skornos

57

Görüntünün dikey mi yoksa yatay mı olacağını bilmiyorsanız (örneğin, kullanıcı kamerayla resim çeker), maksimum genişlik ve yükseklik parametrelerini alan başka bir yöntem oluşturdum.

Eğer bir kullandığınızı varsayalım UIImage *myLargeImage3 oranı: 4 olan.

UIImage *myResizedImage = [ImageUtilities imageWithImage:myLargeImage 
                                        scaledToMaxWidth:1024 
                                               maxHeight:1024];

Yeniden boyutlandırılan UIImage, yatay ise 1024x768 olacaktır; Portre ise 768x1024. Bu yöntem ayrıca retina görüntüleme için daha yüksek çözünürlüklü görüntüler üretecektir.

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)size {
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        UIGraphicsBeginImageContextWithOptions(size, NO, [[UIScreen mainScreen] scale]);
    } else {
        UIGraphicsBeginImageContext(size);
    }
    [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();

    return newImage;
}

+ (UIImage *)imageWithImage:(UIImage *)image scaledToMaxWidth:(CGFloat)width maxHeight:(CGFloat)height {
    CGFloat oldWidth = image.size.width;
    CGFloat oldHeight = image.size.height;

    CGFloat scaleFactor = (oldWidth > oldHeight) ? width / oldWidth : height / oldHeight;

    CGFloat newHeight = oldHeight * scaleFactor;
    CGFloat newWidth = oldWidth * scaleFactor;
    CGSize newSize = CGSizeMake(newWidth, newHeight);

    return [ImageUtilities imageWithImage:image scaledToSize:newSize];
}

1
Bu en iyi şey ama her zaman tanımladığım boyutun iki katı geri geliyor
Mashhadi

2
Yalnızca küçültmek istiyorsanız, büyütmek istemiyorsanız, imageWithImage: scaledToMaxWidth: maxHeight: yöntemini şu yöntemle başlatmayı unutmayın: if (image.size.width <width && image.size.height <height) {return image; }
Arjan

5
Maksimum genişlik ve maksimum yüksekliğe sığacak için, ölçek faktörü olarak asgari oran gerekir: min(ratioX, ratioY). Aksi takdirde, genişlik daha büyükse, maksimum yüksekliğe uymayabilir.
Jason Sturges

ImageUtilities nerede?
tong

42

En iyi cevap Maverick 1st'in Swift'e doğru çevrilmesi (en son swift 3 ile çalışmak ):

func imageWithImage (sourceImage:UIImage, scaledToWidth: CGFloat) -> UIImage {
    let oldWidth = sourceImage.size.width
    let scaleFactor = scaledToWidth / oldWidth

    let newHeight = sourceImage.size.height * scaleFactor
    let newWidth = oldWidth * scaleFactor

    UIGraphicsBeginImageContext(CGSize(width:newWidth, height:newHeight))
    sourceImage.draw(in: CGRect(x:0, y:0, width:newWidth, height:newHeight))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage!
}

38

teşekkürler @ algoritma Mastick1st, ben uyguladım Swift, benim durumumda yüksekliği giriş parametresi

class func resizeImage(image: UIImage, newHeight: CGFloat) -> UIImage {

    let scale = newHeight / image.size.height
    let newWidth = image.size.width * scale
    UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight))
    image.drawInRect(CGRectMake(0, 0, newWidth, newHeight))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage
}

18

Bu yöntem UIImage'da bir kategoridir. AVFoundation kullanarak birkaç kod satırına sığacak şekilde ölçeklendirilir. İçe aktarmayı unutmayın #import <AVFoundation/AVFoundation.h>.

@implementation UIImage (Helper)

- (UIImage *)imageScaledToFitToSize:(CGSize)size
{
    CGRect scaledRect = AVMakeRectWithAspectRatioInsideRect(self.size, CGRectMake(0, 0, size.width, size.height));
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    [self drawInRect:scaledRect];
    UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return scaledImage;
}

@end

2
Bunu, en küçük bellek alanıyla benim için en iyi sonuçları vermek için buldu. +1
Tommie C.

11

@ János'un cevabını temel alarak boyuta göre boyuna sığdırma 5 versiyonu

Modern UIGraphicsImageRendererAPI'yı kullanır , bu nedenle geçerli bir UIImagedönüş garanti edilir.

extension UIImage
{
    /// Given a required height, returns a (rasterised) copy
    /// of the image, aspect-fitted to that height.

    func aspectFittedToHeight(_ newHeight: CGFloat) -> UIImage
    {
        let scale = newHeight / self.size.height
        let newWidth = self.size.width * scale
        let newSize = CGSize(width: newWidth, height: newHeight)
        let renderer = UIGraphicsImageRenderer(size: newSize)

        return renderer.image { _ in
            self.draw(in: CGRect(origin: .zero, size: newSize))
        }
    }
}

Bunu, herhangi bir oluşturma boyutunda kaliteyi korumak için (vektör tabanlı) PDF görüntü öğesiyle birlikte kullanabilirsiniz.


7

En basit yol, çerçevenizi UIImageViewayarlamak contentModeve yeniden boyutlandırma seçeneklerinden birine ayarlamaktır .

Kod şu şekilde gider - Bu, bir yardımcı program yöntemi olarak kullanılabilir -

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize
{
    UIGraphicsBeginImageContext(newSize);
    [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();
    return newImage;
}

OP'nin bir UIImageView'dan bahsetmediğini unutmayın. Bunun yerine, muhtemelen bir [U] görüntüsünün [kopyasını] doğrudan sığdırmak istedi, bu da CoreGraphics çalışması yaparken sahip olmak için çok kullanışlı bir şey.
Womble

6

Programlı:

imageView.contentMode = UIViewContentModeScaleAspectFit;

Storyboard:

resim açıklamasını buraya girin


ANCAK, bellek ayak izlerini azaltarak, uygulamanın daha hızlı çalışmasını sağlayabilir, örneğin çok sayıda küçük resim yükleyebilirsiniz.
ingconti

Bu bir UIImageView değil, bir UIImageView. Bazen, bir UIImageView olmadan çizim yapmanız gerekir.
Womble

5

Burada, görüntüyü yeniden boyutlandırmak için birkaç iyi cevabımız var, ancak ihtiyacım doğrultusunda değiştiriyorum. Umarım bu benim gibi birine yardım eder.

Gereksinim şuydu:

  1. Görüntü genişliği 1920'den büyükse, 1920 genişliğiyle yeniden boyutlandırın ve orijinal en boy oranıyla yüksekliği koruyun.
  2. Görüntü yüksekliği 1080'den yüksekse, 1080 yükseklikle yeniden boyutlandırın ve orijinal en boy oranıyla genişliği koruyun.

 if (originalImage.size.width > 1920)
 {
        CGSize newSize;
        newSize.width = 1920;
        newSize.height = (1920 * originalImage.size.height) / originalImage.size.width;
        originalImage = [ProfileEditExperienceViewController imageWithImage:originalImage scaledToSize:newSize];        
 }

 if (originalImage.size.height > 1080)
 {
            CGSize newSize;
            newSize.width = (1080 * originalImage.size.width) / originalImage.size.height;
            newSize.height = 1080;
            originalImage = [ProfileEditExperienceViewController imageWithImage:originalImage scaledToSize:newSize];

 }

+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize
{
        UIGraphicsBeginImageContext(newSize);
        [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
}

@ Srikar Appal sayesinde , yeniden boyutlandırmak için onun yöntemini kullandım.

Yeniden boyutlandırma hesaplaması için bunu da kontrol etmek isteyebilirsiniz .


4

Sadece AVFoundation'ı içe aktarın ve kullanın AVMakeRectWithAspectRatioInsideRect(CGRectCurrentSize, CGRectMake(0, 0, YOUR_WIDTH, CGFLOAT_MAX)


2

Ryan'ın cevabını geliştirmek için:

   + (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)size {
CGFloat oldWidth = image.size.width;
        CGFloat oldHeight = image.size.height;

//You may need to take some retina adjustments into consideration here
        CGFloat scaleFactor = (oldWidth > oldHeight) ? width / oldWidth : height / oldHeight;

return [UIImage imageWithCGImage:image.CGImage scale:scaleFactor orientation:UIImageOrientationUp];
    }

2
extension UIImage {

    /// Returns a image that fills in newSize
    func resizedImage(newSize: CGSize) -> UIImage? {
        guard size != newSize else { return self }

    let hasAlpha = false
    let scale: CGFloat = 0.0
    UIGraphicsBeginImageContextWithOptions(newSize, !hasAlpha, scale)
        UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)

        draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
        let newImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage
    }

    /// Returns a resized image that fits in rectSize, keeping it's aspect ratio
    /// Note that the new image size is not rectSize, but within it.
    func resizedImageWithinRect(rectSize: CGSize) -> UIImage? {
        let widthFactor = size.width / rectSize.width
        let heightFactor = size.height / rectSize.height

        var resizeFactor = widthFactor
        if size.height > size.width {
            resizeFactor = heightFactor
        }

        let newSize = CGSize(width: size.width / resizeFactor, height: size.height / resizeFactor)
        let resized = resizedImage(newSize: newSize)

        return resized
    }
}

Ölçek 0.0 olarak ayarlandığında, Retina ekranlar için 2.0 veya daha yüksek olan ana ekranın ölçek faktörü kullanılır (iPhone 6 Plus'ta 3.0).


1

Hızlı Kodda Ryan'ın Çözümü @Ryan

kullanın:

 func imageWithSize(image: UIImage,size: CGSize)->UIImage{
    if UIScreen.mainScreen().respondsToSelector("scale"){
        UIGraphicsBeginImageContextWithOptions(size,false,UIScreen.mainScreen().scale);
    }
    else
    {
         UIGraphicsBeginImageContext(size);
    }

    image.drawInRect(CGRectMake(0, 0, size.width, size.height));
    var newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}

//Summon this function VVV
func resizeImageWithAspect(image: UIImage,scaledToMaxWidth width:CGFloat,maxHeight height :CGFloat)->UIImage
{
    let oldWidth = image.size.width;
    let oldHeight = image.size.height;

    let scaleFactor = (oldWidth > oldHeight) ? width / oldWidth : height / oldHeight;

    let newHeight = oldHeight * scaleFactor;
    let newWidth = oldWidth * scaleFactor;
    let newSize = CGSizeMake(newWidth, newHeight);

    return imageWithSize(image, size: newSize);
}

1

In Swift 3 bazı değişiklikler vardır. İşte UIImage için bir uzantı:

public extension UIImage {
    public func resize(height: CGFloat) -> UIImage? {
        let scale = height / self.size.height
        let width = self.size.width * scale
        UIGraphicsBeginImageContext(CGSize(width: width, height: height))
        self.draw(in: CGRect(x:0, y:0, width:width, height:height))
        let resultImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return resultImage
    }
}

1

Swift 5'te Zeeshan Tufail ve Womble küçük iyileştirmelerle cevap veriyor . Burada görüntüyü maxLengthherhangi bir boyut ve jpeg sıkıştırmaya ölçeklemek için 2 işlevli uzantıya sahibiz .

extension UIImage {
    func aspectFittedToMaxLengthData(maxLength: CGFloat, compressionQuality: CGFloat) -> Data {
        let scale = maxLength / max(self.size.height, self.size.width)
        let format = UIGraphicsImageRendererFormat()
        format.scale = scale
        let renderer = UIGraphicsImageRenderer(size: self.size, format: format)
        return renderer.jpegData(withCompressionQuality: compressionQuality) { context in
            self.draw(in: CGRect(origin: .zero, size: self.size))
        }
    }
    func aspectFittedToMaxLengthImage(maxLength: CGFloat, compressionQuality: CGFloat) -> UIImage? {
        let newImageData = aspectFittedToMaxLengthData(maxLength: maxLength, compressionQuality: compressionQuality)
        return UIImage(data: newImageData)
    }
}

0

Bu benim için mükemmeldi. En boy oranını korur ve a maxLength. Genişlik veya YükseklikmaxLength

-(UIImage*)imageWithImage: (UIImage*) sourceImage maxLength: (float) maxLength
{
    CGFloat scaleFactor = maxLength / MAX(sourceImage.size.width, sourceImage.size.height);

    float newHeight = sourceImage.size.height * scaleFactor;
    float newWidth = sourceImage.size.width * scaleFactor;

    UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight));
    [sourceImage drawInRect:CGRectMake(0, 0, newWidth, newHeight)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

0

Kullanılabilir genişlik için görüntünün en iyi yüksekliğini hesaplar.

import Foundation

public extension UIImage {
    public func height(forWidth width: CGFloat) -> CGFloat {
        let boundingRect = CGRect(
            x: 0,
            y: 0,
            width: width,
            height: CGFloat(MAXFLOAT)
        )
        let rect = AVMakeRect(
            aspectRatio: size,
            insideRect: boundingRect
        )
        return rect.size.height
    }
}

-1

Çok daha basit bir şekilde çözdüm

UIImage *placeholder = [UIImage imageNamed:@"OriginalImage.png"];
self.yourImageview.image = [UIImage imageWithCGImage:[placeholder CGImage] scale:(placeholder.scale * 1.5)
                                  orientation:(placeholder.imageOrientation)];

ölçeğin çarpanı görüntünün izini tanımlar, daha fazla çarpan görüntü daha küçüktür. böylece ekranınıza neyin uyduğunu kontrol edebilirsiniz.

Ya da görüntü genişliğini / ekran genişliğini bölerek multiplire elde edebilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.