#83 製作 IG 滑動後 profile tab 卡在上方的效果
模仿 IG App,製作照片牆滑動後 profile tab 卡在上方的效果。
有很多方法可以實現,以下示範利用 collection view,section top inset & auto layout 條件實現。
利用 flow layout 的 collection view 製作照片牆
參考以下連結,以繼承 UICollectionViewController 的 PrinceCollectionViewController 撰寫照片牆的程式。
設定 Section 的 Top inset
我們希望在照片牆的上方顯示 profile 資訊區塊,而區塊的高度為 413,因此我們將 section 的 top inset 設為 413,讓 cell 的上方多出一塊高度 413 points 的空間。
我們也可以從程式設定 section inset,透過定義 protocol UICollectionViewDelegateFlowLayout 的 collectionView(_:layout:insetForSectionAt:)。
class PrinceCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return UIEdgeInsets(top: 413, left: 0, bottom: 0, right: 0)}
加入 profile 資訊區塊的圖片
為了簡化程式,我們先偷懶用 image view 顯示圖片 profile,呈現 profile 資訊區塊。實際開發 App 時,再將它換成容納其它元件的 view 或 stack view 即可。
將 profile 資訊區塊加到 collection view
修改 viewDidLoad,將 profile 資訊區塊加到 collection view。
override func viewDidLoad() { super.viewDidLoad() let profileImageView = UIImageView(image: UIImage(named: "profile")) profileImageView.translatesAutoresizingMaskIntoConstraints = false collectionView.addSubview(profileImageView) profileImageView.heightAnchor.constraint(equalToConstant: 413).isActive = true profileImageView.leadingAnchor.constraint(equalTo: collectionView.frameLayoutGuide.leadingAnchor).isActive = true profileImageView.trailingAnchor.constraint(equalTo: collectionView.frameLayoutGuide.trailingAnchor).isActive = true let topConstraint = profileImageView.topAnchor.constraint(equalTo: collectionView.contentLayoutGuide.topAnchor) topConstraint.priority = UILayoutPriority(999) topConstraint.isActive = trueprofileImageView.bottomAnchor.constraint(greaterThanOrEqualTo: collectionView.safeAreaLayoutGuide.topAnchor, constant: 40).isActive = true let width = (collectionView.bounds.width - 1 * 2) / 3 let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout flowLayout?.itemSize = CGSize(width: width, height: width) flowLayout?.estimatedItemSize = .zero}
說明
- 將 profileImageView 加到 collection view。
let profileImageView = UIImageView(image: UIImage(named: "profile"))
profileImageView.translatesAutoresizingMaskIntoConstraints = false
collectionView.addSubview(profileImageView)
- 設定 profileImageView 的 auto layout 條件。
profileImageView.heightAnchor.constraint(equalToConstant: 413).isActive = trueprofileImageView.leadingAnchor.constraint(equalTo: collectionView.frameLayoutGuide.leadingAnchor).isActive = trueprofileImageView.trailingAnchor.constraint(equalTo: collectionView.frameLayoutGuide.trailingAnchor).isActive = truelet topConstraint = profileImageView.topAnchor.constraint(equalTo: collectionView.contentLayoutGuide.topAnchor)topConstraint.priority = UILayoutPriority(999)topConstraint.isActive = trueprofileImageView.bottomAnchor.constraint(greaterThanOrEqualTo: collectionView.safeAreaLayoutGuide.topAnchor, constant: 40).isActive = true
為了方便使用者切換照片牆,頁面滑動時 profile tab 將卡在上方,因此我們模仿 frame layout guide 設計浮動元件的技巧實現。對 scroll view 的 frame layout guide & content layout guide 不熟的朋友可參考以下連結。
設定 auto layout 條件時,我們讓圖片的下邊界(也是 profile tab 的下邊界)跟 safe area 的上邊界至少有著 40 points 的間距,如此即可讓頁面 scroll 到一定程度時,profile tab 仍會固定在上方。不過為了解決衝突,記得要將圖片和 contentLayoutGuide 上邊界對齊條件的 property 降為 999。
從 Interface Builder 設計 profile 區塊
除了從程式設計 profile 區塊,我們也可以從 Interface Builder 設計後再從程式加入,相關說明可參考以下連結。