Cắt một UIImage


209

Tôi đã có một số mã thay đổi kích thước hình ảnh để tôi có thể lấy một phần nhỏ của trung tâm hình ảnh - Tôi sử dụng mã này để lấy UIImagevà trả về một hình ảnh nhỏ, vuông của hình ảnh, tương tự như hình ảnh trong chế độ xem album của ứng dụng Ảnh. (Tôi biết rằng tôi có thể sử dụng a UIImageViewvà điều chỉnh chế độ cắt để đạt được kết quả tương tự, nhưng những hình ảnh này đôi khi được hiển thị trong UIWebViews).

Tôi đã bắt đầu nhận thấy một số sự cố trong mã này và tôi hơi bối rối. Tôi đã có hai lý thuyết khác nhau và tôi tự hỏi liệu một trong hai là dựa trên cơ sở.

Lý thuyết 1) Tôi đạt được việc cắt xén bằng cách vẽ vào bối cảnh hình ảnh ngoài màn hình với kích thước mục tiêu của tôi. Vì tôi muốn phần trung tâm của hình ảnh, tôi đặt CGRectđối số được chuyển drawInRectsang một cái gì đó lớn hơn giới hạn của bối cảnh hình ảnh của tôi. Tôi đã hy vọng đây là Kosher, nhưng thay vào đó tôi đang cố gắng vẽ lên những ký ức khác mà tôi không nên chạm vào?

Lý thuyết 2) Tôi đang làm tất cả những điều này trong một chủ đề nền. Tôi biết có những phần của UIKit bị giới hạn trong luồng chính. Tôi đã giả định / hy vọng rằng việc vẽ lên một khung nhìn ngoài màn hình không phải là một trong số đó. Liệu tôi có sai?

