顏色封裝 + ClOPageView + 瀑布流
搭建主題框架

導航欄布局
- 改變導航欄的顏色
// 在 AppDelegate 中
UINavigationBar.appearance().barTintColor = .black // tintColor 是導航欄文字的顏色
- 改變狀態欄的顏色
// 蘋果推薦方法 在需要設置頁面 controller 中
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent // 默認為白色
}
// or
// 在 info.plist 中 設置
View controller-based status bar appearance 設置為 NO // 全局
// 在 AppDelegate 中
UIApplication.shared.statusBarStyle = .lightContent
// or
設置首頁 NavigationBar 注意事項
- 如果事件監聽方法為私有時 要在方法前面加
@objc
公開則不需要
// 事件監聽 --> 發送消息 --> 將方法包裝SEL --> 類方法列表 --> IMP
顏色封裝 :UIColor + Extension
- 拓展方法如果有參數 采用便利構造函數
// 自定義 RGB 顏色
convenience init(r : CGFloat, g : CGFloat, b : CGFloat, alpha : CGFloat = 1.0) {
self.init(red: r / 255.0, green: g / 255.0, blue: b / 255.0, alpha: alpha)
}
// 返回一個十六進制顏色
convenience init?(hex : String, alpha : CGFloat = 1.0) {
// 0xff0000
// 1.判斷字符串的長度是否符合
guard hex.characters.count >= 6 else {
return nil
}
// 2.將字符串轉成大寫
var tempHex = hex.uppercased()
// 3.判斷開頭: 0x/#/##
if tempHex.hasPrefix("0x") || tempHex.hasPrefix("##") {
tempHex = (tempHex as NSString).substring(from: 2)
}
if tempHex.hasPrefix("#") {
tempHex = (tempHex as NSString).substring(from: 1)
}
// 4.分別取出RGB
// FF --> 255
var range = NSRange(location: 0, length: 2)
let rHex = (tempHex as NSString).substring(with: range)
range.location = 2
let gHex = (tempHex as NSString).substring(with: range)
range.location = 4
let bHex = (tempHex as NSString).substring(with: range)
// 5.將十六進制轉成數字 emoji表情
var r : UInt32 = 0, g : UInt32 = 0, b : UInt32 = 0
Scanner(string: rHex).scanHexInt32(&r)
Scanner(string: gHex).scanHexInt32(&g)
Scanner(string: bHex).scanHexInt32(&b)
self.init(r : CGFloat(r), g : CGFloat(g), b : CGFloat(b))
}
- 沒有參數的擴展方法 用類方法
/// 隨機顏色
class func randomColor() -> UIColor {
return UIColor(r: CGFloat(arc4random_uniform(256)), g: CGFloat(arc4random_uniform(256)), b: CGFloat(arc4random_uniform(256)))
}
封裝 CLOPageView

類似網易新聞
titleView(UIScrollView) + contentView(UICollectionView)
-
結構視圖
如果類中的屬性沒有初始化,要在創建對象
super.init()
前賦值 否則會報錯
// MARK: - 定義屬性
fileprivate var titles: [String]
fileprivate var titleStyle: CLOPageStyle
fileprivate var childVcs: [UIViewController]
fileprivate var parentVc: UIViewController
init(frame: CGRect, titles: [String], titleStyle: CLOPageStyle, childVcs: [UIViewController], parentVc: UIViewController) {
self.titles = titles
self.titleStyle = titleStyle
self.childVcs = childVcs
self.parentVc = parentVc
super.init(frame: frame)
}
- 如果一個頁面有多個控件繼承自
UIScrollView
,需要設置這些控件的scrollsToTop
屬性,如果都為true
, 則點擊狀態欄都不會移動
collectionView.scrollsToTop = false
scrollView.scrollsToTop = false
在
CLOTitleView
中選擇添加UILabel
而不是UIButton
的理由:UIButton
中不好設置文字的屬性, 直接給UILabel
中添加相應的手勢方法
for (i, label) in titleLabels.enumerated()
可以遍歷數組取出數組中的元素及其下標方法
w = (titles[i] as NSString).boundingRect(with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: 0), options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: style.titleFont], context: nil).width
可以算出給定文字的寬度設置委托協議時能不繼承
NSObjectProtocol
就不繼承,因為NSObjectProtocol
里的方法一般用不到,又因為weak
只能修飾類,所以委托協議只需繼承自class
即可
// MARK: - 設置委托協議
protocol CLOTitleViewDelegate: class {
func titleView(_ titleView: CLOTitleView, didSelected currentIndex: Int)
}
weak var delegate: CLOTitleViewDelegate?
- 獲取
RGB
顏色的差值方法: 分別獲取R、G、B值再相減
class func getRGBDelta(_ firstColor : UIColor, _ seccondColor : UIColor) -> (CGFloat, CGFloat, CGFloat) {
let firstRGB = firstColor.getRGB()
let secondRGB = seccondColor.getRGB()
return (firstRGB.0 - secondRGB.0, firstRGB.1 - secondRGB.1, firstRGB.2 - secondRGB.2)
}
func getRGB() -> (CGFloat, CGFloat, CGFloat) {
guard let cmps = cgColor.components else {
fatalError("保證普通顏色是RGB方式傳入")
}
return (cmps[0] * 255, cmps[1] * 255, cmps[2] * 255)
}
- 顏色漸變動畫實現思路: 先判斷當前是左移還是右移,用移動前
scrollView
的偏移量與移動后的偏移量比較,獲得移動后下一個頁面的下標,再獲取當前頁面移動的百分比,通過代理讓titleView
作出相應處理,讓RGB顏色差值乘以百分比
- 底部滾動條的動畫類似,讓不同滾動條寬度之間的差值乘以這個百分比
瀑布流布局

- 瀑布流布局實現整體思路:采用
UICollectionView
自定義UICollectionViewFlowLayout
主要實現下面三個方法:
// MARK:- 準備布局
extension HYWaterfallLayout {
// 告訴當前 layout 需要改變
override func prepare() {
super.prepare()
...
// Cell --> UICollectionViewLayoutAttributes
// 設置好每個 cell 的 frame
...
}
// MARK:- 返回準備好所有布局
extension HYWaterfallLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return cellAttrs
}
}
// MARK:- 設置contentSize
extension HYWaterfallLayout {
override var collectionViewContentSize: CGSize {
return CGSize(width: 0, height: totalHeights.max()! + sectionInset.bottom)
}
}
- 每個
cell
的frame
計算
// 用一個屬性保存每一行 cell 的高度
fileprivate lazy var totalHeights : [CGFloat] = Array(repeating: self.sectionInset.top, count: self.cols)
// 計算當前一排最小高度及其列數
let minH = totalHeights.min()!
let minIndex = totalHeights.index(of: minH)!
// x 和 y 值
let cellX : CGFloat = sectionInset.left + (minimumInteritemSpacing + cellW) * CGFloat(minIndex)
let cellY : CGFloat = minH + minimumLineSpacing
// 更新當前的最小高度
totalHeights[minIndex] = minH + minimumLineSpacing + cellH
相當于將下一個
cell
加在當前一行中cellY
值最小的cell
的下面