一年半開發(fā)經(jīng)驗什湘,使用 RxSwift 構(gòu)建一個項目的基本框架,這種姿勢足夠優(yōu)雅嗎晦攒?

如果你跟我一樣闽撤,工作了一年半,相信你也經(jīng)歷過幾個大版本的迭代開發(fā)脯颜,甚至擁有幾個應(yīng)用的開發(fā)經(jīng)驗哟旗。那么在實現(xiàn)過那么多復(fù)雜的業(yè)務(wù)邏輯之后,你覺得栋操,利用純代碼闸餐,怎么樣去創(chuàng)建一個美、優(yōu)讼庇、簡以及便于管理的基本框架呢绎巨? 這兩天在思考這個問題近尚,于是有了以下想法(可能還不是很成熟蠕啄,如果你有更好的想法,請評論告知我,在此提前感謝)歼跟。

擼擼思路

今天以搭建一個項目的基本框架為例和媳,來談?wù)勥@個問題,最終的效果圖如下:

顯示效果

大家可以看到哈街,這確實是一個復(fù)雜項目的基礎(chǔ)留瞳,即使邏輯再復(fù)雜的界面,最開始也都是從這個界面起步的骚秦,而對于它她倘,我們需要做的:

  • 初始化幾個子控制器
  • 添加自定義的 TabBarView
  • 添加自定義的 NavigationBar
  • 添加自定的 TopBarColumnView

準(zhǔn)備工作

這里主要是準(zhǔn)備一些項目開發(fā)需要的資源和添加一些需要使用到的第三方庫,過程簡單作箍、瑣碎:

  • 導(dǎo)入需要使用到的全部圖片資源
  • 導(dǎo)入需要使用到的第三方: RxSwift硬梁、RxCocoa
  • 結(jié)合 UIStoryBoard 初始化幾個控制器,并在每個控制器上添加初始化顯示圖片

完成上述操作之后胞得,可得項目結(jié)構(gòu)如下:

目錄名稱 功能介紹
Main 項目的主要入口
Main - MenuViewController 菜單控制器荧止,作用與 TabBarController 相同
Business 存儲所有的業(yè)務(wù)邏輯模塊
Business - Home 首頁業(yè)務(wù)模塊
Business - Medium 書影音 業(yè)務(wù)模塊
Business - Broadcast 廣播 業(yè)務(wù)模塊
Business - Group 小組 業(yè)務(wù)模塊
Business - Profile 我的 業(yè)務(wù)模塊
Utility 存儲所有的功能模塊
Utility - Extension 存儲所有功能類的功能擴(kuò)展

當(dāng)然,大家覺得無聊就直接跳過這個操作阶剑,在 github 上 https://github.com/iJudson/RxBasicInterface.git <前期準(zhǔn)備> 該 commit 上直接拿到這一步的代碼

正題

結(jié)束了所有的準(zhǔn)備工作跃巡,接下來進(jìn)入正題:使用 RxSwift 創(chuàng)建 TabBarView 供菜單控制器 MenuViewController 使用

TabBarView 的創(chuàng)建

其實對于某些 View 的使用,即使是自己創(chuàng)建的牧愁,往往會在過了一段較長的時間之后素邪,再去使用這張 View 時,竟然發(fā)現(xiàn)無從下手猪半,需要再重溫下代碼娘香,特別是對于那些特別復(fù)雜的 View,有多個便利構(gòu)造函數(shù)办龄,或者特別多的子控件烘绽,而且需要根據(jù)不同情況去使用其中的某些控件...
還有一種情況, 你在公司與其他人協(xié)同工作時俐填,會不會發(fā)現(xiàn)要去使用到他創(chuàng)建的較為復(fù)雜的 View 時安接,需要或一些時間,感到特別煩呢英融?
針對這兩個問題盏檐,我的解決方案是:

