利用協(xié)議擴(kuò)展封裝ProgressHUD

原創(chuàng)文章轉(zhuǎn)載請注明出處

Mt. Rainier through the HUD by Mike Hendren on 500px.com
Mt. Rainier through the HUD by Mike Hendren on 500px.com

最近受《IF YOU'RE SUBCLASSING, YOU'RE DOING IT WRONG》的影響,思考自己的代碼中是否有類似的場景,可以將父類的方法抽象出來,以面向協(xié)議的編程(protocol oriented programming既POP)方式來思考問題。

文中提到了傳統(tǒng)的OOP會帶來的各種問題,其中就有本文中要優(yōu)化的上帝類(God classes - 承擔(dān)著很多subclasses需要的重要高層級代碼的所有責(zé)任),Blobs(有過多職權(quán)的classes),Lava Flow(因為含有太多的非法代碼導(dǎo)致任何人都不敢碰的classes)等等這些種反面模式(anti patterns)。

ProgressHUD是每個應(yīng)用都會使用的控件,在GitHub上搜索HUD,至少可以找到100種以上相似的Swift/Objective-C開源控件,他們功能類似,只是展現(xiàn)效果不同。starts比較高的有:MBProgressHUDSVProgressHUDJGProgressHUDMMProgressHUD等。

1.0版本

自11年底開始從事iOS開發(fā)以來,類似的控件嘗試過很多,最早是MBProgressHUD,后來感覺其接口封裝方式不好,使用時不是很方便,又陸續(xù)嘗試了許多同類型的控件。每次進(jìn)行替換的時候,就要將散落在各處的調(diào)用方法統(tǒng)一進(jìn)行替換,雖然IDE提供的replace功能很方便,但是最后我還是將接口進(jìn)行了統(tǒng)一抽象,梳理出常用的方法,最后放到了項目中的BaseViewController中。

    //類似Android toast的純文本提示框
    func showTextOnlyView(message: String!) {
        ...
    }
    
    //等待框
    func showWaitingView(message: String?) {
        ...
    }
    
    //隱藏等待框
    func hideWaitingView() {
        ...
    }
    
    //n秒后隱藏等待框,可傳入回調(diào)函數(shù)
    func hideWaitingViewDelay(interval: Double = 0.3, completion: ((Void) -> Void)? = nil ) {
        ...
    }
    
    //成功提示框,可傳入回調(diào)函數(shù)
    func hideWaitingViewSuccess(message: String? = nil, completion: ((Void) -> Void)? = nil) {
        ...
    }
    
    //失敗提示框,可傳入回調(diào)函數(shù)
    func hideWaitingViewError(message: String? = nil, completion: ((Void) -> Void)? = nil) {
        ...
    }

上述方法基本上滿足了我在App中的使用需求,只要是繼承自BaseViewController的UIViewController,都可以直接調(diào)用以上方法,無需import第三方庫。而當(dāng)我哪天又看上某個漂亮的第三方庫的時候,我只需要修改基類中的方法,無需對全工程的代碼進(jìn)行替換。

問題來了,如果UIViewController不是繼承自我自定義的BaseViewController呢?如果我想在UIView中使用這些方法呢?聰明如我想到了將代碼抽離出來,放到獨立的類中,又或者對第三方庫做一個擴(kuò)展。

2.0版本

將上述方法封裝到一個獨立的類和直接對第三方庫做擴(kuò)展有什么區(qū)別嗎?區(qū)別大概就是做擴(kuò)展的話,在我調(diào)用方法的類中需要import對應(yīng)的庫,而我實在不想在替換第三方庫的時候還要滿世界去修改import代碼,那我們就選擇封裝一個ProgressHUD的類吧。

import SomeProgressHUD

class ProgressHUD {
    
    class func showTextOnlyView(message: String!) {
        ...
    }
    
    class func showWaitingView(message: String?) {
        ...
    }
    
    class func hideWaitingView() {
        ...
    }
    
    class func hideWaitingViewDelay(interval: Double = 0.3, completion: ((Void) -> Void)? = nil ) {
        ...
    }
    
    class func hideWaitingViewSuccess(message: String? = nil, completion: ((Void) -> Void)? = nil) {
        ...
    }
    
    class func hideWaitingViewError(message: String? = nil, completion: ((Void) -> Void)? = nil) {
        ...
    }    
}

現(xiàn)在我們可以隨便使用了,只需要import ProgressHUD,就可以通過ProgressHUD.xxx這樣的形式進(jìn)行調(diào)用。同樣的,當(dāng)我哪天又看上某個漂亮的第三方庫的時候,我只需要修改ProgressHUD類中的實現(xiàn),無需對全工程的代碼進(jìn)行替換。

從封裝的角度來講,現(xiàn)在這個版本已經(jīng)找不出什么太大的問題了。通過封裝類適配了第三方庫,代碼中對于使用哪個庫并不關(guān)心,方便了代碼維護(hù)。

前文說了,最近受《IF YOU'RE SUBCLASSING, YOU'RE DOING IT WRONG》的影響,心里又長草了,因為不喜歡每次調(diào)用接口都要加ProgressHUD.這樣的前綴,懷疑自己不是雙子座??而是處女座??。

3.0版本

好吧,既然這樣不如就嘗試一下協(xié)議擴(kuò)展。

import Foundation
import SomeProgressHUD

// MARK: - ProgressHUDable