(Ồ, làm thế nào tôi bỏ lỡ NSImage's drawInRect:fromRect:operation:fraction:phương pháp.)


Nếu bạn đang cố gắng chẩn đoán sự cố, bạn nên chạy ứng dụng theo trình gỡ lỗi và ghi chú lại những gì xảy ra khi nó gặp sự cố. Bạn thậm chí chưa xác định được liệu có ngoại lệ nào bị ném hay bạn đang bị EXC_BAD_ACCESS do một con trỏ lơ lửng. Một khi bạn biết điều đó, thì bạn có thể bắt đầu đưa ra lý thuyết.
benzado

Đủ công bằng. Tôi đã không phản hồi điều này theo trình gỡ lỗi, mặc dù tôi có các thông báo EXC_BAD_ACCESS trong nhật ký sự cố. Khi tôi đăng bài này, tôi đã làm việc với giả định rằng tôi đã phạm phải một sai lầm ngu ngốc khi thực hiện và ai đó sẽ nhảy vào nó (như quên một con đường cắt).
Jablair

Đối với những gì nó có giá trị và câu trả lời tốt dưới đây mặc dù, có một cuộc khảo sát tốt về các kỹ thuật và hiệu suất của chúng trên NSHipster: nshipster.com/image-resizing . Người theo chủ nghĩa thuần túy trong tôi muốn sử dụng CIImage, nhưng người thực dụng đã chọn bối cảnh hình ảnh / UIKit.
Chris Conover

Câu trả lời:


236

Cập nhật 2014-05-28: Tôi đã viết điều này khi iOS 3 trở đi là điều mới mẻ, tôi chắc chắn có nhiều cách tốt hơn để làm điều này ngay bây giờ, có thể được tích hợp sẵn. Như nhiều người đã đề cập, phương pháp này không tính đến việc xoay vòng; đọc một số câu trả lời bổ sung và lan truyền một số tình yêu thượng lưu xung quanh để giữ cho câu trả lời cho câu hỏi này hữu ích cho mọi người.

Phản hồi ban đầu:

Tôi sẽ sao chép / dán câu trả lời của mình cho cùng một câu hỏi ở nơi khác:

Không có một phương thức lớp đơn giản để làm điều này, nhưng có một chức năng mà bạn có thể sử dụng để có được kết quả mong muốn: CGImageCreateWithImageInRect(CGImageRef, CGRect)sẽ giúp bạn giải quyết.

Đây là một ví dụ ngắn sử dụng nó:

CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
// or use the UIImage wherever you like
[UIImageView setImage:[UIImage imageWithCGImage:imageRef]]; 
CGImageRelease(imageRef);

13
Khi imageOrientationđược đặt thành bất cứ thứ gì ngoại trừ lên, giá trị CGImageđược xoay tương ứng với UIImagevà hình chữ nhật cắt xén sẽ bị sai. Bạn có thể xoay hình chữ nhật cắt cho phù hợp, nhưng hình mới được tạo UIImagesẽ có imageOrientation, do đó, hình cắt CGImagesẽ được xoay bên trong nó.
zoul

25
Tôi muốn chỉ ra rằng trên màn hình võng mạc, bạn cần tăng gấp đôi chiều rộng và chiều cao cropRect của mình bằng phương pháp này. Chỉ cần một cái gì đó tôi chạy vào.
petrocket

21
để làm cho nó hoạt động trong bất kỳ định hướng sử dụng:[UIImage imageWithCGImage:imageRef scale:largeImage.scale orientation:largeImage.imageOrientation];
Nicos Karalis

3
Bạn nên thêm hỗ trợ cho Retina !!
Mário Carvalho

1
@NicosKaralis sẽ chỉ xoay nó theo đúng hướng. Tuy nhiên, khu vực mà bạn cắt ra khỏi hình ảnh vẫn sẽ bị sai.
Tim Bodeit

90

Để cắt hình ảnh võng mạc trong khi vẫn giữ nguyên tỷ lệ và hướng, hãy sử dụng phương pháp sau trong danh mục UIImage (iOS 4.0 trở lên):

- (UIImage *)crop:(CGRect)rect {
    if (self.scale > 1.0f) {
        rect = CGRectMake(rect.origin.x * self.scale,
                          rect.origin.y * self.scale,
                          rect.size.width * self.scale,
                          rect.size.height * self.scale);
    }

    CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}

14
Bạn chỉ có thể giết điều kiện ở đây và luôn luôn nhiều self.scale, không?
Michael Mior

2
Bạn nói đúng, Michael, nhưng tôi nghĩ giải pháp trên nhanh hơn một chút trên các thiết bị không phải võng mạc vì nó chỉ thực hiện một kiểm tra duy nhất thay vì bốn phép nhân cộng với một bài tập. Trên các thiết bị võng mạc, đó chỉ là một kiểm tra boolean duy nhất nhiều hơn mức cần thiết, vì vậy đây là câu hỏi về sở thích cá nhân hoặc mục tiêu phù hợp.
CGee

2
Có đúng không khi sử dụng CGImageRelease(imageRef);với ARC được kích hoạt?
CGee


3
Trên thực tế, 4 phép nhân có thể nhanh hơn so với việc đánh giá một điều kiện :) Phép nhân có thể được tối ưu hóa khá tốt trên các CPU hiện đại trong khi các điều kiện chỉ có thể dự đoán được. Nhưng bạn nói đúng, đó là vấn đề ưu tiên. Tôi thích mã ngắn hơn / đơn giản hơn vì khả năng đọc tăng lên.
marsbear

65

Bạn có thể tạo một thể loại UIImage và sử dụng nó bất cứ nơi nào bạn cần. Dựa trên phản hồi và bình luận của HitScans dưới đây.

@implementation UIImage (Crop)

- (UIImage *)crop:(CGRect)rect {

    rect = CGRectMake(rect.origin.x*self.scale, 
                      rect.origin.y*self.scale, 
                      rect.size.width*self.scale, 
                      rect.size.height*self.scale);       

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], rect);
    UIImage *result = [UIImage imageWithCGImage:imageRef 
                                          scale:self.scale 
                                    orientation:self.imageOrientation]; 
    CGImageRelease(imageRef);
    return result;
}

@end

Bạn có thể sử dụng nó theo cách này:

UIImage *imageToCrop = <yourImageToCrop>;
CGRect cropRect = <areaYouWantToCrop>;   

//for example
//CGRectMake(0, 40, 320, 100);

UIImage *croppedImage = [imageToCrop crop:cropRect];

3
Không nên [[UIScreen mainScreen] scale]chỉ được self.scale? Tỷ lệ của hình ảnh có thể không giống với tỷ lệ của màn hình.
Michael Mior

Xin chào, tôi đã thử câu trả lời của bạn và nó cho tôi No visible @interface for 'UIImage' declares the selector 'crop'mặc dù tôi đã đặt các tệp danh mục .h và .m trong dự án của mình và nhập .h trong lớp tôi đang sử dụng danh mục. Bất kỳ ý tưởng?
Ali

