iOS/UIKit

[UIkit] 이미지 사이즈에 따른 동적 컬렉션뷰 셀

늘스토리 주인장 2025. 2. 3. 18:35
728x90
반응형

🧸 시작

컬렉션뷰셀이 이미지 사이즈에 따라 셀의 사이즈도 바뀐다! 

 

 

🧸 폴더로 이동한다.

import UIKit
import DesignSystem

class CustomCollectionViewController: UIViewController, UICollectionViewDelegate {
    
    var collectionView: UICollectionView!
    var imageList: [UIImage] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 이미지 목록 초기화
        imageList = [
            DesignSystemDemoAppAsset.shirt.image,
            DesignSystemDemoAppAsset.blueshirt.image,
            DesignSystemDemoAppAsset.nike.image,
            DesignSystemDemoAppAsset.nike2.image,
            DesignSystemDemoAppAsset.nike3.image,
            DesignSystemDemoAppAsset.onepiece.image,
            DesignSystemDemoAppAsset.pants.image,
            DesignSystemDemoAppAsset.pants2.image,
            DesignSystemDemoAppAsset.white.image
        ]
        
        let layout = CollectionViewLayout()
        layout.delegate = self
        
        collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
        collectionView.backgroundColor = .white
        collectionView.dataSource = self
        collectionView.delegate = self
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
        view.addSubview(collectionView)
    }
}

extension CustomCollectionViewController: CollectionViewLayoutDelegate {
    // 이미지 사이즈에 따른 셀 높이 계산
    func collectionView(_ collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath: IndexPath) -> CGFloat {
        let cellWidth: CGFloat = (view.bounds.width - 4) / 2
        let imageHeight = imageList[indexPath.item].size.height
        let imageWidth = imageList[indexPath.item].size.width
        let imageRatio = imageHeight / imageWidth
        return imageRatio * cellWidth
    }
}

extension CustomCollectionViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return imageList.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        
        let imageView = UIImageView(image: imageList[indexPath.item])
        imageView.frame = cell.bounds
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        cell.contentView.addSubview(imageView)
        
        return cell
    }
}

public protocol CollectionViewLayoutDelegate: AnyObject {
    func collectionView(_ collectionView: UICollectionView, heightForPhotoAtIndexPath indexPath: IndexPath) -> CGFloat
}

public class CollectionViewLayout: UICollectionViewFlowLayout {
    
    public weak var delegate: CollectionViewLayoutDelegate?
    
    private var contentHeight: CGFloat = 0
    private var contentWidth: CGFloat {
        guard let collectionView = collectionView else {
            return 0
        }
        let insets = collectionView.contentInset
        return collectionView.bounds.width - (insets.left + insets.right)
    }
    
    // 전체 콘텐츠 사이즈 계산
    public override var collectionViewContentSize: CGSize {
        return CGSize(width: contentWidth, height: contentHeight)
    }

    private var cache: [UICollectionViewLayoutAttributes] = []

    public override init() {
        super.init()
    }
    
    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public override func prepare() {
        guard let collectionView = collectionView, cache.isEmpty else { return }

        let numberOfColumns: Int = 2
        let cellPadding: CGFloat = 5
        let cellWidth: CGFloat = contentWidth / CGFloat(numberOfColumns)

        let xOffSet: [CGFloat] = [0, cellWidth]
        var yOffSet: [CGFloat] = .init(repeating: 0, count: numberOfColumns)

        var column: Int = 0

        for item in 0..<collectionView.numberOfItems(inSection: 0) {
            let indexPath = IndexPath(item: item, section: 0)
            let imageHeight = delegate?.collectionView(collectionView, heightForPhotoAtIndexPath: indexPath) ?? 180
            let height = cellPadding * 2 + imageHeight

            let frame = CGRect(x: xOffSet[column],
                               y: yOffSet[column],
                               width: cellWidth,
                               height: height)
            let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)

            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
            attributes.frame = insetFrame
            cache.append(attributes)

            contentHeight = max(contentHeight, frame.maxY)
            yOffSet[column] = yOffSet[column] + height

            column = yOffSet[0] > yOffSet[1] ? 1 : 0
        }
    }

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

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

        return visibleLayoutAttributes
    }

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

 

📂 결과

고정된 사이즈로 나타내는 거보다 다채로운 셀의 사이즈로 신선한 느낌으로 예쁜 거 같다!

 

 

 

 

 

 

 

728x90
반응형

 

 

 

 

 

728x90
반응형