Как эффективно создать многорядный фотоколлаж из массива изображений в Swift

проблема

Я строю коллаж из фотографий из массива изображений, которые я помещаю в таблицу. Я хочу сделать обтекание изображений, когда число изображений достигает границы ширины ячейки табличного представления (это позволило бы отображать строки изображений в коллаже). В настоящее время я получаю один ряд. Пожалуйста, не стесняйтесь сообщить, если требуется дополнительная информация. Скорее всего, я не подхожу к этому наиболее эффективным способом, поскольку есть задержка, поскольку количество изображений, используемых в массиве, начинает увеличиваться. (Любая обратная связь по этому вопросу будет очень признателен).

Нота Бене

Я создаю образ коллажа. Это на самом деле одно изображение. Я хочу расположить коллаж, создав эффективную матрицу столбцов и строкв памяти, Затем я заполняю эти записи изображениями. Наконец я делаю снимок полученного изображения и использую его при необходимости. Алгоритм не так эффективен, как написано, и выдает только один ряд изображений. Мне нужна легкая альтернатива алгоритму, используемому ниже. Я не верю, что UICollectionView будет полезной альтернативой в этом случае.

Псевдокод

Дан массив изображений и целевой прямоугольник (представляющий целевой вид)Получить количество изображений в массиве по сравнению с максимально допустимым числом в строкеОпределите меньший прямоугольник соответствующего размера для хранения изображения (чтобы каждая строка заполняла целевой прямоугольник, т. Е. - если одно изображение, то оно должно заполнить строку; если 9 изображений, то это должно заполнить строку полностью; если 10 изображений с макс. 9 изображений в строке, затем 10-й начинается второй ряд)Перебрать коллекциюРазмещайте каждый прямоугольник в правильном месте слева направо, пока не будет достигнуто либо последнее изображение, либо максимальное число в строке; продолжайте в следующем ряду, пока все изображения не поместятся в целевой прямоугольникПри достижении максимального количества изображений в строке поместите изображение и настройте следующий прямоугольник, чтобы он отображался в следующем ряду.

Использование: 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
    }

Альтернатива 1

Я немного усовершенствовал свое решение. Этот действительно складывает изображения в столбцы и строки однако, как указано; Я заинтересован в том, чтобы сделать это максимально эффективным. То, что представлено, это моя попытка создать простейшую вещь, которая работает.

Предостережение

Изображение, полученное с помощью этого, скорее перекошено, чем равномерно распределено по всей ячейке таблицы. Эффективное, равномерное распределение по ячейке таблицы будет оптимальным.

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
    }

Альтернатива 2

Альтернатива, представленная ниже, масштабирует изображения таким образом, чтобы они всегда заполняли прямоугольник (в моем случае это ячейка таблицы). По мере добавления новых изображений они масштабируются по ширине прямоугольника. Когда изображения соответствуют максимальному количеству изображений в строке, они переносятся. Это желаемое поведение, которое происходит в памяти, относительно быстро и содержится в простой функции класса, которую я расширяю для класса UIImage. Я по-прежнему заинтересован в любом алгоритме, который может обеспечить ту же функциональность только быстрее.

Nota Bene: Я не верю, что добавление большего количества пользовательского интерфейса полезно для достижения эффектов, как отмечено выше. Поэтому я ищу более эффективный алгоритм кодирования.

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
    }

Тестирование эффективности

Реда Лемеден дает некоторое процедурное представление о том, как проверить эти CG-вызовы в Инструментах на этомСообщение блога, Он также указывает на некоторые интересные заметки Энди Матущака (из команды UIKit) о некоторых особенностяхвнеэкранный рендеринг, Я, вероятно, все еще не использую решение CIImage должным образом, потому что первоначальные результаты показывают, что решение становится медленнее при попытке форсировать использование графического процессора.

Ответы на вопрос(4)

Ваш ответ на вопрос