<創(chuàng)建該 View 專屬的樣式類>
在這里,你可以先去跟你的同事或跟自己約定一套創(chuàng)建 View 的風(fēng)格驶悟,就是在創(chuàng)建該 View 的時候先去創(chuàng)建該 View 的專屬樣式類:用于管理該 View 的所有樣式:顏色胡野、子控件類型和數(shù)量、子控件的顯示位置...
我們想創(chuàng)建 TabBarView痕鳍,讓我們先來創(chuàng)建 TabBarView 的樣式管理類:

typealias TabBarItemElement = (normalImage: UIImage?, selectedImage: UIImage?, title: String?)

// TabBarView 的樣式管理類
struct TabBarStyle {
    //  子控件的元素集合   
    var itemElements: [TabBarItemElement]
     //  TabBarView 的顏色主題(默認(rèn)為白色)   
    var themeColor: UIColor = UIColor.white
    //  是否含有頂部分割線   
    var hasTopLine: Bool = true
    // 在初始化構(gòu)造器中硫豆,只去初始化必要的元素龙巨,避免初始化構(gòu)造器過于冗長
    init(elements: [TabBarItemElement] = []) {
        self.itemElements = elements
    }
}

<創(chuàng)建 TabBarView>
有了上面的樣式管理類,我們在該 View 中熊响,對外的屬性就特別少旨别,基本就只有兩個,這也大程度的增強(qiáng)了該 View 的封裝性汗茄。
1. 定義對外的樣式屬性:

// 主題樣式
var themeStyle: TabBarStyle = TabBarStyle() {
    didSet {
        // 通過屬性監(jiān)視器秸弛,外部可以通過該屬性更新 TabBarView 的整體樣式
        update(ThemeStyle: themeStyle)
    }
}

2. 創(chuàng)建便利構(gòu)造函數(shù):便于外部對該類的使用

convenience init(frame: CGRect? = nil, themeStyle: TabBarStyle) {
    // 當(dāng)外部不傳入 Frame 值時,我們默認(rèn)使用以下 Frame 值
    let barFrame = frame ?? CGRect(x: 0, y: screenHeight - tabBarHeight, width: screenWidth, height: tabBarHeight)
    self.init(frame: barFrame)
    // 設(shè)置背景顏色
    self.backgroundColor = themeStyle.themeColor
    // 根據(jù)樣式管理類洪碳,更新該 view 的主體樣式
    update(ThemeStyle: themeStyle)
    // 根據(jù)樣式管理類递览,決定是否添加頂部分割線
    add(TopLine: themeStyle.hasTopLine)
}

OK,到這里這個 View 大體上就創(chuàng)建完畢了瞳腌。是不是很簡單非迹,日后是不是也很便于管理?目前來看纯趋,這個 View 只有一個對外屬性憎兽,還有一個 convenience 構(gòu)造函數(shù),確實很便于我們管理吵冒,而對于樣式纯命,我們只需要去查看樣式管理類,就只有這個 View 的所有的樣式...
哈痹栖,其實接下來亿汞,還是有些細(xì)節(jié)要繼續(xù)完善。

3. 實現(xiàn)樣式更新的方法
在其中揪阿,我們只需要拿到外部傳進(jìn)來的樣式屬性疗我,去更新樣式。
因為這里的 TabBarItem 既有圖片南捂,又有標(biāo)題吴裤,所有外部需要將這兩個屬性都傳進(jìn)來。

