Как создать сетку / раствор для изображений с динамическим размером, сохраняя пропорции изображения без обрезки? - PullRequest
0 голосов
/ 02 февраля 2019

Мне нужно динамически размещать изображения в виде сетки / раствора, сохраняя их исходное соотношение сторон.По сути, я пытаюсь найти решение, похожее на решение Adobe Lightroom.

adobe lightroom cc

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

Я предполагаю, что мне также придется динамически играть с высотой, но я не понимаю, как.

Код, который я использую для процесса нормализации:

var i = 0
while i < sizes.count {
    var maxWidth = collectionViewWidth // the width of the UICollectionView
    var rowWidth: CGFloat = 0.0
    var j = i
    while rowWidth < maxWidth, j < sizes.count {
        let belowThreshold = sizes[j].width < (maxWidth - rowWidth) * 1.30
        let remainsEnough = (maxWidth - rowWidth) / maxWidth > 0.3 && belowThreshold
        if belowThreshold || remainsEnough {
            rowWidth += sizes[j].width
            j += 1
        } else { break }
    }

    let spacing = CGFloat((j - i - 1) * 2)
    maxWidth -= spacing
    var newRowWidth: CGFloat = 0
    for l in i..<j {
        let x = (sizes[l].width * maxWidth) / rowWidth
        sizes[l].width = x.rounded(to: 3)
        newRowWidth += sizes[l].width
    }

    if newRowWidth >= maxWidth {
        let width = sizes[j-1].width - (newRowWidth - maxWidth).rounded(to: 3)
        sizes[j-1].width = width.rounded(to: 3)
    }

    i = j
}

ОБНОВЛЕНИЕ 1

Вот URL-адрес GitHub для примера проектас тем, что у меня сейчас есть: https://github.com/abrahamduran/ios-mortar-view

1 Ответ

0 голосов
/ 11 февраля 2019

Я написал стартовый макет для вас.Вы должны проверить весь код, который я опубликовал ofc.Используйте пользовательский макет, например:

let layout = CustomLayout()
layout.minimumLineSpacing = 4
layout.minimumInteritemSpacing = 4
collectionView.collectionViewLayout = layout
collectionView.backgroundColor = .lightGray

...
// Delegate

func collectionView(_ collectionView: UICollectionView, sizeForPhotoAtIndexPath indexPath: IndexPath) -> CGSize {
    return images[indexPath.row].size
}

Код для Пользовательский макет .

import UIKit

protocol CustomLayoutDelegate: class {
    func collectionView(_ collectionView: UICollectionView, sizeForPhotoAtIndexPath indexPath: IndexPath) -> CGSize
}

class CustomLayout: UICollectionViewFlowLayout {

    var preferredHeight: CGFloat = 100 {
        didSet {
            invalidateLayout()
        }
    }

    fileprivate var cache = [UICollectionViewLayoutAttributes]()
    fileprivate var contentSize: CGSize = .zero

    override func prepare() {
        super.prepare()

        cache.removeAll()
        guard let collectionView = collectionView,
            let delegate = collectionView.delegate as? CustomLayoutDelegate else {
            return
        }

        var sizes: [IndexPath: CGSize] = [:]

        let maxRowWidth = collectionView.frame.width - (collectionView.contentInset.left + collectionView.contentInset.right)

        var offsetY: CGFloat = 0
        var rowIndexes: [IndexPath] = []
        var rowWidth: CGFloat = 0
        let spacing = minimumInteritemSpacing

        let numberOfItems = collectionView.numberOfItems(inSection: 0)
        for item in 0..<numberOfItems {
            let indexPath = IndexPath(item: item, section: 0)
            let size = delegate.collectionView(collectionView, sizeForPhotoAtIndexPath: indexPath)
            sizes[indexPath] = size

            let aspectRatio = size.width / size.height
            let preferredWidth = preferredHeight * aspectRatio
            rowWidth += preferredWidth
            rowIndexes.append(indexPath)

            if rowIndexes.count > 1 {
                // Check if we fit row width.
                let rowWidthWithSpacing = rowWidth + CGFloat(rowIndexes.count - 1) * spacing
                if rowWidthWithSpacing > maxRowWidth {
                    let previousRowWidthWithSpacing = rowWidthWithSpacing - spacing - preferredWidth
                    let diff = abs(maxRowWidth - rowWidthWithSpacing)
                    let previousDiff = abs(maxRowWidth - previousRowWidthWithSpacing)

                    let scale: CGFloat
                    let finalRowIndexPaths: [IndexPath]

                    if previousDiff < diff {
                        rowWidth -= preferredWidth
                        rowIndexes.removeLast()
                        finalRowIndexPaths = rowIndexes
                        scale = maxRowWidth / rowWidth
                        rowWidth = preferredWidth
                        rowIndexes = [indexPath]
                    } else {
                        finalRowIndexPaths = rowIndexes
                        scale = maxRowWidth / rowWidth
                        rowWidth = 0
                        rowIndexes = []
                    }

                    let finalHeight = preferredHeight * scale
                    var offsetX: CGFloat = 0
                    finalRowIndexPaths.forEach {
                        let size = sizes[$0]!
                        let scale = finalHeight / size.height
                        let attributes = UICollectionViewLayoutAttributes(forCellWith: $0)
                        attributes.frame = CGRect(x: offsetX, y: offsetY, width: size.width * scale, height: size.height * scale).integral
                        offsetX = attributes.frame.maxX + spacing
                        cache.append(attributes)
                    }
                    offsetY = (cache.last?.frame.maxY ?? 0) + minimumLineSpacing
                }
            }

            if numberOfItems == item + 1 && !rowIndexes.isEmpty {
                let finalHeight = preferredHeight
                var offsetX: CGFloat = 0
                rowIndexes.forEach {
                    let size = sizes[$0]!
                    let scale = finalHeight / size.height
                    let attributes = UICollectionViewLayoutAttributes(forCellWith: $0)
                    attributes.frame = CGRect(x: offsetX, y: offsetY, width: size.width * scale, height: size.height * scale).integral
                    offsetX = attributes.frame.maxX + spacing
                    cache.append(attributes)
                }
                offsetY = (cache.last?.frame.maxY ?? 0) + minimumLineSpacing
            }

            contentSize = CGSize(width: collectionView.frame.width, height: cache.last?.frame.maxY ?? 0)
        }
    }

    override var collectionViewContentSize: CGSize {
        return contentSize
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()

        for attributes in cache {
            if attributes.frame.intersects(rect) {
                visibleLayoutAttributes.append(attributes)
            }
        }
        return visibleLayoutAttributes
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        return cache[indexPath.item]
    }

}

Sample

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...