import UIKit
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
@IBOutlet weak var collectionViewHeight: NSLayoutConstraint!
let dataSource: [String] = ["아이템", "아이템111", "아이템”222", "아이템3333", "아이템1111", "아이템3123123", "아이템123123", "아이템", "아이템12313", "아이템222", "아이템31231", "아이템23333", "아이템", "아이템232332", "아이템123123", "아이템66", "아이템2", "아이템", "아이템123123", "아이템3", "아이템123312", "아이템123123213", "아이템", "아이템", "아이템321312", "아이템3", "아이", "아이템2", "아이템", "아이템", "아이템4444", "아이템", "아이템", "아이템123123"]
let sectionCount: CGFloat = 5
let sectionHight: CGFloat = 37
var sectionWidth: CGFloat = 0
let itemHeight: CGFloat = 27
let itemPadding: CGFloat = 8
let padding: CGFloat = 5
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self._configureView()
}
private func _configureView() {
self.sectionWidth = self.view.bounds.width
self.collectionView.dataSource = self
// let layout = UICollectionViewFlowLayout()
//
// layout.estimatedItemSize = CGSize(width: 100, height: itemHight)
// layout.minimumInteritemSpacing = itemPadding
// layout.minimumLineSpacing = linePadding
let layout = HorizontalFlowLayout()
layout.delegate = self
self.collectionView.collectionViewLayout = layout
self._makeSectionWidth()
self.collectionViewHeight.constant = sectionHight * sectionCount - padding * 2
}
private func _makeSectionWidth() {
var sumSize: CGFloat = padding
dataSource.forEach {
sumSize += (self._makeTitleWidth(title: $0) + padding)
}
let oneLineWidth = sumSize / sectionCount
var lastIndex: Int = 0
var lineWitdh: CGFloat = padding
var lineWitdhs: [CGFloat] = []
dataSource.enumerated().forEach { index, item in
let itemWidth: CGFloat = _makeTitleWidth(title: item)
lineWitdh += itemWidth
if lineWitdh > oneLineWidth {
let widthDiff = lineWitdh - oneLineWidth
if widthDiff < itemWidth/3 {
lineWitdh -= itemWidth
lineWitdhs.append(lineWitdh)
lineWitdh = itemWidth + padding
lastIndex = index
} else {
lineWitdhs.append(lineWitdh)
lineWitdh = 0
lastIndex = index + 1
}
} else {
lineWitdh += padding
}
}
lineWitdh = padding
for i in lastIndex..<dataSource.count {
let itemWidth: CGFloat = _makeTitleWidth(title: dataSource[i])
lineWitdh += itemWidth + padding
}
lineWitdhs.append(lineWitdh)
guard let maxLineWitdh = lineWitdhs.max() else { return }
self.sectionWidth = maxLineWitdh
}
private func _makeTitleWidth(title: String) -> CGFloat {
return title.size(withAttributes:
[NSAttributedString.Key.font: UIFont.systemFont(
ofSize: 15, weight: .regular
)]).width + itemPadding * 2
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataSource.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as? CollectionViewCell else {
return UICollectionViewCell()
}
cell.display(item: dataSource[indexPath.item])
return cell
}
}
extension ViewController: HorizontalFlowLayoutDelegate {
func collectionView(
_ collectionView: UICollectionView, sizeForPillAtIndexPath indexPath: IndexPath
) -> CGSize {
let item = dataSource[indexPath.item]
return CGSize(width: _makeTitleWidth(title: item), height: itemHeight)
}
func collectionView(
_ collectionView: UICollectionView, insetsForItemsInSection section: Int
) -> UIEdgeInsets {
return UIEdgeInsets(top: 3, left: itemPadding, bottom: 3, right: itemPadding)
}
func collectionView(
_ collectionView: UICollectionView, itemSpacingInSection section: Int
) -> CGFloat {
return padding
}
func collectionView(
_ collectionView: UICollectionView, lineWidthSection: Int
) -> CGFloat {
return sectionWidth
}
}
import UIKit
protocol HorizontalFlowLayoutDelegate: AnyObject {
func collectionView(
_ collectionView: UICollectionView, sizeForPillAtIndexPath indexPath: IndexPath
) -> CGSize
func collectionView(
_ collectionView: UICollectionView, insetsForItemsInSection section: Int
) -> UIEdgeInsets
func collectionView(
_ collectionView: UICollectionView, itemSpacingInSection section: Int
) -> CGFloat
func collectionView(
_ collectionView: UICollectionView, lineWidthSection: Int
) -> CGFloat
}
final class HorizontalFlowLayout: UICollectionViewFlowLayout {
weak var delegate: HorizontalFlowLayoutDelegate?
var layoutHeight: CGFloat = 0.0
var layoutWidth: CGFloat = 0.0
private var itemCache: [UICollectionViewLayoutAttributes] = []
override func prepare() {
super.prepare()
itemCache.removeAll()
guard let collectionView = collectionView else {
return
}
var layoutWidthIterator: CGFloat = 0.0
for section in 0..<collectionView.numberOfSections {
let insets: UIEdgeInsets = delegate?.collectionView(collectionView, insetsForItemsInSection: section) ?? UIEdgeInsets.zero
let interItemSpacing: CGFloat = delegate?.collectionView(collectionView, itemSpacingInSection: section) ?? 0.0
let maxLineWidth: CGFloat = delegate?.collectionView(collectionView, lineWidthSection: section) ?? 0.0
var itemSize: CGSize = .zero
layoutWidth = maxLineWidth
for item in 0..<collectionView.numberOfItems(inSection: section) {
let indexPath = IndexPath(item: item, section: section)
itemSize = delegate?.collectionView(collectionView, sizeForPillAtIndexPath: indexPath) ?? .zero
if (layoutWidthIterator + itemSize.width + insets.left + insets.right) > maxLineWidth {
layoutWidthIterator = 0.0
layoutHeight += itemSize.height + interItemSpacing
}
let frame = CGRect(
x: layoutWidthIterator + insets.left,
y: layoutHeight,
width: itemSize.width,
height: itemSize.height
)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = frame
itemCache.append(attributes)
layoutWidthIterator = layoutWidthIterator + frame.width + interItemSpacing
}
layoutWidthIterator = 0.0
}
}
override func layoutAttributesForElements(in rect: CGRect)-> [UICollectionViewLayoutAttributes]? {
super.layoutAttributesForElements(in: rect)
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = []
for attributes in itemCache {
if attributes.frame.intersects(rect) {
visibleLayoutAttributes.append(attributes)
}
}
return visibleLayoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
super.layoutAttributesForItem(at: indexPath)
return itemCache[indexPath.row]
}
override var collectionViewContentSize: CGSize {
return CGSize(width: layoutWidth, height: contentHight)
}
private var contentWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
private var contentHight: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.height - (insets.bottom + insets.top)
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
layoutHeight = 0.0
layoutWidth = 0.0
return true
}
}
'iOS > 학습' 카테고리의 다른 글
TCA) Store vs ViewStore (0) | 2023.02.13 |
---|---|
Storyboard 에서 Generic Type 의존성 주입하기 2 (0) | 2022.08.29 |
RxNimble + Quick Unit Test (0) | 2022.05.26 |
iOS View 에서의 init 함수들 (0) | 2022.05.08 |
iOS RxDataSource로 Custom Xib CalendarView 만들기 (0) | 2022.05.07 |