实现下图的效果
层次关系为:
- 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
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。