Đã sửa nó. Tôi đã bỏ lỡ việc đặt tiêu đề phương thức trong tệp UIImage + Crop.h.
Ali

Tôi muốn cắt hình ảnh trong hình tròn. Vì vậy, chúng ta chỉ có thể thấy đường dẫn tròn và đường dẫn khác vẫn trong suốt.
Alfa

Hơn bạn có thể sử dụng CGImageCreateWithMask hoặc CGImageCreateWithMaskingColors thay vì CGImageCreateWithImageInRect
Vilém Kurz

54

Phiên bản Swift 3

func cropImage(imageToCrop:UIImage, toRect rect:CGRect) -> UIImage{
    
    let imageRef:CGImage = imageToCrop.cgImage!.cropping(to: rect)!
    let cropped:UIImage = UIImage(cgImage:imageRef)
    return cropped
}


let imageTop:UIImage  = UIImage(named:"one.jpg")! // add validation

nhập mô tả hình ảnh ở đây

với sự trợ giúp của chức năng cầu nối này CGRectMake-> CGRect(tín dụng cho câu trả lời này được trả lời bởi @rob mayoff):

 func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {
    return CGRect(x: x, y: y, width: width, height: height)
}

Cách sử dụng là:

if var image:UIImage  = UIImage(named:"one.jpg"){
   let  croppedImage = cropImage(imageToCrop: image, toRect: CGRectMake(
        image.size.width/4,
        0,
        image.size.width/2,
        image.size.height)
    )
}

Đầu ra:

nhập mô tả hình ảnh ở đây


Không chắc chắn tại sao nó đã được bỏ phiếu, tôi nghĩ rằng đó là một câu trả lời tốt. Điều duy nhất tôi có thể nghĩ là nó không xử lý tỷ lệ nên nó sẽ không hoạt động nếu bạn có hình ảnh @ 2x / @ 3x và tên hàm của bạn không khớp.
William T.

4
Bạn cần guarddòng đầu tiên trong trường hợp UIImage.CGImagetrả về nilvà tại sao sử dụng varkhi lethoạt động hoàn hảo.
NoodleOfDeath

Điều này là chính xác, TUY NHIÊN, đọc các tài liệu trên cropping(to:)rất cẩn thận. Kết quả CGImagegiữ một tham chiếu mạnh mẽ đến lớn hơn CGImagemà từ đó nó đã được cắt. Vì vậy, nếu bạn muốn chỉ treo vào hình ảnh được cắt và không có nhu cầu về bản gốc, hãy xem các giải pháp khác.
jsadler

Điều này không hoạt động với hình ảnh có thẻ định hướng. Vùng trồng trọt không được luân chuyển hợp lý.
Tối đa

1
@Max điều này là do cgImage không có hỗ trợ định hướng. Nếu UIImage đang có rightthì cgImage nên được xoay ccw 90 độ, do đó, chỉnh lưu crop cũng nên được xoay
MK Yung

49

Đây là triển khai cây trồng UIImage của tôi tuân theo thuộc tính imageOrientation. Tất cả các định hướng đã được kiểm tra kỹ lưỡng.

inline double rad(double deg)
{
    return deg / 180.0 * M_PI;
}