fileprivate func update(ThemeStyle style: TabBarStyle) {
    // 所有的樣式元素數(shù)量
    let itemCount = style.itemElements.count
    let itemWidth = screenWidth/itemCount
    var commonItemFrame = CGRect(x: 0, y: 0, width: itemWidth, height: tabBarHeight)
    // 通過 for 循環(huán)溺健,將拿到的所有元素遍歷添加到 TabBarView 上
    for (index, element) in style.itemElements.enumerated() {
        commonItemFrame.origin.x = index * itemWidth
        let commonTabBarIem = CommonItemView(frame: commonItemFrame, image: element.normalImage, title: element.title)
        commonTabBarIem.setImage(element.selectedImage, for: .selected)
        commonTabBarIem.alignmentStyle = .upImageDownText(space: 2)
        commonTabBarIem.tag = index
        commonTabBarIem.titleLabel?.font = UIFont.systemFont(ofSize: 12)
        commonTabBarIem.setTitleColor(UIColor.gray, for: .normal)
        commonTabBarIem.setTitleColor(UIColor.black, for: .selected)
        
        self.addSubview(commonTabBarIem)
        // 這里我們添加監(jiān)聽者麦牺,監(jiān)聽每一個 Item 的點擊操作,當(dāng) item 被點擊時鞭缭,我們通過 Driver 序列剖膳,發(fā)送點擊事情到這和序列上
        let selectedIndex = commonTabBarIem.rx.controlEvent(.touchDown).throttle(0.5, scheduler: MainScheduler.instance).flatMapLatest { (_) -> Driver<Int> in
            return Driver<Int>.just(commonTabBarIem.tag)
            }.asDriver(onErrorJustReturn: 0)
        // 對外監(jiān)聽屬性的集合 
        selectedIndexes.append(selectedIndex)
        
        // 默認(rèn)選中第一個 tabBarItem
        commonTabBarIem.isSelected = (index == 0 ? true : false)
    }
}

在上面方法中,大家應(yīng)該有注意到岭辣,我們需要一個對外監(jiān)聽屬性的集合吱晒,監(jiān)聽用戶的點擊操作,因為一般 TabBar 有5個 Item沦童,故而這個集合一般都有五個對外監(jiān)聽屬性仑濒,屬性定義如下:

 //當(dāng)前選中的 index
 var selectedIndexes: [Driver<Int>] = []

4. 添加頂部分割線

 fileprivate func add(TopLine isExisting: Bool) {
    // 事先判斷是否需要添加分割線叹话,如果不需要,可直接 return 返回
    guard isExisting else {
        return
    }
   
    let lineHeight: CGFloat = 0.5
    let topLine = UIView(frame: CGRect(x: 0, y: -lineHeight, width: screenWidth, height: lineHeight))
    topLine.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.3)
    self.addSubview(topLine)
 }

到這一步的代碼躏精,大家可以在 https://github.com/iJudson/RxBasicInterface <創(chuàng)建 TabBarView> 當(dāng)前 commit 上獲仍ⅰ(注意鹦肿,現(xiàn)在在這個 commit 上運行程序矗烛,還是沒有任何效果,畢竟還沒有使用到該 TabBarView)

TabBarView 在 MenuViewController 的使用

TabBarView 是在 MenuViewController 去使用的箩溃, 在 MenuViewController 中也需要去監(jiān)聽 TabBarView 的 selectedIndexes 數(shù)組屬性瞭吃,從而監(jiān)聽到 TabBarView 上的點擊操作。而實際上涣旨,TabBarView 的使用歪架,由于使用了 RxSwift ,也變得特別簡單霹陡,也方便管理和蚪。
1. 我們在 viewDidLoad 添加兩個方法

  • 初始化 MenuViewController 中所有的子控制器,并默認(rèn)讓首頁控制器顯示在主頁上
  • 配置底部 TabBarView 烹棉,創(chuàng)建所需樣式管理類 TabBarViewStyle攒霹,并添加所需元素
 override func viewDidLoad() {
    super.viewDidLoad()
    
    initializeTopViewControllers()
    
    configureTabBar()
 }

接下來,我們只需要定義并完善上述兩個方法...

