Cómo crear eficientemente un collage de fotos de varias filas a partir de una variedad de imágenes en Swift

Problema

Estoy construyendo un collage de fotos a partir de una serie de imágenes que estoy colocando en una vista de tabla. Quiero hacer que las imágenes se ajusten cuando el número de imágenes alcance el límite del ancho de la celda de vista de tabla (esto me permitiría mostrar filas de imágenes en el collage). Actualmente tengo una sola fila. No dude en avisar si se requiere información adicional. Lo más probable es que no aborde esto de la manera más eficiente ya que hay un retraso ya que el número de imágenes utilizadas en la matriz comienza a aumentar. (cualquier comentario sobre esto sería muy apreciado).

Nota Bene

Estoy creando una imagen de collage. En realidad es una imagen. Quiero organizar el collage creando una matriz eficiente de columnas y filasen memoria. Luego lleno estos rectos con imágenes. Finalmente tomo una foto de la imagen resultante y la uso cuando sea necesario. El algoritmo no es eficiente como está escrito y produce solo una sola fila de imágenes. Necesito una alternativa ligera al algoritmo utilizado a continuación. No creo que UICollectionView sea una alternativa útil en este caso.

Pseudocódigo

Dado un conjunto de imágenes y un rectángulo objetivo (que representa la vista objetivo)Obtenga el número de imágenes en la matriz en comparación con el número máximo permitido por filaDefina un rectángulo más pequeño del tamaño apropiado para contener la imagen (de modo que cada fila llene el rectángulo objetivo, es decir, si una imagen debería llenar la fila; si 9 imágenes, entonces debería llenar la fila por completo; si 10 imágenes con un máximo de 9 imágenes por fila, luego el 10 comienza la segunda fila)Iterar sobre la colecciónColoque cada rectángulo en la ubicación correcta de izquierda a derecha hasta alcanzar la última imagen o un número máximo por fila; continúe en la fila siguiente hasta que todas las imágenes encajen dentro del rectángulo objetivoCuando alcance un número máximo de imágenes por fila, coloque la imagen y configure el siguiente rectángulo para que aparezca en la fila sucesiva

Utilizando: Swift 2.0

class func collageImage (rect:CGRect, images:[UIImage]) -> UIImage {

        let maxSide = max(rect.width / CGFloat(images.count), rect.height / CGFloat(images.count))

        UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.mainScreen().scale)

        var xtransform:CGFloat = 0.0

        for img in images {
            let smallRect:CGRect = CGRectMake(xtransform, 0.0,maxSide, maxSide)
            let rnd = arc4random_uniform(270) + 15
            //draw in rect
            img.drawInRect(smallRect)
            //rotate img using random angle.
            UIImage.rotateImage(img, radian: CGFloat(rnd))
            xtransform += CGFloat(maxSide * 0.8)
        }

        let outputImage = UIGraphicsGetImageFromCurrentImageContext();

        UIGraphicsEndImageContext();

        return outputImage
    }

    class func rotateImage(src: UIImage, radian:CGFloat) -> UIImage
    {
        //  Calculate the size of the rotated view's containing box for our drawing space
        let rotatedViewBox = UIView(frame: CGRectMake(0,0, src.size.width, src.size.height))

        let t: CGAffineTransform  = CGAffineTransformMakeRotation(radian)

        rotatedViewBox.transform = t
        let rotatedSize = rotatedViewBox.frame.size

        //  Create the bitmap context
        UIGraphicsBeginImageContext(rotatedSize)

        let bitmap:CGContextRef = UIGraphicsGetCurrentContext()

        //  Move the origin to the middle of the image so we will rotate and scale around the center.
        CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2);

        //  Rotate the image context
        CGContextRotateCTM(bitmap, radian);

        //  Now, draw the rotated/scaled image into the context
        CGContextScaleCTM(bitmap, 1.0, -1.0);
        CGContextDrawImage(bitmap, CGRectMake(-src.size.width / 2, -src.size.height / 2, src.size.width, src.size.height), src.CGImage)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage
    }

Alternativa 1

He refinado un poco mi solución a esto. Sin embargo, éste apila las imágenes en columnas y filas, como se indicó; mi interés es hacer esto lo más eficiente posible. Lo que se presenta es mi intento de producir lo más simple posible que funcione.

Consideración

La imagen producida con esto está sesgada en lugar de distribuirse uniformemente en toda la celda de vista de tabla. La distribución eficiente y uniforme en la celda de vista de tabla sería óptima.

class func collageImage (rect:CGRect, images:[UIImage]) -> UIImage {

