iOS 面向協議方式封裝空白頁功能

爲了良好的交互體驗,相信你們在對待scrollView無數據時的提示頁都會使用一些第三方來定製,最典型的就是使用DZNEmptyDataSet。可是每一個界面都寫一堆與DZNEmptyDataSetDelegateDZNEmptyDataSetSource相關的代碼就不太好,那通常狀況下天然的就會採用繼承的方式來避免。而Swift除了能夠面向對象編程,它還能夠面向協議編程。那可不能夠也用協議來解決狀況呢?嘿嘿,這個能夠有,那咱們接下來就來試試怎麼經過協議的方式來避免上述狀況,而且實現一行代碼添加空白頁功能git

前言

ps: 目前 LXFProtocolTool 已更新屢次,代碼實現已有較大差異,有興趣的同窗能夠在閱讀完本文後再看看具體的代碼實現 EmptyDataSetablegithub

若是對面向協議有疑問的同窗能夠看下我以前的兩篇文章編程

iOS - Swift 面向協議編程(一)swift

iOS - Swift 面向協議編程(二)bash

以前的文章中提到了,協議除了起規範做用,還有別一個用處,就是賦予能力。咱們如今的目的就是讓目標控制器或者目標視圖在遵照咱們的協議後,就能夠有實現空白頁的功能。微信

1、基本實現

一、建立協議

// MARK:- 空視圖佔位協議
public protocol LXFEmptyDataSetable {
    
}
複製代碼

二、肯定面向類

肯定咱們面向的類,通常tableView或者collectionView都是寫在控制器裏,那咱們面向的類就規定爲UIViewController,或許也有人寫在UIView裏,不過這裏先按UIViewController來寫吧閉包

// MARK:- UIViewController - 空視圖佔位協議
public extension LXFEmptyDataSetable where Self : UIViewController {
    // 三、的實現的方法寫在這裏
}
複製代碼

三、定義功能方法

scrollView傳遞進來,讓咱們定義的方法來暗地裏作些操做ide

func lxf_EmptyDataSet(_ scrollView: UIScrollView) {
    scrollView.emptyDataSetDelegate = self
    scrollView.emptyDataSetSource = self
}
複製代碼

四、設置數據源和代理

三、定義功能方法中將delegatesource設置爲了self ,而協議是沒法遵照再次遵照其它協議的,那讓什麼來遵照對應的協議呢?要明白這裏的self指的是UIViewController,考慮到UIView的可能,這裏我就讓萬物對象之父NSObject來遵照,並實現對應的數據源方法和代理方法post

extension NSObject : DZNEmptyDataSetDelegate, DZNEmptyDataSetSource {
    public func image(forEmptyDataSet scrollView: UIScrollView!) -> UIImage! {
        // 返回提示圖片
    }
    public func title(forEmptyDataSet scrollView: UIScrollView!) -> NSAttributedString! {
        // 設置富文本標題
    }
    public func verticalOffset(forEmptyDataSet scrollView: UIScrollView!) -> CGFloat {
        // 設置縱向偏移
    }
}
複製代碼

2、定製空白頁

經過上述步驟後,只要讓UIViewController遵照咱們的協議,再調用一下lxf_EmptyDataSet方法就能夠實現數據空白頁了。可是,這樣直接寫死的方式很很差,有時候一些場景是須要咱們作出定製的,那怎麼實現定製呢?協議又不能有本身的變量來存放咱們的定製。ui

這裏先作出一個限定,咱們要使用重載方法來完成該功能,實現便可高定製,又可以使用默認定製。

回到剛剛的話題,使用UserDefaults來實現能夠嗎?能夠,可是比較麻煩,由於UserDefaults是單例,整個進程共用這一份資源,若是你當前controller遵照了咱們的協議LXFEmptyDataSetable並作出了定製,那麼當下一個controller在遵照協議後使用了默認定製時,那你要怎麼辦?還要區分scrollView,那就得保存當前scrollView,在退出當前controller後還要把對應的東西置空。好咯好咯,那你說到底要怎麼搞才最合適?