UIImage* UIImageCrop(UIImage* img, CGRect rect)
{
    CGAffineTransform rectTransform;
    switch (img.imageOrientation)
    {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -img.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -img.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -img.size.width, -img.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, img.scale, img.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([img CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:img.scale orientation:img.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}

7
Cảnh báo implicit declaration of function 'rad' is invalid in c99có thể được xóa bằng cách thay thế: rad (90), rad (-90), rad (-180) -> M_PI_2, -M_PI_2, -_M_PI
Sound Blaster

Ối xin lỗi. Đã thêm chức năng tiện ích rad () vào đoạn mã nguồn.
Sergii Rudigan

contains undefined reference for architecture armv7Tôi có thiếu một thư viện không? CoreGraphics được nhập khẩu.
Bob Spryn

1
@SergiiRudchenko "Tất cả các định hướng đã được kiểm tra kỹ lưỡng." - Điều này có bao gồm các định hướng nhân đôi?
Tim Bodeit

@BobSpryn Không, bạn không thiếu thư viện. Mặc dù tôi không thể giải thích lỗi này có nghĩa là gì, nhưng việc thay thế rad (), như SoundBlaster đã đề xuất, cũng khắc phục lỗi này.
Tim Bodeit

39

Cảnh giác: tất cả các câu trả lời này giả định một CGImageđối tượng hình ảnh được hỗ trợ.

image.CGImagecó thể trả về nil, nếu UIImageđược hỗ trợ bởi a CIImage, đó sẽ là trường hợp nếu bạn tạo hình ảnh này bằng cách sử dụng a CIFilter.

Trong trường hợp đó, bạn có thể phải vẽ hình ảnh trong một bối cảnh mới và trả lại hình ảnh đó ( chậm ).

UIImage* crop(UIImage *image, rect) {
    UIGraphicsBeginImageContextWithOptions(rect.size, false, [image scale]);
    [image drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
    cropped_image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return cropped_image;
}

5
Đây chính xác là những gì tôi cần, hoạt động trong tất cả các kịch bản do đó tốt hơn bất kỳ giải pháp nào khác!
David Thompson

2
image.size trong dòng đầu tiên phải là orth.size UIGraphicsBeginImageContextWithOptions (orth.size, false, image.scale);
Brett

2
Cảm ơn Brett, chắc chắn ngay tại đó. Tôi đã cập nhật mã để bao gồm sửa lỗi này.
colinta

Tại sao nó -rect.origin.x không chỉ là orth.origin.x?
Kẻ xâm lược

2
Bởi vì chúng tôi đang cắt xén; nói cách khác, đối số 'orth' đang nói "Tôi muốn phía trên bên trái của hình ảnh mới bắt đầu, nói, [10, 10]". Để làm điều đó, chúng tôi vẽ hình ảnh sao cho [10, 10] ở gốc của hình ảnh mới.
colinta

35

Không có câu trả lời nào ở đây xử lý tất cả các vấn đề về tỷ lệ và xoay chính xác 100%. Đây là tổng hợp của tất cả mọi thứ cho đến nay, cập nhật kể từ iOS7 / 8. Nó có nghĩa là được đưa vào như một phương pháp trong một thể loại trên UIImage.

- (UIImage *)croppedImageInRect:(CGRect)rect
{
    double (^rad)(double) = ^(double deg) {
        return deg / 180.0 * M_PI;
    };

    CGAffineTransform rectTransform;
    switch (self.imageOrientation) {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -self.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -self.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -self.size.width, -self.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, self.scale, self.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);

    return result;
}

1
Tại sao câu trả lời này không được nâng cao hơn? Tôi đã thử tất cả các câu trả lời ở trên và gặp vô số vấn đề (đáng chú ý là chuyển đổi từ UIImage sang CIImage và mất dữ liệu chuyển đổi phù hợp). ĐÂY LÀ MỘT CÔNG TRÌNH DUY NHẤT CHO TÔI!
Kẻ xâm lược

1
Cảm ơn @Aggressor, rất vui vì điều này hiệu quả với bạn. Câu trả lời của tôi chỉ được đăng một tháng trước trong khi nhiều câu trả lời đã ở đây được 5 năm. Tôi đoán điều đó giải thích việc thiếu phiếu bầu so sánh.
awolf

Tôi đang sử dụng điều này để vuốt các bộ lọc trên các hình ảnh (như cách snapchat làm) và tôi đang gặp vấn đề về tỷ lệ nhỏ. Có bất cứ điều gì trong mã của bạn, bạn có thể đề nghị tôi xem cái nào có thể giữ nguyên nhân của lỗi tỷ lệ này không? Tôi đã đăng giải pháp sử dụng mã của bạn tại đây: stackoverflow.com/questions/23319497/
Kẻ

một chức năng rất hữu ích, cảm ơn bạn. Bất kỳ lời khuyên nào nên được sửa đổi để cắt tập trung vào phần trung tâm của hình ảnh?
riêng tư

NÀY NÊN TRẢ LỜI.
móng

17

Phiên bản Swift của awolfcâu trả lời, hoạt động cho tôi:

public extension UIImage {
    func croppedImage(inRect rect: CGRect) -> UIImage {
        let rad: (Double) -> CGFloat = { deg in
            return CGFloat(deg / 180.0 * .pi)
        }
        var rectTransform: CGAffineTransform
        switch imageOrientation {
        case .left:
            let rotation = CGAffineTransform(rotationAngle: rad(90))
            rectTransform = rotation.translatedBy(x: 0, y: -size.height)
        case .right:
            let rotation = CGAffineTransform(rotationAngle: rad(-90))
            rectTransform = rotation.translatedBy(x: -size.width, y: 0)
        case .down:
            let rotation = CGAffineTransform(rotationAngle: rad(-180))
            rectTransform = rotation.translatedBy(x: -size.width, y: -size.height)
        default:
            rectTransform = .identity
        }
        rectTransform = rectTransform.scaledBy(x: scale, y: scale)
        let transformedRect = rect.applying(rectTransform)
        let imageRef = cgImage!.cropping(to: transformedRect)!
        let result = UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
        return result
    }
}

10
CGSize size = [originalImage size];
int padding = 20;
int pictureSize = 300;
int startCroppingPosition = 100;
if (size.height > size.width) {
    pictureSize = size.width - (2.0 * padding);
    startCroppingPosition = (size.height - pictureSize) / 2.0; 
} else {
    pictureSize = size.height - (2.0 * padding);
    startCroppingPosition = (size.width - pictureSize) / 2.0;
}
// WTF: Don't forget that the CGImageCreateWithImageInRect believes that 
// the image is 180 rotated, so x and y are inverted, same for height and width.
CGRect cropRect = CGRectMake(startCroppingPosition, padding, pictureSize, pictureSize);
CGImageRef imageRef = CGImageCreateWithImageInRect([originalImage CGImage], cropRect);
UIImage *newImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:originalImage.imageOrientation];
[m_photoView setImage:newImage];
CGImageRelease(imageRef);

Hầu hết các câu trả lời tôi thấy chỉ liên quan đến vị trí (0, 0) cho (x, y). Ok đó là một trường hợp nhưng tôi muốn hoạt động cắt xén của tôi được tập trung. Điều khiến tôi mất một thời gian để tìm ra là dòng sau bình luận WTF.

Hãy xem trường hợp của một hình ảnh được chụp với hướng dọc:

  1. Chiều cao hình ảnh gốc cao hơn chiều rộng của nó (Woo, không có gì ngạc nhiên cho đến nay!)
  2. Hình ảnh mà phương thức CGImageCreateWithImageInRect tưởng tượng trong thế giới riêng của nó không thực sự là một bức chân dung mà là một cảnh quan (Đó cũng là lý do tại sao nếu bạn không sử dụng đối số định hướng trong hàm tạo của imageWithCGImage, nó sẽ hiển thị dưới dạng xoay 180).
  3. Vì vậy, bạn nên tưởng tượng rằng đó là một phong cảnh, vị trí (0, 0) là góc trên cùng bên phải của hình ảnh.

Hy vọng nó có ý nghĩa! Nếu không, hãy thử các giá trị khác nhau mà bạn sẽ thấy rằng logic được đảo ngược khi chọn đúng x, y, chiều rộng và chiều cao cho cropRect của bạn.


8

nhanh chóng3

extension UIImage {
    func crop(rect: CGRect) -> UIImage? {
        var scaledRect = rect
        scaledRect.origin.x *= scale
        scaledRect.origin.y *= scale
        scaledRect.size.width *= scale
        scaledRect.size.height *= scale
        guard let imageRef: CGImage = cgImage?.cropping(to: scaledRect) else {
            return nil
        }
        return UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
    }
}

7

Mở rộng Swift

extension UIImage {
    func crop(var rect: CGRect) -> UIImage {
        rect.origin.x*=self.scale
        rect.origin.y*=self.scale
        rect.size.width*=self.scale
        rect.size.height*=self.scale

        let imageRef = CGImageCreateWithImageInRect(self.CGImage, rect)
        let image = UIImage(CGImage: imageRef, scale: self.scale, orientation: self.imageOrientation)!
        return image
    }
}

4

Giải pháp tốt nhất để cắt xén một UIImage trong Swift , về độ chính xác, tỷ lệ pixel ...:

private func squareCropImageToSideLength(let sourceImage: UIImage,
    let sideLength: CGFloat) -> UIImage {
        // input size comes from image
        let inputSize: CGSize = sourceImage.size

        // round up side length to avoid fractional output size
        let sideLength: CGFloat = ceil(sideLength)

        // output size has sideLength for both dimensions
        let outputSize: CGSize = CGSizeMake(sideLength, sideLength)

        // calculate scale so that smaller dimension fits sideLength
        let scale: CGFloat = max(sideLength / inputSize.width,
            sideLength / inputSize.height)

        // scaling the image with this scale results in this output size
        let scaledInputSize: CGSize = CGSizeMake(inputSize.width * scale,
            inputSize.height * scale)

        // determine point in center of "canvas"
        let center: CGPoint = CGPointMake(outputSize.width/2.0,
            outputSize.height/2.0)

        // calculate drawing rect relative to output Size
        let outputRect: CGRect = CGRectMake(center.x - scaledInputSize.width/2.0,
            center.y - scaledInputSize.height/2.0,
            scaledInputSize.width,
            scaledInputSize.height)

        // begin a new bitmap context, scale 0 takes display scale
        UIGraphicsBeginImageContextWithOptions(outputSize, true, 0)

        // optional: set the interpolation quality.
        // For this you need to grab the underlying CGContext
        let ctx: CGContextRef = UIGraphicsGetCurrentContext()
        CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh)

        // draw the source image into the calculated rect
        sourceImage.drawInRect(outputRect)

        // create new image from bitmap context
        let outImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()

        // clean up
        UIGraphicsEndImageContext()

        // pass back new image
        return outImage
}

Hướng dẫn sử dụng để gọi chức năng này:

let image: UIImage = UIImage(named: "Image.jpg")!
let squareImage: UIImage = self.squareCropImageToSideLength(image, sideLength: 320)
self.myUIImageView.image = squareImage

Lưu ý: nguồn cảm hứng mã nguồn ban đầu được viết trong Objective-C đã được tìm thấy trên blog "Cocoanetic".


3

Đoạn mã dưới đây có thể giúp đỡ.

import UIKit

extension UIImage {
    func cropImage(toRect rect: CGRect) -> UIImage? {
        if let imageRef = self.cgImage?.cropping(to: rect) {
            return UIImage(cgImage: imageRef)
        }
        return nil
    }
}

2

Trông hơi lạ nhưng hoạt động tuyệt vời và xem xét định hướng hình ảnh:

var image:UIImage = ...

let img = CIImage(image: image)!.imageByCroppingToRect(rect)
image = UIImage(CIImage: img, scale: 1, orientation: image.imageOrientation)

1
nhưng nó rõ ràng phá vỡ quy mô
Sulthan

2

Swift 5:

extension UIImage {
    func cropped(rect: CGRect) -> UIImage? {
        guard let cgImage = cgImage else { return nil }

        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
        let context = UIGraphicsGetCurrentContext()

        context?.translateBy(x: 0.0, y: self.size.height)
        context?.scaleBy(x: 1.0, y: -1.0)
        context?.draw(cgImage, in: CGRect(x: rect.minX, y: rect.minY, width: self.size.width, height: self.size.height), byTiling: false)


        let croppedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return croppedImage
    }
}

1
- (UIImage *)getSubImage:(CGRect) rect{
    CGImageRef subImageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    CGRect smallBounds = CGRectMake(rect.origin.x, rect.origin.y, CGImageGetWidth(subImageRef), CGImageGetHeight(subImageRef));

    UIGraphicsBeginImageContext(smallBounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextDrawImage(context, smallBounds, subImageRef);
    UIImage* smallImg = [UIImage imageWithCGImage:subImageRef];
    UIGraphicsEndImageContext();

    return smallImg;
}

1
 (UIImage *)squareImageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
    double ratio;
    double delta;
    CGPoint offset;

    //make a new square size, that is the resized imaged width
    CGSize sz = CGSizeMake(newSize.width, newSize.width);

    //figure out if the picture is landscape or portrait, then
    //calculate scale factor and offset
    if (image.size.width > image.size.height) {
        ratio = newSize.width / image.size.width;
        delta = (ratio*image.size.width - ratio*image.size.height);
        offset = CGPointMake(delta/2, 0);
    } else {
        ratio = newSize.width / image.size.height;
        delta = (ratio*image.size.height - ratio*image.size.width);
        offset = CGPointMake(0, delta/2);
    }

    //make the final clipping rect based on the calculated values
    CGRect clipRect = CGRectMake(-offset.x, -offset.y,
                                 (ratio * image.size.width) + delta,
                                 (ratio * image.size.height) + delta);


    //start a new context, with scale factor 0.0 so retina displays get
    //high quality image
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        UIGraphicsBeginImageContextWithOptions(sz, YES, 0.0);
    } else {
        UIGraphicsBeginImageContext(sz);
    }
    UIRectClip(clipRect);
    [image drawInRect:clipRect];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}

1

Trên iOS9.2SDK, tôi sử dụng phương pháp bên dưới để chuyển đổi khung hình từ UIView sang UIImage

-(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
{
  CGSize cropSize = rect.size;
  CGFloat widthScale = image.size.width/self.imageViewOriginal.bounds.size.width;
  CGFloat heightScale = image.size.height/self.imageViewOriginal.bounds.size.height;
  cropSize = CGSizeMake(rect.size.width*widthScale, 
              rect.size.height*heightScale);
  CGPoint pointCrop = CGPointMake(rect.origin.x*widthScale,
             rect.origin.y*heightScale);
  rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, cropSize.height);
  CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
  UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
  CGImageRelease(subImage);

  return croppedImage;
}

1

Cập nhật Swift 2.0 ( CIImagetương thích)

Mở rộng ra câu trả lời của Maxim nhưng cũng hoạt động nếu hình ảnh của bạn cũng CIImagedựa.

public extension UIImage {
    func imageByCroppingToRect(rect: CGRect) -> UIImage? {
        if let image = CGImageCreateWithImageInRect(self.CGImage, rect) {
            return UIImage(CGImage: image)
        } else if let image = (self.CIImage)?.imageByCroppingToRect(rect) {
            return UIImage(CIImage: image)
        }
       return nil
   }
}

1

Đây là phiên bản Swift 3 được cập nhật dựa trên câu trả lời của Noodles

func cropping(to rect: CGRect) -> UIImage? {

    if let cgCrop = cgImage?.cropping(to: rect) {
        return UIImage(cgImage: cgCrop)
    }
    else if let ciCrop = ciImage?.cropping(to: rect) {
        return UIImage(ciImage: ciCrop)
    }

    return nil
}

1

Theo dõi câu trả lời của @Arne. Tôi chỉ sửa chữa chức năng Danh mục. đặt nó trong Thể loại của UIImage.

-(UIImage*)cropImage:(CGRect)rect{

    UIGraphicsBeginImageContextWithOptions(rect.size, false, [self scale]);
    [self drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
    UIImage* cropped_image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return cropped_image;
}

0

Tôi không hài lòng với các giải pháp khác vì chúng rút ra nhiều lần (sử dụng nhiều năng lượng hơn mức cần thiết) hoặc có vấn đề với định hướng. Đây là những gì tôi đã sử dụng cho một hình vuông được cắt theo tỷ lệ từ hình ảnh UIImage *.

CGFloat minimumSide = fminf(image.size.width, image.size.height);
CGFloat finalSquareSize = 600.;

//create new drawing context for right size
CGRect rect = CGRectMake(0, 0, finalSquareSize, finalSquareSize);
CGFloat scalingRatio = 640.0/minimumSide;
UIGraphicsBeginImageContext(rect.size);

//draw
[image drawInRect:CGRectMake((minimumSide - photo.size.width)*scalingRatio/2., (minimumSide - photo.size.height)*scalingRatio/2., photo.size.width*scalingRatio, photo.size.height*scalingRatio)];

UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

0

Tôi sử dụng phương pháp dưới đây.

  -(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
  {
    CGSize cropSize = rect.size;
    CGFloat widthScale =  
    image.size.width/self.imageViewOriginal.bounds.size.width;
    CGFloat heightScale = 
    image.size.height/self.imageViewOriginal.bounds.size.height;
    cropSize = CGSizeMake(rect.size.width*widthScale,  
    rect.size.height*heightScale);
    CGPoint  pointCrop = CGPointMake(rect.origin.x*widthScale, 
    rect.origin.y*heightScale);
    rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, 
    cropSize.height);
    CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
    UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
    CGImageRelease(subImage);
    return croppedImage;

}


0

Hãy xem https://github.com/vvbogdan/BVCropPhoto

- (UIImage *) croppingImage {
    CGFloat scale = self.sourceImage.size. Thong / self.scrollView.contentSize. Thong;

    UIImage * FinalImage = nil;
    CGRect targetFrame = CGRectMake ((self.scrollView.contentInset.left + self.scrollView.content Offerset.x) * scale,
            (self.scrollView.contentInset.top + self.scrollView.content Offerset.y) * scale,
            thang đo self.cropSize. thong *,
            thang đo self.cropSize.height *);

    CGImageRef bối cảnhImage = CGImageCreateWithImageInRect ([[self imageWithRotation: self.sourceImage] CGImage], targetFrame);

    if (bối cảnh! = NULL) {
        FinalImage = [UIImage imageWithCGImage: bối cảnh
                                         tỷ lệ: self.sourceImage.scale
                                   định hướng: UIImageOrientationUp];

        CGImageRelease (bối cảnh);
    }

    trả lại hình ảnh cuối cùng;
}


- (UIImage *) imageWithRotation: (UIImage *) image {


    if (image.imageOrientation == UIImageOrientationUp) trả lại hình ảnh;
    Biến đổi CGAffineTransform = CGAffineTransformIdentity;

    chuyển đổi (image.imageOrientation) {
        trường hợp UIImageOrientationDown:
        trường hợp UIImageOrientationDownMirrored:
            biến đổi = CGAffineTransformTranslate (biến đổi, image.size. thong, image.size.height);
            biến đổi = CGAffineTransformRotate (biến đổi, M_PI);
            phá vỡ;

        trường hợp UIImageOrientationLeft:
        trường hợp UIImageOrientationLeftMirrored:
            biến đổi = CGAffineTransformTranslate (biến đổi, image.size. thong, 0);
            biến đổi = CGAffineTransformRotate (biến đổi, M_PI_2);
            phá vỡ;

        trường hợp UIImageOrientationRight:
        trường hợp UIImageOrientationRightMirrored:
            biến đổi = CGAffineTransformTransTable (biến đổi, 0, image.size.height);
            biến đổi = CGAffineTransformRotate (biến đổi, -M_PI_2);
            phá vỡ;
        trường hợp UIImageOrientationUp:
        trường hợp UIImageOrientationUpMirrored:
            phá vỡ;
    }

    chuyển đổi (image.imageOrientation) {
        trường hợp UIImageOrientationUpMirrored:
        trường hợp UIImageOrientationDownMirrored:
            biến đổi = CGAffineTransformTranslate (biến đổi, image.size. thong, 0);
            biến đổi = CGAffineTransformScale (biến đổi, -1, 1);
            phá vỡ;

        trường hợp UIImageOrientationLeftMirrored:
        trường hợp UIImageOrientationRightMirrored:
            biến đổi = CGAffineTransformTransTable (biến đổi, image.size.height, 0);
            biến đổi = CGAffineTransformScale (biến đổi, -1, 1);
            phá vỡ;
        trường hợp UIImageOrientationUp:
        trường hợp UIImageOrientationDown:
        trường hợp UIImageOrientationLeft:
        trường hợp UIImageOrientationRight:
            phá vỡ;
    }

    // Bây giờ chúng ta vẽ CGImage cơ bản vào một bối cảnh mới, áp dụng biến đổi
    // đã tính ở trên.
    CGContextRef ctx = CGBitmapContextCreate (NULL, image.size. Thong, image.size.height,
            CGImageGetBitsPerComponent (image.CGImage), 0,
            CGImageGetColorSpace (image.CGImage),
            CGImageGetBitmapInfo (image.CGImage));
    CGContextConcatCTM (ctx, biến đổi);
    chuyển đổi (image.imageOrientation) {
        trường hợp UIImageOrientationLeft:
        trường hợp UIImageOrientationLeftMirrored:
        trường hợp UIImageOrientationRight:
        trường hợp UIImageOrientationRightMirrored:
            // Ơ ...
            CGContextDrawImage (ctx, CGRectMake (0, 0, image.size.height, image.size. Thong), image.CGImage);
            phá vỡ;

        mặc định:
            CGContextDrawImage (ctx, CGRectMake (0, 0, image.size. Thong, image.size.height), image.CGImage);
            phá vỡ;
    }

    // Và bây giờ chúng ta chỉ cần tạo một UIImage mới từ bối cảnh vẽ
    CGImageRef cgimg = CGBitmapContextCreateImage (ctx);
    UIImage * img = [hình ảnh UIImageWithCGImage: cgimg];
    CGContextRelease (ctx);
    CGImageRelease (cgimg);
    trả lại img;

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.