2016-02-28 3 views
1

Я пытаюсь обрезать изображение в квадрат, но как только я на самом деле попытаюсь сделать обрезку с помощью CGImageCreateWithImageInRect(), эта строка выйдет из строя. Я установил точки останова и убедился, что аргументы, переданные в эту функцию, не ноль.CGImageCreateWithImageInRect() return nil

Я довольно новичок в программировании и Swift, но искал вокруг и не нашел решения для моей проблемы.

Причина неудачи:

fatal error: unexpectedly found nil while unwrapping an Optional value

func cropImageToSquare(imageData: NSData) -> NSData { 

    let image = UIImage(data: imageData) 
    let contextImage : UIImage = UIImage(CGImage: image!.CGImage!) 
    let contextSize: CGSize = contextImage.size 

    let imageDimension: CGFloat = contextSize.height 
    let posY : CGFloat = (contextSize.height + (contextSize.width - contextSize.height)/2) 
    let rect: CGRect = CGRectMake(0, posY, imageDimension, imageDimension) 

    // error on line below: fatal error: unexpectedly found nil while unwrapping an Optional value 
    let imageRef: CGImageRef = CGImageCreateWithImageInRect(contextImage.CGImage, rect)! 
    let croppedImage : UIImage = UIImage(CGImage: imageRef, scale: 1.0, orientation: image!.imageOrientation) 

    let croppedImageData = UIImageJPEGRepresentation(croppedImage, 1.0) 

    return croppedImageData! 

} 
+0

Я готов держать пари, что 'rect' не прав. Я не уверен, что вы делаете, но вы устанавливаете его на высоту плюс половину разницы между шириной и высотой (которая сама по себе не имеет смысла для меня), но затем пытается получить прямоугольник, который начинается с этого 'posY', но затем снова является высотой изображения. Таким образом, 'posY' плюс эта странная высота, вероятно, дает« CGRect », который не соответствует размеру исходного изображения. Я уверен, что 'CGImageCreateWithImageInRect' терпит неудачу, потому что он не может использовать' rect', который вы передали ему. – Rob

+0

Кроме того, вы также конвертируете данные в 'UIImage', получая его' CGImage', создавая из него еще один 'UIImage', а затем снова получая' CGImage'. Все это кажется излишне запутанным. Я не уверен, что вы пытаетесь сделать с 'contextImage', но я бы ушел в отставку и просто использую ваш' image.CGImage' при вашем вызове 'CGImageCreateWithImageInRect'. – Rob

+0

То, что я пытаюсь сделать с 'posY', заключалось в том, чтобы обрезать квадрат из центра изображения, а не сверху. Кроме того, хотя я ограничиваю только портретные изображения, распечатка 'contextSize' даст мне высоту и ширину. (Высота была фактически меньше ширины, почему-то) –

ответ

-1

Ваш код использует много сил-разворачивания с ! с. Я бы рекомендовал избегать этого - компилятор пытается помочь вам написать код, который не будет аварийно завершен. Используйте опциональную цепочку с ? и if let/guard let.

! Эта конкретная строка скрывает проблему, когда CGImageCreateWithImageInRect может возвращать нуль. The documentation объясняет, что это происходит, когда rect неверно находится внутри границ изображения. Ваш код работает для изображений в портретной ориентации, но не для пейзажа.

Кроме того, есть удобная функция, предоставляемая AVFoundation, которая может автоматически найти правый прямоугольник для использования, называемый AVMakeRectWithAspectRatioInsideRect. Нет необходимости делать расчеты вручную :-)

Здесь не то, что я бы рекомендовал:

import AVFoundation 

extension UIImage 
{ 
    func croppedToSquare() -> UIImage 
    { 
     guard let cgImage = self.CGImage else { return self } 

     // Note: self.size depends on self.imageOrientation, so we use CGImageGetWidth/Height here. 
     let boundingRect = CGRect(
      x: 0, y: 0, 
      width: CGImageGetWidth(cgImage), 
      height: CGImageGetHeight(cgImage)) 

     // Crop to square (1:1 aspect ratio) and round the resulting rectangle to integer coordinates. 
     var cropRect = AVMakeRectWithAspectRatioInsideRect(CGSize(width: 1, height: 1), boundingRect) 
     cropRect.origin.x = ceil(cropRect.origin.x) 
     cropRect.origin.y = ceil(cropRect.origin.y) 
     cropRect.size.width = floor(cropRect.size.width) 
     cropRect.size.height = floor(cropRect.size.height) 

     guard let croppedImage = CGImageCreateWithImageInRect(cgImage, cropRect) else { 
      assertionFailure("cropRect \(cropRect) was not inside \(boundingRect)") 
      return self 
     } 

     return UIImage(CGImage: croppedImage, scale: self.scale, orientation: self.imageOrientation) 
    } 
} 

// then: 
let croppedImage = myUIImage.croppedToSquare() 
+0

Спасибо @jtbandes. Я не думал об использовании 'AVMakeRectWithAspectRatioInsideRect'. Я не понимаю, как изображение обрезается в центре? Я не понимаю, как этот код делает это правильно. –

+0

'AVMakeRectWithAspectRatioInsideRect' центрирует его автоматически. – jtbandes

+0

О, как удобно! Это потрясающе. Огромное спасибо. –