實現下圖的效果git
header 上面有一個 scroll viewgithub
圖中可視區域內,有 6 個app
能夠更新下面 cell 的內容ide
佈局精華佈局
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 } }
主要是狀態的管理,動畫
舊狀態抹去,spa
新狀態呈現debug
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 } } }