2. 初始化所有的子控制器

 fileprivate func initializeTopViewControllers() {
     // 通過 UIStoryboard 拿到所有的子導(dǎo)航控制器
    let homeNav = UIStoryboard.NavigationController.home
    let mediumNav = UIStoryboard.NavigationController.medium
    let broadcastNav = UIStoryboard.NavigationController.broadcast
    let groupNav = UIStoryboard.NavigationController.group
    let profileNav = UIStoryboard.NavigationController.profile
    // 并預(yù)先添加到 topViewControllers 數(shù)組中浆洗,當(dāng)點擊某一個 TabBarItem 時催束,根據(jù)需要添加具體的控制器
    topViewControllers = [homeNav, mediumNav, broadcastNav, groupNav, profileNav]
    
    // 初始化時,默認(rèn)添加首頁控制器
    self.addChildViewController(homeNav)
    homeNav.view.frame = self.view.bounds
    self.view.addSubview(homeNav.view)
    // 默認(rèn)顯示首頁控制器
    self.selectedViewController = homeNav
    // 更新狀態(tài)欄的狀態(tài)
    self.setNeedsStatusBarAppearanceUpdate()
 }

3. 配置 TabBarView伏社,監(jiān)聽其中的點擊屬性

  • 我們拿到所有的圖片和標(biāo)題屬性
  • 配置 TabBar 樣式管理類
  • 監(jiān)聽TabBarItem 的點擊操作(這里由于 RxSwift 會變得特別簡單)
  • 處理 TabBarItem 的點擊事件
 fileprivate func configureTabBar() {
    // 拿到五個 BarItem 上的元素(圖片和標(biāo)題)
    let barItemElements: [TabBarItemElement] = [
        (normalImage: UIImage(named: "ic_tab_home_gray_32x32_"), selectedImage: UIImage(named: "ic_tab_home_32x32_"), title: "首頁"),
        (normalImage: UIImage(named: "ic_tab_subject_gray_32x32_"), selectedImage: UIImage(named: "ic_tab_subject_32x32_"), title: "書影音"),
        (normalImage: UIImage(named: "ic_tab_timeline_gray_32x32_"), selectedImage: UIImage(named: "ic_tab_timeline_32x32_"), title: "廣播"),
        (normalImage: UIImage(named: "ic_tab_group_gray_32x32_"), selectedImage: UIImage(named: "ic_tab_group_32x32_"), title: "小組"),
        (normalImage: UIImage(named: "profile_normal_32x32_"), selectedImage: UIImage(named: "profile_active_32x32_"), title: "我的")
    ]
    // 創(chuàng)建管理樣式類
    let tabBarStyle = TabBarStyle(elements: barItemElements)
    // 拿到樣式類抠刺,創(chuàng)建 TabBarView 
    let tabBarView = TabBarView(themeStyle: tabBarStyle)
    self.view.addSubview(tabBarView)
    // 監(jiān)聽 TabBarView 的 selectedIndexes 屬性,當(dāng)有點擊操作時摘昌,觸發(fā)處理點擊事件的方法
    for selectedIndex in tabBarView.selectedIndexes {
        selectedIndex.drive(onNext: { [weak self] (index) in
            guard let strongSelf = self else {
                return
            }
            // 處理點擊事件
            strongSelf.handleTopControllerSelectionEvent(currentIndex: index)
            
        }).disposed(by: disposeBag)
    }
 }

而其點擊事件的處理如下:

fileprivate func handleTopControllerSelectionEvent(currentIndex: Int) {
   // 移除上一次選擇的控制器
    if let selectedViewController = selectedViewController {
        selectedViewController.willMove(toParentViewController: nil)
        selectedViewController.view.removeFromSuperview()
        selectedViewController.removeFromParentViewController()
        selectedViewController.viewWillDisappear(false)
    }
    // 添加當(dāng)前選擇的控制器速妖,并顯示
    let currentSelectedViewController = topViewControllers[currentIndex]
    self.addChildViewController(currentSelectedViewController)
    currentSelectedViewController.view.frame = self.view.bounds
    self.view.addSubview(currentSelectedViewController.view)
    currentSelectedViewController.didMove(toParentViewController: self)
    self.selectedViewController = currentSelectedViewController
    self.setNeedsStatusBarAppearanceUpdate()
    self.selectedIndex = currentIndex
    
    // 置頂 TabBar
    for subView in self.view.subviews {
        if let tabBar = subView as? TabBarView {
            self.view.bringSubview(toFront: tabBar)
        }
    }
}

