EmptyPage(空白頁組件)原理與使用

app 顯示列表內容時, 在某一時刻可能數據爲空(等待網絡請求/網絡請求失敗)等, 添加一個空白指示頁將有效緩解用戶可能形成的焦慮或混亂. 並能夠幫助用戶處理問題.git

市面上已經有部分紅熟的空白頁框架,最典型的就是使用DZNEmptyDataSet.github

可是其使用DZNEmptyDataSetDelegate,DZNEmptyDataSetSource來定製空白頁元素,使用時較爲繁瑣.swift

筆者借鑑其原理的基礎上,製做了對標框架(單向對標)EmptyPage來簡化平常項目開發.api

前言

EmptyPage 歷時1年, 在我司項目中穩定使用迭代6個版本,算是比較穩定.ruby

支持UICollectionView & UITableView.網絡

ps: 目前階段只提供 swift 版本.app

實現原理

該核心部分 做爲一個單獨的子庫 實現, 可以使用 如下方式單獨引用.框架

pod 'EmptyPage/Core'
複製代碼

具體代碼可查閱 Github Link, 超級簡單.函數

  1. UIScrollView添加emptyView對象做爲空白頁實例:
    public extension UIScrollView {
      public var emptyView: UIView?
    }
    複製代碼
  2. Method Swizzling方式替換掉UITableView \ UICollectionView 中部分相關函數.如下拿UITableView 舉例:
    // DZNEmptyDataSet 對 autolayout 項目不太友好. (也可能本人沒深度使用...)
    // EmptyPage 
    // UITableView frame 變化相關函數
    open func layoutSubviews()
    open func layoutIfNeeded()
    // 數據源增減相關函數
    open func insertRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation)
    open func deleteRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation)
    open func insertSections(_ sections: IndexSet, with animation: UITableView.RowAnimation)
    open func deleteSections(_ sections: IndexSet, with animation: UITableView.RowAnimation)
    open func reloadData()
    複製代碼
  3. 在數據/frame變化時判斷空白頁顯示與隱藏.
    func setEmptyView(event: () -> ()) {
        oldEmptyView?.removeFromSuperview()
        event()
        guard bounds.width != 0, bounds.height != 0 else { return }
        var isHasRows = false
        let sectionCount = dataSource?.numberOfSections?(in: self) ?? numberOfSections
        for index in 0..<sectionCount {
          if numberOfRows(inSection: index) > 0 {
            isHasRows = true
            break
          }
        }
        isScrollEnabled = isHasRows
        if isHasRows {
          emptyView?.removeFromSuperview()
          return
        }
        guard let view = emptyView else{ return }
        view.frame = bounds
        addSubview(view)
        sendSubview(toBack: view)
      }
    複製代碼
  4. 使用佈局

    UITableView().emptyView = CustomView()
    UICollectionView().emptyView = CustomView()
    複製代碼

    UITableView().emptyView 第一次被賦值時纔會進行 Method Swizzling 相關函數.

模板視圖

DZNEmptyDataSet 的成功離不開其可高度定製化的模板視圖.但其繁瑣的 delegate apis 遠不如自定義視圖來的方便, 其對自定義視圖的支持也並不友善.

EmptyPage 優先支持 自定義視圖,並附贈 3 套能夠湊合看的模板視圖(支持超級高自定義調節,但畢竟UI咱們說了不算...)

採用 如下方式 則包含該部份內容:

pod 'EmptyPage'
複製代碼
  1. 自定義視圖
    • 僅支持autolayout佈局模式

      不使用 autolayout 模式:

      1. pod 'EmptyPage/Core'

      2. UITableView().emptyView = CustomView()

    • 自定義視圖須要autolayout實現自適應高

      能夠參考 內置的幾套模板視圖的約束實現.

    • 添加 EmptyPageContentViewProtocol 協議

      該協議默認實現了將自定義視圖居中約束至一個backgroundView上.

      通用性考慮: backgroundView.frame 與 tableView.frame 相同

      示例:

      class CustomView: EmptyPageContentViewProtocol{
          ...
      }
      
      let customView = CustomView()
      UITableView().emptyView = customView.mix()
      複製代碼

      不添加該協議,可採用如下方式:

      UITableView().emptyView = EmptyPageView.mix(view: customView)

    • 視圖關係

      視圖關係

  2. 內置模板視圖

    **特性: **

    1. 支持鏈式調用.
    2. 元素支持高度自定義.
    3. 一樣依照自定義視圖的標準實現.

    ps: 徹底等同於提早寫好的自定義模板視圖.

    • 目前能夠選擇3套基本的模板視圖.
      • 文字模板(EmptyPageView.ContentView.onlyText)
      • 圖片模板(EmptyPageView.ContentView.onlyImage)
      • 混合模板(EmptyPageView.ContentView.standard)
文字模板
圖片模板
混合模板
  • 使用

    • 示例:

      UITableView().emptyView = EmptyPageView.ContentView.standard
      	.change(hspace: .button, value: 80)
      	.change(height: .button, value: 60)
      	.change(hspace: .image, value: 15)
      	.config(button: { (item) in
      		item.backgroundColor = UIColor.blue
      		item.contentEdgeInsets = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20)
      	})
      	.set(image: UIImage(named: "empty-1002")!)
      	.set(title: "Connection failure", color: UIColor.black, font: UIFont.boldSystemFont(ofSize: 24))
      	.set(text: "Something has gone wrong with the internet connection. Let's give it another shot.", color: UIColor.black, font: UIFont.systemFont(ofSize: 15))
      	.set(buttonTitle: "TRY AGAIN")
      	.set(tap: {
      	// 點擊事件
      	})
      	.mix()
      複製代碼
  • Apis

    模板視圖中總結起來只有三種配置函數:

    • 約束配置函數: func change(...) -> Self

      約束函數具體可配置項採用枚舉的形式限定.(以避免改變/衝突自適應高度相關約束)

      enum HSpaceType { } // 修改視圖水平方向上的間距

      enum VSpaceType { } // 修改視圖垂直方向上的間距

      enum HeightType { } // 修改視圖具體高度

      例如:

      standardView.change(hspace: .button, value: 80)
      			.change(height: .button, value: 60)
      複製代碼
    • 控件配置函數: func set(...) -> Self

      提供了簡單的文本/字體/圖片/顏色配置.例如:

      standardView.set(title: "Connection failure", color: UIColor.black, font: UIFont.boldSystemFont(ofSize: 24))
      複製代碼
    • 控件自定義配置函數: func config(element: { (element) in ... }) -> Self

      返回一個完整的控件,可供深度配置. 例如:

      standardView.config(button: { (item) in
      	item.backgroundColor = UIColor.blue
      	item.contentEdgeInsets = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20)
      	})
      複製代碼
    • 視圖混合函數func mix():

      該函數由 EmptyPageContentViewProtocol 協議默認實現.

      做用: 將視圖約束至 backgroundView 上

      ps: 別忘了...

結尾

項目開源連接: Github/EmptyPage

我的博客連接: 四方

相關文章
相關標籤/搜索