实现下图的成果
档次关系为:
- Collection View 有一个 header,
header 下面有一个 scroll view
- Collection View 还有很多 cell
图中可视区域内,有 6 个
成果是,点击 scroll view 下面的 item,
能够更新上面 cell 的内容
第一局部,自定义布局 Collection View layout
布局精髓
class HanGridLayout: UICollectionViewLayout {override public func prepare() {
guard let collectionView = collectionView else {return}
prepareCache()
contentHeight = 0
// 配置 header 的地位,let headerH = layout.headSize.height
let headerIP = IndexPath(item: 0, section: 0)
let headerAttributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: CommonComponent.header.kind, with: headerIP)
headerAttributes.frame = CGRect(x: 0, y: contentHeight, width: UI.std.width, height: headerH)
var cellX: CGFloat = layout.contentEdge.left
cache[.header]?[headerIP] = headerAttributes
contentHeight += (headerH + layout.contentEdge.top)
let count = collectionView.numberOfItems(inSection: 0)
// 配置 cell 的地位
for item in 0 ..< count {let cellIndexPath = IndexPath(item: item, section: 0)
let attributes = UICollectionViewLayoutAttributes(forCellWith: cellIndexPath)
attributes.frame = CGRect(x: cellX, y: contentHeight, width: layout.itemSize.width, height: layout.itemSize.height)
// 次要是,这里有一个换行
contentHeight += layout.exceed(origin: &cellX, limit: collectionViewWidth)
cache[.cell]?[cellIndexPath] = attributes
}
// 这里有一个兼顾显示
// 对于最初一行的格子,内容的出现
if count % 2 == 1{contentHeight += layout.itemSize.height}
contentHeight += layout.contentEdge.bottom
}
}
第 2 局部,header 下面的滚动条
次要是状态的治理,
旧状态抹去,
新状态出现
class HanLanTopV: UICollectionReusableView {
@IBOutlet weak var midView: UIView!
// 文本转 UILabel
var list = [String]()
// 滚动视图
lazy var scroll = UIScrollView()
var names = [UILabel]()
var bags = [Disposable]()
// 动画的,底部的那条线
lazy var line: UIView = // ...
override func awakeFromNib(){super.awakeFromNib()
// 初始化配置
if scroll.superview == nil{midView.addSubs([scroll])
}
if line.superview == nil{scroll.addSubs([line])
}
scroll.snp.makeConstraints {(m) in
m.edges.equalToSuperview()}
}
func config(package press: String, name n: String, config src: String?, list info: SubNameInfo, selected sIdx: Int){
// 状态还原
bags.forEach {$0.dispose()
}
names.forEach {$0.removeFromSuperview()
}
bags.removeAll()
names.removeAll()
// ...
// 简略的显示配置
// 每次刷新进去,// 底部的标签,都是动静生成
let temps = info.names.map {(str) -> UILabel in
let l = UILabel()
l.text = str
l.isUserInteractionEnabled = true
l.textAlignment = .center
l.textColor = UIColor(rgb: 0x404248)
l.font = UIFont.regular(ofSize: 14)
return l
}
names.append(contentsOf: temps)
scroll.addSubs(names)
for i in 0..<info.cnt{// names[i].layer.debug()
let tagGesture = UITapGestureRecognizer()
names[i].addGestureRecognizer(tagGesture)
let bag = tagGesture.rx.event.subscribe {(t) in
self.delegate?.choose(idx: i)
}
bags.append(bag)
names[i].snp.makeConstraints {(m) in
m.height.centerY.equalToSuperview()
if i == 0{m.leading.equalToSuperview().offset(16)
}
else{m.leading.equalTo(names[i - 1].snp.trailing).offset(28)
}
if i == info.cnt - 1{m.trailing.equalToSuperview().offset(16.neg)
}
}
}
names[sIdx].textColor = UIColor(rgb: 0x0080FF)
names[sIdx].font = UIFont.semibold(ofSize: 14)
midView.layoutIfNeeded()
// 点击滚动成果,放在这里
// mark & config
animate(idx: sIdx)
}
func animate(idx index: Int){UIView.animate(withDuration: 0.3) {let v = self.names[index]
let f = v.frame
self.line.frame = CGRect(x: f.origin.x, y: self.scroll.frame.maxY - 1, width: f.size.width, height: 1)
self.midView.layoutIfNeeded()} completion: {(_) in
self.line.isHidden = false
}
}
}