OK,到這里聪黎,我們一個簡單的項目框架就已經(jīng)完成买优,其效果如下:

BasicInterface.gif

你可以在 https://github.com/iJudson/RxBasicInterface <TabBarView 在 MenuViewController 的使用> 的分支上拿到迄今為止的代碼。

后續(xù)

當(dāng)然一個簡單的項目框架還不止這些可能會有:

  • 導(dǎo)航欄 - NavigationBar
  • 頂部選擇欄 - TopBarColumnView

而這兩個 View 的創(chuàng)建跟 TabBarView 的創(chuàng)建是一模一樣的挺举,大家都試著去創(chuàng)建體會下杀赢。我這里也不再贅余,而最終的效果如下:

Last Version BasicInterface.gif

最終的代碼湘纵,大家可以在 https://github.com/iJudson/RxBasicInterface 中找到脂崔。

如果這個基本框架還有哪些地方可以繼續(xù)優(yōu)化,請不吝評論告知我梧喷。
如果你覺得這個基本框架還不錯砌左,歡迎 star脖咐。

感謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末汇歹,一起剝皮案震驚了整個濱河市屁擅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌产弹,老刑警劉巖派歌,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異痰哨,居然都是意外死亡胶果,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門斤斧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來早抠,“玉大人,你說我怎么就攤上這事撬讽∪锪” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵游昼,是天一觀的道長甘苍。 經(jīng)常有香客問我,道長酱床,這世上最難降的妖魔是什么羊赵? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮扇谣,結(jié)果婚禮上昧捷,老公的妹妹穿的比我還像新娘。我一直安慰自己罐寨,他們只是感情好靡挥,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸯绿,像睡著了一般跋破。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瓶蝴,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天毒返,我揣著相機(jī)與錄音,去河邊找鬼舷手。 笑死拧簸,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的男窟。 我是一名探鬼主播盆赤,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼贾富,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了牺六?” 一聲冷哼從身側(cè)響起颤枪,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎淑际,沒想到半個月后畏纲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡庸追,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年霍骄,在試婚紗的時候發(fā)現(xiàn)自己被綠了台囱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淡溯。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖簿训,靈堂內(nèi)的尸體忽然破棺而出咱娶,到底是詐尸還是另有隱情,我是刑警寧澤强品,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布膘侮,位于F島的核電站,受9級特大地震影響的榛,放射性物質(zhì)發(fā)生泄漏琼了。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一夫晌、第九天 我趴在偏房一處隱蔽的房頂上張望雕薪。 院中可真熱鬧,春花似錦晓淀、人聲如沸所袁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽燥爷。三九已至,卻和暖如春懦窘,著一層夾襖步出監(jiān)牢的瞬間前翎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工畅涂, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留港华,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓毅戈,卻偏偏與公主長得像苹丸,于是被迫代替她去往敵國和親愤惰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,498評論 25 707
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫赘理、插件宦言、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,022評論 4 62
  • 傷口就是 你以為它愈合了 陰天時就會痛 提醒你受過傷 傷口還在 你笑靨如花 卻讓別人看到眼角眉梢之間露出的端倪 有...
    哀慕熙榮閱讀 115評論 0 1
  • 猴年馬月,科普的帖子很多瞪醋,從6月5日到7月3日的29天里忿晕,猴年怎么來,馬月怎么說银受,應(yīng)有盡有践盼,只是在熱潮下,在BB外...
    氓夏閱讀 168評論 0 0
  • 01 大概五年前的時候,有天我突然感覺腳掌隱隱作痛顶霞,自查后發(fā)現(xiàn)長了個玉米粒大的東西肄程,而且越來越痛。去附近醫(yī)院檢查醫(yī)...
    王玬錦閱讀 251評論 0 2