        let maxSide = max(rect.width / CGFloat(images.count), rect.height / CGFloat(images.count)) //* 0.80
        //let rowHeight = rect.height / CGFloat(images.count) * 0.8
        let maxImagesPerRow = 9
        var index = 0
        var currentRow = 1
        var xtransform:CGFloat = 0.0
        var ytransform:CGFloat = 0.0
        var smallRect:CGRect = CGRectZero

        UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.mainScreen().scale)

        for img in images {

            let x = ++index % maxImagesPerRow //row should change when modulus is 0

            //row changes when modulus of counter returns zero @ maxImagesPerRow
            if x == 0 {
                //last column of current row
                //xtransform += CGFloat(maxSide)
                smallRect = CGRectMake(xtransform, ytransform, maxSide, maxSide)

                //reset for new row
                ++currentRow
                xtransform = 0.0
                ytransform = (maxSide * CGFloat(currentRow - 1))

            } else {
                //not a new row
                if xtransform == 0 {
                    //this is first column
                    //draw rect at 0,ytransform
                    smallRect = CGRectMake(xtransform, ytransform, maxSide, maxSide)
                    xtransform += CGFloat(maxSide)
                } else {
                    //not the first column so translate x, ytransform to be reset for new rows only
                    smallRect = CGRectMake(xtransform, ytransform, maxSide, maxSide)
                    xtransform += CGFloat(maxSide)
                }

            }

            //draw in rect
            img.drawInRect(smallRect)

        }

        let outputImage = UIGraphicsGetImageFromCurrentImageContext();

        UIGraphicsEndImageContext();

        return outputImage
    }

Alternativa 2

La alternativa presentada a continuación escala las imágenes para que siempre llenen el rectángulo (en mi caso, la celda de vista de tabla). A medida que se agregan más imágenes, se ajustan para ajustarse al ancho del rectángulo. Cuando las imágenes alcanzan el número máximo de imágenes por fila, se ajustan. Este es el comportamiento deseado, ocurre en la memoria, es relativamente rápido y está contenido en una función de clase simple que extiendo en la clase UIImage. Todavía estoy interesado en cualquier algoritmo que pueda ofrecer la misma funcionalidad solo más rápido.

Nota Bene: No creo que agregar más IU sea útil para lograr los efectos como se indicó anteriormente. Por lo tanto, un algoritmo de codificación más eficiente es lo que estoy buscando.

class func collageImage (rect:CGRect, images:[UIImage]) -> UIImage {

        let maxImagesPerRow = 9
        var maxSide : CGFloat = 0.0

        if images.count >= maxImagesPerRow {
            maxSide = max(rect.width / CGFloat(maxImagesPerRow), rect.height / CGFloat(maxImagesPerRow))
        } else {
            maxSide = max(rect.width / CGFloat(images.count), rect.height / CGFloat(images.count))
        }

        var index = 0
        var currentRow = 1
        var xtransform:CGFloat = 0.0
        var ytransform:CGFloat = 0.0
        var smallRect:CGRect = CGRectZero

        UIGraphicsBeginImageContextWithOptions(rect.size, false,  UIScreen.mainScreen().scale)

        for img in images {

            let x = ++index % maxImagesPerRow //row should change when modulus is 0

            //row changes when modulus of counter returns zero @ maxImagesPerRow
            if x == 0 {
                //last column of current row
                smallRect = CGRectMake(xtransform, ytransform, maxSide, maxSide)

                //reset for new row
                ++currentRow
                xtransform = 0.0
                ytransform = (maxSide * CGFloat(currentRow - 1))

            } else {
                //not a new row
                smallRect = CGRectMake(xtransform, ytransform, maxSide, maxSide)
                xtransform += CGFloat(maxSide)  
            }

            //draw in rect
            img.drawInRect(smallRect)

        }

        let outputImage = UIGraphicsGetImageFromCurrentImageContext();

        UIGraphicsEndImageContext();

        return outputImage
    }

Pruebas de eficiencia

Reda Lemeden da una idea de procedimiento sobre cómo probar estas llamadas CG dentro de los instrumentos en esteentrada en el blog. También señala algunas notas interesantes de Andy Matuschak (del equipo de UIKit) sobre algunas de las peculiaridades derepresentación fuera de la pantalla. Probablemente todavía no estoy aprovechando la solución CIImage correctamente porque los resultados iniciales muestran que la solución se vuelve más lenta cuando intento forzar la utilización de la GPU.

Respuestas a la pregunta(4)

Su respuesta a la pregunta