Visual_Novel_iOS/crush/Crush/Src/Components/UI/BaseView/FlowLayoutContainer.swift

186 lines
6.0 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// FlowContainer.swift
// Crush
//
// Created by Leon on 2025/7/19.
//
import UIKit
class FlowAutoLayoutContainer: UIView {
// MARK: - Properties
/// Item
var itemSpacing: CGFloat = 8.0 {
didSet { setNeedsLayout() }
}
///
var lineSpacing: CGFloat = 8.0 {
didSet { setNeedsLayout() }
}
///
private var arrangedSubviews: [UIView] = []
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
private func setup() {
//
backgroundColor = .clear
}
// MARK: - Public Methods
///
func addArrangedSubview(_ view: UIView) {
arrangedSubviews.append(view)
addSubview(view)
setNeedsLayout()
}
///
func addArrangedSubviews(_ views: [UIView]) {
arrangedSubviews.append(contentsOf: views)
views.forEach { addSubview($0) }
setNeedsLayout()
}
///
func removeAllArrangedSubviews() {
arrangedSubviews.forEach { $0.removeFromSuperview() }
arrangedSubviews.removeAll()
setNeedsLayout()
}
// MARK: - Layout
override func layoutSubviews() {
super.layoutSubviews()
//
arrangedSubviews.forEach { $0.snp.removeConstraints() }
//
let containerWidth = bounds.width
var currentX: CGFloat = 0
var currentY: CGFloat = 0
var currentRowHeight: CGFloat = 0
var currentRowViews: [UIView] = [] // views
var previousView: UIView?
//
for view in arrangedSubviews {
view.translatesAutoresizingMaskIntoConstraints = false
let viewSize = view.intrinsicContentSize == .zero ? view.bounds.size : view.intrinsicContentSize
//
if currentX + viewSize.width > containerWidth && !currentRowViews.isEmpty {
//
layoutRow(views: currentRowViews, rowHeight: currentRowHeight, startY: currentY, previousView: &previousView)
//
currentX = 0
currentY += currentRowHeight + lineSpacing
currentRowHeight = 0
currentRowViews.removeAll()
}
//
currentRowViews.append(view)
currentX += viewSize.width + itemSpacing
currentRowHeight = max(currentRowHeight, viewSize.height)
}
//
if currentRowViews.isEmpty == false { // has views in lastViews
layoutRow(views: currentRowViews, rowHeight: currentRowHeight, startY: currentY, previousView: &previousView)
if let last = currentRowViews.last {
//dlog("💤currentY:\(currentY)")
last.snp.makeConstraints { make in
make.top.equalToSuperview().offset(currentY)
}
}
invalidateIntrinsicContentSize() // 🔥
}
//dlog("💤currentRowHeight \(currentRowHeight)")
bounds = CGRect(x: 0, y: 0, width: intrinsicContentSize.width, height: intrinsicContentSize.height)
//dlog("💤currentRowHeight:\(currentRowHeight) intrinsicContentSize:\(intrinsicContentSize) sizeHeight:\(bounds.size.height)")
}
///
private func layoutRow(views: [UIView], rowHeight: CGFloat, startY: CGFloat, previousView: inout UIView?) {
for (index, view) in views.enumerated() {
view.snp.makeConstraints { make in
// bottom = startY + rowHeight
make.bottom.equalTo(snp.top).offset(startY + rowHeight)
if index == 0 {
//
make.leading.equalToSuperview()
} else if let prev = previousView {
//
make.leading.equalTo(prev.snp.trailing).offset(itemSpacing)
}
// intrinsicContentSize
if view.intrinsicContentSize.height != UIView.noIntrinsicMetric {
make.height.equalTo(view.intrinsicContentSize.height)
}
}
previousView = view
}
}
///
override var intrinsicContentSize: CGSize {
// layoutIfNeeded()
//
// var totalHeight: CGFloat = 0
// var currentX: CGFloat = 0
// var currentRowHeight: CGFloat = 0
//
// for view in arrangedSubviews {
// let viewSize = view.intrinsicContentSize == .zero ? view.bounds.size : view.intrinsicContentSize
//
// if currentX + viewSize.width > bounds.width && currentX > 0 {
// totalHeight += currentRowHeight + lineSpacing
// currentX = 0
// currentRowHeight = 0
// }
//
// currentX += viewSize.width + itemSpacing
// currentRowHeight = max(currentRowHeight, viewSize.height)
// }
//
// //
// if currentRowHeight > 0 {
// totalHeight += currentRowHeight
// }
//
// dlog("💤Totalheight: \(totalHeight)")
// return CGSize(width: bounds.width, height: totalHeight)
layoutIfNeeded()
if subviews.count > 0{
let view = subviews.last!
// dlog("💤 last:\(view)")
let height = max(view.frame.maxY, 30)
return CGSize(width: bounds.width, height: height)
}
return CGSize(width: bounds.width, height: 30)
}
}