繼續上一篇文章的進度,我們實際完成了微博基本框架的搭建,具體實現的效果如下左圖,但我們實際需要實現的效果為右圖,除去主要的頁面內容不談,僅僅下面的TabBar距離我們的需求就有相當的差距。因此本文着重於實現需要的效果。


再簡要匯總一下我們的需求:
1.我們要在TabBar原有四個按鈕的基礎上,再增加一個按鈕,作為撰寫微博的入口;
2.新加入的按鈕必須和原有按鈕一起,均勻分布在TabBar上;
3.新加入的按鈕只有圖片,沒有文字。
需求匯總如上,下面我們先進行一下分析和測試:
1.在TabBarController上直接拖拽一個UITabBarItem,經測試,無法拖上,此方式無法實現需求;
2.新創建ViewController,拖拽一個UITabBar,作為測試,直接修改默認的兩個TabBarItem中的其中一個,測試結果如下: 
使用此種方式后,TabBarItem的圖片完全無法正常顯示,並且圖片位置偏上,與要求不符;並且這種方式將改變原有的界面架構,因此這種方案也被放棄;
以上兩種方案均不能達到效果,也就意味着,要實現想要的效果,就只能使用最后一個辦法:自定義TabBar。
一、自定義控件
在iOS中,只要是能在屏幕上顯示的控件,那就可以進行自定義。因此,當系統提供的控件類型無法滿足使用需要時,使用自定義控件就是最好、最快捷的實現方式。
二、自定義TabBar
方案確定后,立刻開始實施。
創建UITabBar子類MainTabBar,並設置TabBarController中TabBar繼承的類為MainTabBar如下:

自此,設置的部分完畢,開始進入代碼:
1.創建按鈕並添加到TabBar中。
要往TabBar中添加按鈕,首先就要創建按鈕。
1 /// 懶加載撰寫按鈕,設置圖片並添加到TabBar中 2 lazy var composeButton: UIButton = { 3 let btn = UIButton() 4 btn.setImage(UIImage(named: "tabbar_compose_icon_add"), forState: UIControlState.Normal) 5 btn.setImage(UIImage(named: "tabbar_compose_icon_add_highlighted"), forState: UIControlState.Highlighted) 6 btn.setBackgroundImage(UIImage(named: "tabbar_compose_button"), forState: UIControlState.Normal) 7 btn.setBackgroundImage(UIImage(named: "tabbar_compose_button_highlighted"), forState: UIControlState.Highlighted) 8 // 添加到父控件中 9 self.addSubview(btn) 10 11 return btn 12 }()
2.調整按鈕布局
僅僅添加按鈕,只能證明在邏輯上,TabBar有一個按鈕子控件,但是並不足以顯示到屏幕上,如果要顯示出來,必須有准確的位置和尺寸才行。
對於有子控件的控件來說,每一次對子控件位置的調整都會調用其自身的 layoutSubviews 方法,因此我們可以先在這個方法中測試一番,為后面的工作做准備。實際測試的結果如下:

我們可以發現,在程序運行之后,layoutSubviews 方法被調用了兩次,並且每次都打印出了6個子控件,除去對應四個子視圖控制器的按鈕外,還有另外兩個控件,並且每一個控件都有詳細的 位置、尺寸等信息。
但是我們只需要關注其中的四個按鈕,原因是我們僅需要對按鈕的位置進行調整,再在中間位置插入我們創建的按鈕即可。而這四個按鈕相對於另外兩個控件最大的區別是這四個按鈕是響應者類型,而另外兩個控件卻不是,從它們的 userInteractionEnabled = NO 就可以看得出來。
初始代碼和實現效果分別如左、右圖:


這事我們發現應該居於右側的兩個按鈕也靠到了左邊,因此需要再做調整,並加上對中央撰寫按鈕的尺寸設置。最終實現如下:
private let buttonCount = 5 override func layoutSubviews() { super.layoutSubviews() /// 設置每個按鈕的寬高和基准尺寸 let w = self.bounds.width / CGFloat(buttonCount) let h = self.bounds.height let frame = CGRectMake(0, 0, w, h) var index: CGFloat = 0 for view in self.subviews as! [UIView] { if (view is UIControl && !(view is UIButton)) { /** * 當前控件是響應者對象,並且不是 UIButton 類型時參照設置尺寸位置 */ view.frame = CGRectOffset(frame, index * w, 0) index += (index == 1) ? 2 : 1 } } /** * 設置撰寫按鈕的尺寸和位置 */ self.composeButton.frame = frame self.composeButton.center = CGPointMake(self.bounds.width * 0.5, self.bounds.height * 0.5 ) }

3.設置撰寫按鈕的點擊事件
撰寫按鈕將在后續執行相應的操作,因此也是需要響應事件的,我們在這里就為撰寫按鈕添加點擊事件。
回到最初始的 MainTabBarController 類中,調整 viewDidLoad 方法,給撰寫按鈕添加點擊事件,同時實現點擊事件所調用的方法,並測試如下:

至此,自定義TabBar的工作全部完成。
雜談:
1.開發思路
在實際投入開發之前,開發人員通常並不能十分的確定應當使用何種方式或者手段完成開發任務,因此在伴隨開發過程中,開發人員通常是邊嘗試邊開發,不同的嘗試各種方案,最后找出可以實現的方案(也有可能此時找到的方案並不是最佳的)。因此在我們平時的開發中,對於獨立性比較強或者獨立性較強的功能或者模塊,可以采用在另一個項目中測試、完成、實現,然后再移植到正常開發的代碼中的模式。這種方式也是對正常開發影響最小的一種方法。
有的開發者在收到需求后,喜歡找一個自己認為可能實現的方式,然后進行開發,並寄希望一次運行調試成功,如果遇到了問題,一旦原本的方案走不通,便有可能放棄,重新開始。而實際上,遇到bug和問題是開發人員的家常便飯,因此,這種方式起始並不可取。在這里我建議 “還沒有能夠久經考驗的開發模式” 的開發人員遵循“從需求倒推”的開發方式和思路。
在蘋果目前提供的解決方案中,除去非常尖端的問題無法實現,其他的那些所謂“高大上”的難題都是可以解決的,前提是能找到合適的解決方案,除了在第一點中所說的多嘗試,更重要的是利用倒推的方法,可以逐步理清方案中所使用的技術點和思路,這也是我推薦這個方式的最重要的原因。
2.打印調試信息
在開發中,開發者通常使用打印調試信息的方式來判斷代碼邏輯是否正確。但由於目前 Swift 語言的輸出還不夠智能,因此打印出來的結果通常都直接串連在一起。而 OC 和 Swift 之間有着良好的互通性,並且 OC 在打印調試信息時的格式是非常干凈和整齊的,因此在打印 Swift 的調試信息時,可以將 Swift 對象轉換為 OC 對象,以獲得更清晰、明確的結果。