解決方案:拓展UIScrollView!!!有沒有發現?,很是地恰巧,咱們定義的方法lxf_EmptyDataSet須要外界將UIScrollView傳遞進來,在DZNEmptyDataSet的數據源方法和代理方法也有scrollView。那讓UIScrollView來攜帶咱們的定製就好啦。

一、定義定製相關的枚舉

這裏我定義了經常使用的定製相關的枚舉

public enum LXFEmptyDataSetAttributeKeyType {
    /// 縱向偏移(-50) CGFloat
    case verticalOffset
    /// 提示語(暫無數據) String
    case tipStr
    /// 提示語的font(system15) UIFont
    case tipFont
    /// 提示語顏色(D2D2D2) UIColor
    case tipColor
    /// 提示圖(LXFEmptyDataPic) UIImage
    case tipImage
    /// 容許滾動(true) Bool
    case allowScroll
}
複製代碼

二、拓展UIScrollView

UIScrollView定義一個定製相關的屬性字典

extension UIScrollView {
    private struct AssociatedKeys {
        static var lxf_emptyAttributeDict:[LXFEmptyDataSetAttributeKeyType : Any]?
    }
    /// 屬性字典
    var lxf_emptyAttributeDict: [LXFEmptyDataSetAttributeKeyType : Any]? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.lxf_emptyAttributeDict) as? [LXFEmptyDataSetAttributeKeyType : Any]
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.lxf_emptyAttributeDict, newValue as [LXFEmptyDataSetAttributeKeyType : Any]?, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}
複製代碼

三、完善lxf_EmptyDataSet方法

這裏咱們讓外界經過閉包的方式來定製本身的空白頁

// MARK:- UIViewController - 空視圖佔位協議
public extension LXFEmptyDataSetable where Self : UIViewController {
    func lxf_EmptyDataSet(_ scrollView: UIScrollView, attributeBlock: (()->([LXFEmptyDataSetAttributeKeyType : Any]))? = nil) {
        scrollView.lxf_emptyAttributeDict = attributeBlock != nil ? attributeBlock!() : nil
        scrollView.emptyDataSetDelegate = self
        scrollView.emptyDataSetSource = self
    }
}
複製代碼

四、使用定製屬性字典

這裏以返回提示圖片的方法爲例吧

public func image(forEmptyDataSet scrollView: UIScrollView!) -> UIImage! {
    guard let tipImg = scrollView.lxf_emptyAttributeDict?[.tipImage] as? UIImage else {
        return UIImage(named: "LXFEmptyDataPic")
    }
    return tipImg
}
複製代碼

五、外界的使用姿式

class LXFEmptyDemoController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        initUI()
    }
}

extension LXFEmptyDemoController: LXFEmptyDataSetable {
    fileprivate func initUI() {
        let tableView = UITableView()
        // ...
        
        // 高定製
        self.lxf_EmptyDataSet(tableView) { () -> ([LXFEmptyDataSetAttributeKeyType : Any]) in
            return [
                .tipStr:"喲喲喲",
                .verticalOffset:-150,
                .allowScroll: false
            ]
        }
        
        // 默認定製
        // self.lxf_EmptyDataSet(tableView)
    }
}
複製代碼

大功告成

3、開源庫

我對這個過程進行一次整理,並作成一個名爲 LXFProtocolTool 的庫並上傳至gitHub。可使用Cocoapods的方式來安裝使用

pod 'LXFProtocolTool'
複製代碼

我也將 iOS - Swift 面向協議編程(二) 中說起的經過協議便捷加載xib的功能也集成了進來。你們能夠根據本身的須要在Podfile寫明要安裝的功能

  • Xib加載
pod 'LXFProtocolTool/LXFNibloadable'
複製代碼
  • 空白視圖
pod 'LXFProtocolTool/LXFEmptyDataSetable'
複製代碼

建立這個庫的目的是爲了經過協議的方式來方便快捷地實現一些的實用功能,目前功能很少,不過日後會逐漸增長,或許你有什麼想實現的功能也能夠提出來,喜歡的就給個Star鼓勵下我吧 🚀 🚀 🚀

微信公衆號
相關文章
相關標籤/搜索