objective animatedimage ios objective-c iphone uiview uiimageview

ios - animatedimage - objective c uiimage



¿Cómo recortar un UIImageView a un nuevo UIImage en modo ''relleno de aspecto''? (1)

Dividamos el problema en dos partes:

  1. Dado el tamaño de un UIImageView y el tamaño de su UIImage, si el modo de contenido del UIImageView es Aspect Fill, ¿cuál es la parte del UIImage que se ajusta al UIImageView? Necesitamos, en efecto, recortar la imagen original para que coincida con lo que realmente está mostrando el UIImageView.

  2. Dado un rect arbitrario dentro de UIImageView, ¿a qué parte de la imagen recortada (derivada en la parte 1) corresponde?

La primera parte es la parte interesante, así que probémoslo. (La segunda parte resultará trivial).

Aquí está la imagen original que usaré:

https://static1.squarespace.com/static/54e8ba93e4b07c3f655b452e/t/56c2a04520c64707756f4267/1455596221531/

Esa imagen es 1000x611. Esto es lo que parece reducido (pero tenga en cuenta que voy a usar la imagen original en todo momento):

Sin embargo, mi vista de imagen será 139x182 y está configurada en Relleno de aspecto. Cuando muestra la imagen, se ve así:

El problema que queremos resolver es: ¿qué parte de la imagen original se muestra en mi vista de imagen, si mi vista de imagen está configurada en Relleno de aspecto?

Aquí vamos. Suponga que iv es la vista de imagen:

let imsize = iv.image!.size let ivsize = iv.bounds.size var scale : CGFloat = ivsize.width / imsize.width if imsize.height * scale < ivsize.height { scale = ivsize.height / imsize.height } let croppedImsize = CGSize(width:ivsize.width/scale, height:ivsize.height/scale) let croppedImrect = CGRect(origin: CGPoint(x: (imsize.width-croppedImsize.width)/2.0, y: (imsize.height-croppedImsize.height)/2.0), size: croppedImsize)

Así que ahora hemos resuelto el problema: croppedImrect es la región de la imagen original que se muestra en la vista de imagen. Procedamos a usar nuestro conocimiento, recortando la imagen a una nueva imagen que coincida con lo que se muestra en la vista de imagen:

let r = UIGraphicsImageRenderer(size:croppedImsize) let croppedIm = r.image { _ in iv.image!.draw(at: CGPoint(x:-croppedImrect.origin.x, y:-croppedImrect.origin.y)) }

El resultado es esta imagen (ignore el borde gris):

Pero he aquí, ¡esa es la respuesta correcta! Extraje de la imagen original exactamente la región representada en el interior de la vista de imagen.

Así que ahora tienes toda la información que necesitas. croppedIm es el UIImage que se muestra realmente en el área recortada de la vista de imagen. scale es la escala entre la vista de imagen y esa imagen. ¡Por lo tanto, puede resolver fácilmente el problema que propuso originalmente! Dado cualquier rectángulo impuesto sobre la vista de la imagen, en las coordenadas de los límites de la vista de la imagen, simplemente aplica la escala (es decir, divide los cuatro atributos por la scale ), y ahora tiene el mismo rectángulo que una parte de croppedIm .

(Observe que realmente no necesitábamos recortar la imagen original para croppedIm ; en realidad, era suficiente saber cómo realizar ese recorte. La información importante es la scale junto con el origin de croppedImRect ; dada esa información, puede tomar el rectángulo impuesto sobre la vista de imagen, escalarlo y compensarlo para obtener el rectángulo deseado de la imagen original).

EDITAR Agregué un pequeño screencast solo para mostrar que mi enfoque funciona como una prueba de concepto:

EDITAR También creó un proyecto de ejemplo descargable aquí:

https://github.com/mattneub/Programming-iOS-Book-Examples/blob/39cc800d18aa484d17c26ffcbab8bbe51c614573/bk2ch02p058cropImageView/Cropper/ViewController.swift

Pero tenga en cuenta que no puedo garantizar que la URL dure para siempre, así que lea la discusión anterior para comprender el enfoque utilizado.

Estoy tratando de recortar una UIView de una vista de imagen usando una superposición UIView que se puede colocar en cualquier lugar de UIImageView . Estoy tomando prestada una solución de una publicación similar sobre cómo resolver esto cuando el modo de contenido UIImageView es ''Aspect Fit''. Esa solución propuesta es:

func computeCropRect(for sourceFrame : CGRect) -> CGRect { let widthScale = bounds.size.width / image!.size.width let heightScale = bounds.size.height / image!.size.height var x : CGFloat = 0 var y : CGFloat = 0 var width : CGFloat = 0 var height : CGFloat = 0 var offSet : CGFloat = 0 if widthScale < heightScale { offSet = (bounds.size.height - (image!.size.height * widthScale))/2 x = sourceFrame.origin.x / widthScale y = (sourceFrame.origin.y - offSet) / widthScale width = sourceFrame.size.width / widthScale height = sourceFrame.size.height / widthScale } else { offSet = (bounds.size.width - (image!.size.width * heightScale))/2 x = (sourceFrame.origin.x - offSet) / heightScale y = sourceFrame.origin.y / heightScale width = sourceFrame.size.width / heightScale height = sourceFrame.size.height / heightScale } return CGRect(x: x, y: y, width: width, height: height) }

El problema es que el uso de esta solución cuando la vista de imagen es de relleno de aspecto hace que el segmento recortado no se alinee exactamente con el lugar donde se UIView la superposición UIView . No estoy muy seguro de cómo adaptar este código para acomodar Aspect Fill o reposicionar mi superposición UIView para que se alinee 1: 1 con el segmento que estoy tratando de recortar.

ACTUALIZACIÓN Resuelto usando la respuesta de Matt a continuación

class ViewController: UIViewController { @IBOutlet weak var catImageView: UIImageView! private var cropView : CropView! override func viewDidLoad() { super.viewDidLoad() cropView = CropView(frame: CGRect(x: 0, y: 0, width: 45, height: 45)) catImageView.image = UIImage(named: "cat") catImageView.clipsToBounds = true catImageView.layer.borderColor = UIColor.purple.cgColor catImageView.layer.borderWidth = 2.0 catImageView.backgroundColor = UIColor.yellow catImageView.addSubview(cropView) let imageSize = catImageView.image!.size let imageViewSize = catImageView.bounds.size var scale : CGFloat = imageViewSize.width / imageSize.width if imageSize.height * scale < imageViewSize.height { scale = imageViewSize.height / imageSize.height } let croppedImageSize = CGSize(width: imageViewSize.width/scale, height: imageViewSize.height/scale) let croppedImrect = CGRect(origin: CGPoint(x: (imageSize.width-croppedImageSize.width)/2.0, y: (imageSize.height-croppedImageSize.height)/2.0), size: croppedImageSize) let renderer = UIGraphicsImageRenderer(size:croppedImageSize) let _ = renderer.image { _ in catImageView.image!.draw(at: CGPoint(x:-croppedImrect.origin.x, y:-croppedImrect.origin.y)) } } @IBAction func performCrop(_ sender: Any) { let cropFrame = catImageView.computeCropRect(for: cropView.frame) if let imageRef = catImageView.image?.cgImage?.cropping(to: cropFrame) { catImageView.image = UIImage(cgImage: imageRef) } } @IBAction func resetCrop(_ sender: Any) { catImageView.image = UIImage(named: "cat") } }

El resultado final