protocol ProgressHUDable {
    func showTextOnlyView(message: String!)
    func showWaitingView(message: String?)
    func hideWaitingView()
    func hideWaitingViewDelay(interval: Double, completion: ((Void) -> Void)?)
    func hideWaitingViewSuccess(message: String?, completion: ((Void) -> Void)?)
    func hideWaitingViewError(message: String?, completion: ((Void) -> Void)?)
}

extension ProgressHUDable where Self: UIViewController {
    
    func showTextOnlyView(message: String!) {
        ...
    }
    
    func showWaitingView(message: String?) {
        ...
    }
    
    func hideWaitingView() {
        ...
    }
    
    func hideWaitingViewDelay(interval: Double = 0.3, completion: ((Void) -> Void)? = nil ) {
        ...
    }
    
    func hideWaitingViewSuccess(message: String? = nil, completion: ((Void) -> Void)? = nil) {
        ...
    }
    
    func hideWaitingViewError(message: String? = nil, completion: ((Void) -> Void)? = nil) {
        ...
    }
}

extension ProgressHUDable where Self: UIView {
    ...
}

需要使用的類就帶上該協(xié)議

class LoginViewController: UIViewController, ProgressHUDable {
    ...
}

終于可以擺脫前綴了,感覺世界清靜了。附上我現(xiàn)在使用的代碼,沒錯我最近看上的是KVNProgressAsync是對GCD封裝的語法糖,使用起來很方便,順便推薦一下。

//
//  ProgressHUDable.swift
//  MyProject
//
//  Created by chenjsa on 16/3/1.
//  Copyright ? 2016年 MyCompany. All rights reserved.
//

import Foundation
import KVNProgress
import Async

// MARK: - ProgressHUDable

protocol ProgressHUDable {
    func showTextOnlyView(message: String!)
    func showWaitingView(message: String?)
    func hideWaitingView()
    func hideWaitingViewDelay(interval: Double, completion: ((Void) -> Void)?)
    func hideWaitingViewSuccess(message: String?, completion: ((Void) -> Void)?)
    func hideWaitingViewError(message: String?, completion: ((Void) -> Void)?)
}

extension ProgressHUDable where Self: UIViewController {
    
    func showTextOnlyView(message: String!) {
        KVNProgress.configuration().circleSize = 0
        KVNProgress.showErrorWithStatus(message)
    }
    
    func showWaitingView(message: String?) {
        var msg = message
        if ValidatorUtils.isEmptyString(msg) {
            msg = NSLocalizedString("wait", comment: "")
        }
        KVNProgress.configuration().circleSize = 80
        KVNProgress.showWithStatus(msg)
    }
    
    func hideWaitingView() {
        KVNProgress.dismiss()
    }
    
    func hideWaitingViewDelay(interval: Double = 0.3, completion: ((Void) -> Void)? = nil ) {
        Async.main(after: interval) { _ in
            KVNProgress.dismissWithCompletion(completion)
        }
    }
    
    func hideWaitingViewSuccess(message: String? = nil, completion: ((Void) -> Void)? = nil) {
        var msg = message
        if ValidatorUtils.isEmptyString(msg) {
            msg = NSLocalizedString("success", comment: "")
        }
        KVNProgress.configuration().circleSize = 80
        KVNProgress.showSuccessWithStatus(msg, completion: completion)
    }
    
    func hideWaitingViewError(message: String? = nil, completion: ((Void) -> Void)? = nil) {
        var msg = message
        if ValidatorUtils.isEmptyString(msg) {
            msg = NSLocalizedString("error", comment: "")
        }
        KVNProgress.configuration().circleSize = 80
        KVNProgress.showErrorWithStatus(msg, completion: completion)
    }

}

extension ProgressHUDable where Self: UIView {
    
    func showTextOnlyView(message: String!) {
        KVNProgress.configuration().circleSize = 0
        KVNProgress.showErrorWithStatus(message)
    }
    
    func showWaitingView(message: String?) {
        var msg = message
        if ValidatorUtils.isEmptyString(msg) {
            msg = NSLocalizedString("wait", comment: "")
        }
        KVNProgress.configuration().circleSize = 80
        KVNProgress.showWithStatus(msg)
    }
    
    func hideWaitingView() {
        KVNProgress.dismiss()
    }
    
    func hideWaitingViewDelay(interval: Double = 0.3, completion: ((Void) -> Void)? = nil ) {
        Async.main(after: interval) { _ in
            KVNProgress.dismissWithCompletion(completion)
        }
    }
    
    func hideWaitingViewSuccess(message: String? = nil, completion: ((Void) -> Void)? = nil) {
        var msg = message
        if ValidatorUtils.isEmptyString(msg) {
            msg = NSLocalizedString("success", comment: "")
        }
        KVNProgress.configuration().circleSize = 80
        KVNProgress.showSuccessWithStatus(msg, completion: completion)
    }
    
    func hideWaitingViewError(message: String? = nil, completion: ((Void) -> Void)? = nil) {
        var msg = message
        if ValidatorUtils.isEmptyString(msg) {
            msg = NSLocalizedString("error", comment: "")
        }
        KVNProgress.configuration().circleSize = 80
        KVNProgress.showErrorWithStatus(msg, completion: completion)
    }
    
}

我是咕咕雞,一個還在不停學(xué)習(xí)的全棧工程師。
熱愛生活,喜歡跑步,家庭是我不斷向前進(jìn)步的動力。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容