一個(gè)Swift語(yǔ)言封裝的EmptyView顯示庫(kù),可作用于WKWebView巧号、UITableView、UICollectionView
示例
-
WKWebView
webViewEmptyGIF.gif -
UITableView
tableViewEmptyGIF.gif
- UICollectionView
引言:
項(xiàng)目開(kāi)發(fā)過(guò)程中當(dāng)網(wǎng)絡(luò)斷開(kāi)或者數(shù)據(jù)獲取失敗導(dǎo)致的界面顯示為空的情況下保檐,我們常會(huì)用到圖片加文字加刷新按鈕、文字加刷新按鈕或者純文本提醒的空界面顯示瘾境,所以對(duì)該功能實(shí)現(xiàn)的封裝封裝就顯得很有必要。
該技術(shù)封裝模塊使用Swift語(yǔ)言镰惦,參考OC封裝模塊的內(nèi)部實(shí)現(xiàn)邏輯迷守,利用runtime的系統(tǒng)方法交換機(jī)制,實(shí)現(xiàn)在WKWebView網(wǎng)頁(yè)加載界面旺入、UITableView兑凿、UICollectionView列表視圖等界面數(shù)據(jù)獲取失敗情況下的提醒顯示和刷新操作功能。
一:內(nèi)部實(shí)現(xiàn)原理
1茵瘾、通過(guò)runtime key值關(guān)聯(lián)HDEmptyView顯示對(duì)象
創(chuàng)建UIScrollView的extension對(duì)象UIScrollView+Empty類礼华,通過(guò)runtime key值關(guān)聯(lián)HDEmptyView顯示界面對(duì)象ly_emptyView ,該對(duì)象可根據(jù)調(diào)用界面的參數(shù)設(shè)置來(lái)控制空界面顯示的內(nèi)容龄捡、布局樣式卓嫂、顏色等。
struct RuntimeKey {
static let kEmptyViewKey = UnsafeRawPointer.init(bitPattern: "kEmptyViewKey".hashValue)
}
public var ly_emptyView: HDEmptyView? {
set {
objc_setAssociatedObject(self, RuntimeKey.kEmptyViewKey!, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
for view in self.subviews {
if view.isKind(of: HDEmptyView.classForCoder()) {
view.removeFromSuperview()
}
}
self.addSubview(ly_emptyView!)
self.ly_emptyView?.isHidden = true
}
get {
return objc_getAssociatedObject(self, RuntimeKey.kEmptyViewKey!) as? HDEmptyView
}
}
2聘殖、WKWebView 調(diào)用顯隱方法
如果是WKWebView的空數(shù)據(jù)界面顯示晨雳,根據(jù)界面加載成功或失敗的情況,調(diào)用顯示/隱藏空界面方法
public func ly_showEmptyView() {
self.ly_emptyView?.superview?.layoutSubviews()
self.ly_emptyView?.isHidden = false
//始終保持顯示在最上層
if self.ly_emptyView != nil {
self.bringSubview(toFront: self.ly_emptyView!)
}
}
public func ly_hideEmptyView() {
self.ly_emptyView?.isHidden = true
}
3奸腺、列表視圖顯隱控制
如果是UITableView餐禁、UICollectionView則根據(jù)DataSource判斷是否自動(dòng)顯示emptyView
首先獲取當(dāng)前列表視圖上cell的個(gè)數(shù)
//MARK: - Private Method
fileprivate func totalDataCount() -> NSInteger {
var totalCount: NSInteger = 0
if self.isKind(of: UITableView.classForCoder()) {
let tableView = self as? UITableView
if (tableView?.numberOfSections)! >= 1 {
for section in 0...(tableView?.numberOfSections)!-1 {
totalCount += (tableView?.numberOfRows(inSection: section))!
}
}
}
else if self.isKind(of: UICollectionView.classForCoder()) {
let collectionView = self as? UICollectionView
if (collectionView?.numberOfSections)! >= 1 {
for section in 0...(collectionView?.numberOfSections)!-1 {
totalCount += (collectionView?.numberOfItems(inSection: section))!
}
}
}
return totalCount
}
然后根據(jù)cell的個(gè)數(shù)判斷是否顯示 emptyView
fileprivate func getDataAndSet() {
if self.totalDataCount() == 0 {
show()
} else {
hide()
}
}
fileprivate func show() {
if self.ly_emptyView?.autoShowEmptyView == false {
self.ly_emptyView?.isHidden = true
return
}
ly_showEmptyView()
}
fileprivate func hide() {
if self.ly_emptyView?.autoShowEmptyView == false {
self.ly_emptyView?.isHidden = true
return
}
ly_hideEmptyView()
}
4、列表視圖的方法交換與界面刷新顯示
private static let swizzleMethod: Void = {
//insertSections
let originalSelector = #selector(insertSections(_:with:))
let swizzledSelector = #selector(ly_insertSections(_:with:))
HDRunTime.exchangeMethod(selector: originalSelector, replace: swizzledSelector, class: UITableView.self)
//deleteSections
let originalSelector1 = #selector(deleteSections(_:with:))
let swizzledSelector1 = #selector(ly_deleteSections(_:with:))
HDRunTime.exchangeMethod(selector: originalSelector1, replace: swizzledSelector1, class: UITableView.self)
//insertRows
let originalSelector2 = #selector(insertRows(at:with:))
let swizzledSelector2 = #selector(ly_insertRowsAtIndexPaths(at:with:))
HDRunTime.exchangeMethod(selector: originalSelector2, replace: swizzledSelector2, class: UITableView.self)
//deleteRows
let originalSelector3 = #selector(deleteRows(at:with:))
let swizzledSelector3 = #selector(ly_deleteRowsAtIndexPaths(at:with:))
HDRunTime.exchangeMethod(selector: originalSelector3, replace: swizzledSelector3, class: UITableView.self)
//reload
let originalSelector4 = #selector(reloadData)
let swizzledSelector4 = #selector(ly_reloadData)
HDRunTime.exchangeMethod(selector: originalSelector4, replace: swizzledSelector4, class: UITableView.self)
}()
//section
@objc func ly_insertSections(_ sections: NSIndexSet, with animation: UITableViewRowAnimation) {
ly_insertSections(sections, with: animation)
getDataAndSet()
}
@objc func ly_deleteSections(_ sections: NSIndexSet, with animation: UITableViewRowAnimation) {
ly_deleteSections(sections, with: animation)
getDataAndSet()
}
//row
@objc func ly_insertRowsAtIndexPaths(at indexPaths: [IndexPath], with animation: UITableViewRowAnimation){
ly_insertRowsAtIndexPaths(at: indexPaths, with: animation)
getDataAndSet()
}
@objc func ly_deleteRowsAtIndexPaths(at indexPaths: [IndexPath], with animation: UITableViewRowAnimation){
ly_deleteRowsAtIndexPaths(at: indexPaths, with: animation)
getDataAndSet()
}
//reloadData
@objc func ly_reloadData() {
self.ly_reloadData()
self.getDataAndSet()
}
二:使用方法
1突照、創(chuàng)建 HDEmptyView 界面顯示對(duì)象
//創(chuàng)建方式一:Block回調(diào)
let emptyV:HDEmptyView = HDEmptyView.emptyActionViewWithImageStr(imageStr: "net_error_tip", titleStr: "暫無(wú)數(shù)據(jù)帮非,點(diǎn)擊重新加載", detailStr: "", btnTitleStr: "點(diǎn)擊刷新") {
print("點(diǎn)擊刷新")
weakSelf?.reloadDataWithCount(count: 4)
}
//創(chuàng)建方式二:target/action
let emptyV:HDEmptyView = HDEmptyView.emptyActionViewWithImageStr(imageStr: "net_error_tip", titleStr: "暫無(wú)數(shù)據(jù),點(diǎn)擊重新加載", detailStr: "", btnTitleStr: "點(diǎn)擊刷新", target: self, action: #selector(reloadBtnAction)) as! HDEmptyView
2讹蘑、設(shè)置顯示參數(shù)屬性
emptyV.titleLabTextColor = UIColor.red
emptyV.actionBtnFont = UIFont.systemFont(ofSize: 19)
emptyV.contentViewOffset = -50
emptyV.actionBtnBackGroundColor = .white
emptyV.actionBtnBorderWidth = 0.7
emptyV.actionBtnBorderColor = UIColor.gray
emptyV.actionBtnCornerRadius = 10
3末盔、賦值給當(dāng)前顯示對(duì)象的ly_emptyView
webView.scrollView.ly_emptyView = emptyV
tableView.ly_emptyView = emptyV
collectionView.ly_emptyView = emptyV
//設(shè)置點(diǎn)擊空白區(qū)域是否有刷新操作
self.webView.scrollView.ly_emptyView?.tapContentViewBlock = {
//weakSelf!.loadingURL(urltring: "http://news.baidu.com/")
}
4、自定義空數(shù)據(jù)界面顯示
//自定義空數(shù)據(jù)界面顯示
func setupMyEmptyView() {
let emptyView: MyEmptyView = Bundle.main.loadNibNamed("MyEmptyView", owner: self, options: nil)?.last as! MyEmptyView
emptyView.reloadBtn.addTarget(self, action: #selector(reloadBtnAction(_:)), for: UIControlEvents.touchUpInside)
emptyView.frame = view.bounds
//空數(shù)據(jù)界面顯示
let emptyV:HDEmptyView = HDEmptyView.emptyViewWithCustomView(customView: emptyView) as! HDEmptyView
tableView.ly_emptyView = emptyV
tableView.ly_emptyView?.tapContentViewBlock = {
print("點(diǎn)擊界面空白區(qū)域")
}
tableView.ly_showEmptyView()
}
注意事項(xiàng):
是否自動(dòng)顯隱EmptyView的參數(shù) autoShowEmptyView 默認(rèn)設(shè)置是true座慰,列表視圖會(huì)根據(jù)界面cell的count數(shù)量自動(dòng)顯隱空界面陨舱。
當(dāng)設(shè)置成false時(shí)只能手動(dòng)調(diào)用 ly_showEmptyView() 和 ly_hideEmptyView() 方法進(jìn)行顯隱操作