自定義 UITabBar 總結(jié)(一個(gè)模擬 instagram TabBar 的例子)轉(zhuǎn)

最近剛換工作,開發(fā)一個(gè)新的 app 要重新搭建框架,像 UINavigaiontControllerhe UITabBarController之類的控件,雖然重要,項(xiàng)目一旦穩(wěn)定就很少去弄這些東西了,本來打算自己寫一篇總結(jié)也算做做筆記,但是發(fā)現(xiàn)前人總結(jié)的強(qiáng)多了。自認(rèn)難以達(dá)此程度,所以原文照搬,勉強(qiáng)也算自己的一個(gè)筆記吧。

開源萬歲!

碰到了跟 TabBar 有關(guān)的東西,希望自己盡量對(duì) TabBar 的使用了解清楚而不是直接復(fù)制粘貼,所以整體研究一番,在此總結(jié)。

內(nèi)容主要跟 TabBar 的樣式修改有關(guān),涉及到一點(diǎn)點(diǎn)點(diǎn)擊事件。文中提到的諸如比較重要的方法、比較重要的屬性的重要性均是相對(duì)本文內(nèi)容而言。

要實(shí)現(xiàn)的結(jié)果如圖:



其中,點(diǎn)擊中間按鈕時(shí),不激活那個(gè) tab 頁,而是執(zhí)行自定義的任務(wù)。(因?yàn)?instagram 中間那個(gè)按鈕功能就是和其他的不一樣嘛。)

相關(guān)文檔

首先,敬上四篇相關(guān)文檔:

先來一點(diǎn)沒那么有趣的東西:D。

UITabBarController
當(dāng)你使用 UITabBarController 的時(shí)候,你可以新建一個(gè)類繼承 UITabBarController 來管理你的 TabBar 及相關(guān)事件。

UITabBarController 包含許多屬性,與本文相關(guān)的比較重要的幾個(gè)屬性是:

  • delegate:一個(gè) UITabBarControllerDelegate 對(duì)象,用于追蹤和處理一些 TabBar 事件。
  • tabBar: 一個(gè) UITabBar 對(duì)象,直接一點(diǎn)說就是你在屏幕上看到的那個(gè) TabBar。
  • viewControllers:每個(gè) Tab 頁面對(duì)應(yīng)的 viewController 組成的列表??梢酝ㄟ^排列順序值獲取對(duì)應(yīng) viewController,比如第一個(gè) tab 的 viewController 為:self.viewControllers[0]。

UITabBarControllerDelegate
一個(gè)比較重要的方法:

  • override func tabBarController(_ tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool
    用于決定被選中的 tab 是否激活。當(dāng)返回值是 false 時(shí),保留在之前的 tab 頁面;當(dāng)返回值是 true 時(shí),激活當(dāng)前選中的 tab 頁面。

UITabBar

幾個(gè)重要的屬性:

  • items:一個(gè) UITabBarItem 列表,即每一個(gè) tab 按鈕對(duì)象組成的列表。
  • selectedItem:當(dāng)前被選中的 TabBarItem。
  • tintColor:覆蓋被選中的 TabBarItem 圖片的顏色。(默認(rèn)為藍(lán)色。就是選中時(shí)圖標(biāo)的顏色。)
2.png
*   barTintColor:整個(gè) TabBar 的背景顏色。

UITabBarItem

幾個(gè)重要的方法和屬性:

7.png
8.png
9.png
10.png
11.png
12.gif
13.png
15.png
16.png
17.png
18.gif
19.png
20.png
21.png
22.gif
23.png
  • 初始化方法(init(…))??梢酝ㄟ^ init 方法新建 TabBarItem,并賦予指定初始值,比如圖片、圖片顏色、選中狀態(tài)時(shí)的圖片及顏色、tag 值等等。
  • badgeValue:看圖。通過設(shè)置這個(gè)值可以直接得到這個(gè)效果,我覺得挺有意思的,很省事的樣子。


  • selectedImage:TabBarItem 被選中時(shí)顯示的圖片,默認(rèn)和未選中狀態(tài)圖片一樣。

TabBar 基本樣式的設(shè)置

我目前的看法是,能用 storyboard 解決的一定不用代碼。所以基本樣式設(shè)置全部在 storyboard 完成。

TabBar

選中 TabBar,打開右側(cè)的屬性面板。


  • 一般都不用修改 item Positioning,默認(rèn)是居中均勻分布,應(yīng)該符合大部分需求。
  • Translucent 半透明度可以根據(jù)需要勾選。像微博的客戶端就是半透明的,而instagram 不是。
  • 沒有特別需求的話,可以直接在這里設(shè)置 Image Tint。我們這個(gè)例子在代碼里面設(shè)置。
  • Style:系統(tǒng)提供了 Default 默認(rèn)的白色背景和可選的 Black 黑色背景??筛鶕?jù)需要選擇。如圖:

TabBarItem

選中 tab 頁面的 viewController 底部的 tab 圖標(biāo)。右側(cè)的屬性面板如圖:

[圖片上傳失敗...(image-65edf2-1555559141135)]

先看下面的:

  • Title:就是在圖標(biāo)下顯示的文字??梢詾榭眨梢酝ㄟ^ Title Position 調(diào)整位置。
  • Image:圖標(biāo)的圖片。
  • Tag:當(dāng)前 TabBarItem 的標(biāo)志,設(shè)置一個(gè)值方便在代碼里找到對(duì)應(yīng)的 TabBarItem。
  • Enabled:沒什么好說的:)

然后是這些:

  • Badge:就是之前提到過的紅點(diǎn)??梢栽谶@里設(shè)置一個(gè)初始值。
  • System Item:一些系統(tǒng)自帶的 Tab 圖標(biāo),根據(jù)需要自行取用。一般還是用自己的圖吧。
  • Selected Image:tab 被選中的時(shí)候的圖片。
  • Title Postition:設(shè)置 title 的位置。
    可以如圖設(shè)置水平偏移垂直偏移。

[圖片上傳失敗...(image-1f9a1b-1555559141135)]

值得注意的是:水平偏移對(duì)圖標(biāo)有影響,垂直偏移對(duì)圖標(biāo)沒有影響。效果如圖,紅線是我自己加的一條中線,用于對(duì)比。

[圖片上傳失敗...(image-d046a-1555559141135)]

One more thing :)

屬性面板右邊有一個(gè)設(shè)置尺寸的面板:
[圖片上傳失敗...(image-948401-1555559141135)]

在這里可以設(shè)置圖標(biāo)的偏移量,從而修改圖標(biāo)的位置。

因?yàn)槟J(rèn)圖標(biāo)在 Title上面,位置偏高,就算沒有 Title 圖標(biāo)也在那個(gè)位置。所以當(dāng)我們不用 Title 的時(shí)候,像 instagram,需要調(diào)整圖標(biāo)的位置,讓它看起來居中一點(diǎn)。

一種是直接在面板設(shè)置,一種是用代碼設(shè)置。
[圖片上傳失敗...(image-399321-1555559141135)]
tabBarItem.imageInsets = UIEdgeInsetsMake(6, 0, -6, 0);
對(duì)比效果如圖:
[圖片上傳失敗...(image-6e0a5f-1555559141135)]

注意:top 和 bottom 要設(shè)置成相反數(shù),不然點(diǎn)擊 tab 圖標(biāo)時(shí) image 的大小會(huì)一直改變?。?!
如圖:
[圖片上傳失敗...(image-85f0f5-1555559141135)]

一個(gè)建議

有時(shí)候需要調(diào)整圖標(biāo)的大小,不建議用代碼修改或者視圖通過 image inset 來修改。

建議做圖標(biāo)的時(shí)候,就在圖標(biāo)周圍留白。比如 75px 的圖片,其中上下左右各留白 20px,這樣圖標(biāo)就小了。

另外,根據(jù) Apple 制定的標(biāo)準(zhǔn),tab 圖標(biāo)準(zhǔn)備 3 個(gè)尺寸,分別是 75 px(@3x),55px(@2x)和25px。

[圖片上傳失敗...(image-6c98b7-1555559141135)]

TabBar 個(gè)性化樣式設(shè)置要求(大家好,重點(diǎn)要開始了:D)

經(jīng)過之前一些簡(jiǎn)單的步驟,你將得到如圖效果:

[圖片上傳失敗...(image-1caa69-1555559141135)]

記得將五個(gè) TabBarItem 的 tag 設(shè)置為 0 - 5,方便以后。

但是我們要實(shí)現(xiàn)的不是這么簡(jiǎn)單。

要求:

  • 選中 TabBarItem 背景顏色為黑色。
  • 未選中 TabBarItem 背景顏色為某種灰色。
  • 選中 TabBarItem 圖標(biāo)顏色為白色。
  • 未選中 TabBarItem 圖標(biāo)顏色為某種灰色。
  • 中間按鈕始終背景顏色為某種藍(lán)色、圖標(biāo)顏色為白色。

如何在 UITabBarController 中修改樣式

新建一個(gè) Cocoa Touch Class,取名叫MainTabBarViewController,繼承 UITabBarController。去 storyboard 把作為入口的那個(gè) UITabBarController 的類修改為MainTabBarViewController。

這個(gè)時(shí)候,我們就可以在MainTabBarViewController中對(duì) TabBar 進(jìn)行修改了。

首先來試驗(yàn)一下。

<figure class="highlight plain" style="box-sizing: border-box; color: rgb(209, 217, 225); background: rgb(33, 35, 38); font-family: Menlo, "Meslo LG", monospace; border-radius: 4px; padding: 10px 15px; overflow-x: auto; margin: 1rem 0px;">

|

<pre style="box-sizing: border-box; font-family: Menlo, "Meslo LG", monospace; font-size: 13px; padding: 0px; line-height: 22px; border-radius: 4px; border: none; overflow-x: auto; hyphens: manual; background: none;">import UIKit

class MainTabBarViewController: UITabBarController {

override func viewDidLoad() {
super.viewDidLoad()

//設(shè)置 tabBar 圖標(biāo)選中時(shí)的顏色
self.tabBar.tintColor = UIColor.whiteColor()
//遍歷全部 tabBarItem,設(shè)置 badege 值
for one in self.tabBar.items! {
one.badgeValue = "100"
}

}
}
</pre>

|

</figure>

WOW。好酷。

[圖片上傳失敗...(image-7f7f62-1555559141135)]
28691)]

  • 選中 TabBarItem 圖標(biāo)顏色為白色。 達(dá)成√??!
  • 未選中 TabBarItem 圖標(biāo)顏色為某種灰色。 達(dá)成√?。?/li>

然后還順便嘗試了設(shè)置 badge 值。

如何通過代碼設(shè)置單個(gè) TabBarItem 背景顏色

之前提到過 barTintColor 這個(gè)屬性。通過self.tabBar.barTintColor = UIColor().greyColor()就可以把背景設(shè)置為灰色了。但是這并沒有完成被選中 TabBarItem 背景顏色變?yōu)楹谏囊蟆?/p>

事實(shí)上,并沒有直接設(shè)置單個(gè) TabBarItem 背景顏色的方法。

解決方案

重寫 viewDidAppear 方法和 didSelectItem 方法。didSelectItem 是每當(dāng)有 tabBarItem 被選中時(shí)執(zhí)行的函數(shù)。

import UIKit

class MainTabBarViewController: UITabBarController {

 //定義一個(gè)用于存儲(chǔ)背景層 view 的列表
 var tabBarItemBgViews: [UIView] = []

 override func viewDidLoad() {
 super.viewDidLoad()

 self.tabBar.tintColor = UIColor.whiteColor()
 for it in self.tabBar.items! {
 it.badgeValue = "100"
 }

 }

 override func viewDidAppear(animated: Bool) {
 //初始化需要用到的尺寸數(shù)值
 let ITEM_WIDTH = self.tabBar.frame.width / 5
 let ITEM_HEIGHT = CGFloat(49)

 //初始化 tab bar item 背景層
 for one in self.tabBar.items! {
 let vw = UIView()
 vw.backgroundColor = UIColor(red: 37/255, green: 29/255, blue: 42/255, alpha: 1)
 vw.frame = CGRectMake(ITEM_WIDTH * CGFloat(one.tag), 0, ITEM_WIDTH, ITEM_HEIGHT)
 self.tabBarItemBgViews.append(vw)
 //將背景層插入到 index 為 1 的位置,這樣背景層就在圖標(biāo)下面。記住這個(gè) 1 就好了:)
 tabBar.insertSubview(vw, atIndex: 1)
 }

 //修改第一個(gè) item 的背景色為選中狀態(tài)顏色
 self.tabBarItemBgViews[0].backgroundColor = UIColor.blackColor()

 //自定義中間按鈕樣式
 self.tabBarItemBgViews[2].backgroundColor = UIColor(red: 17/255, green: 86/255, blue: 136/255, alpha: 1)
 }

 override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
 //當(dāng)選中的 TabBarItem 不是中間那個(gè)時(shí),改變背景顏色
 if item.tag != 2 {
 for one in tabBar.items! {
 //將當(dāng)前被選中的 TabBarItem 背景顏色設(shè)置為黑色
 if one.tag == item.tag {
 self.tabBarItemBgViews[one.tag].backgroundColor = UIColor.blackColor()
 } else {
 //將其他 TabBarItem 背景顏色設(shè)置為灰色
 self.tabBarItemBgViews[one.tag].backgroundColor = UIColor(red: 37/255, green: 29/255, blue: 42/255, alpha: 1)
 }
 }

 //將中間 TabBarItem 的背景顏色重置為藍(lán)色
 self.tabBarItemBgViews[2].backgroundColor = UIColor(red: 17/255, green: 86/255, blue: 136/255, alpha: 1)
 } 
 }

}

通過上面的代碼,得到了:

[圖片上傳失敗...(image-cc298d-1555559141135)]

解釋:為什么是重寫 viewDidAppear 方法而不是 viewDidLoad

如果把上面那段代碼寫在viewDidLoad方法中,會(huì)出現(xiàn)如下情況:

[圖片上傳失敗...(image-f14ec2-1555559141135)]

可以看到,第一個(gè) TabBarItem 沒有圖標(biāo),而且點(diǎn)擊無效。

簡(jiǎn)單來說,在viewDidLoad的時(shí)候,頁面的那些視圖大概還沒準(zhǔn)備好,所以插入的時(shí)候會(huì)有錯(cuò)誤。包括寫在viewWillAppear中也會(huì)出現(xiàn)同樣問題。

所以記得重寫viewDiaAppear。

兩個(gè)問題

  1. 打開 app 的時(shí)候,中間按鈕圖標(biāo)顏色不是白色。(當(dāng)然這是因?yàn)槲覀兩厦娲a里面沒有設(shè)置。)
  2. 點(diǎn)擊中間按鈕,進(jìn)入了對(duì)應(yīng)的 tab 頁面,如圖。(這不是我們希望的。)
    [圖片上傳失敗...(image-b83d5f-1555559141135)]

如何修改 TabBarItem 樣式

初一想,在viewDidAppear結(jié)尾加上這段代碼就可以:

//將 tabBar 包含的 tabBarItem 中的第二個(gè)修改為自定義的一個(gè) UITabBarItem
//使用原本設(shè)置的圖片,原本設(shè)置的 tag 值,并將圖片設(shè)置為使用原圖片。
//imageWithRenderingMode(.AlwaysOriginal),稍后詳細(xì)解釋
self.tabBar.items![2] = UITabBarItem(title: nil, image: self.tabBarItem.image?.imageWithRenderingMode(.AlwaysOriginal), tag: self.tabBarItem.tag)
//修改偏移量,讓圖標(biāo)居中
self.tabBar.items![2].imageInsets = UIEdgeInsetsMake(6, 0, -6, 0)

編譯也沒有問題。但是運(yùn)行的時(shí)候報(bào)錯(cuò)了。
[圖片上傳失敗...(image-1e542e-1555559141135)]

需要注意:不能直接在 tab bar controller 里面修改 tab bar item 的屬性

文檔原文是這樣說的:
”You should never access the tab bar view of a tab bar controller directly. To configure the tabs of a tab bar controller, you assign the view controllers that provide the root view for each tab to the viewControllers property.”
這里我覺得 tab bar view 可能說得不夠清楚,應(yīng)該是 tab bar item view。

可能有同學(xué)會(huì)對(duì)上面我們?cè)黾颖尘坝幸蓡?。那難道不是修改 tab bar item 嗎?

并不是。上面我們其實(shí)是給 TabBar 增加子視圖,并定位到各個(gè) TabBarItem 背后,看起來像是在給 TabBarItem 添加背景。并沒有直接修改 TabBarItem。

解決方案

如文檔所說,我們應(yīng)該在對(duì)應(yīng)的 viewController 里面去操作。

到我們之前應(yīng)該創(chuàng)建好了的ThirdViewController.swift里面,寫一個(gè)用于修改第三個(gè) TabBarItem 樣式的函數(shù)。

import UIKit

class ThirdViewController: UIViewController, UITabBarControllerDelegate {

 override func viewDidLoad() {
 super.viewDidLoad()

 }

 func initTabBarItem() {
 //將該 TabBarItem 替換為一個(gè)新的
 self.tabBarItem = UITabBarItem(title: nil, image: self.tabBarItem.image?.imageWithRenderingMode(.AlwaysOriginal), tag: self.tabBarItem.tag)
 //設(shè)置偏移量
 self.tabBarItem.imageInsets = UIEdgeInsetsMake(6, 0, -6, 0)
 }
}

然后回到MainTabBarViewController.swift。加上這兩句:

let vc = self.viewControllers![2] as! ThirdViewController
vc.initTabBarItem()

viewDidAppear方法現(xiàn)在應(yīng)該如下:

override func viewDidAppear(animated: Bool) {
 let ITEM_WIDTH = self.tabBar.frame.width / 5
 let ITEM_HEIGHT = CGFloat(49)

 //初始化 tab bar item 背景層
 for one in self.tabBar.items! {
 let vw = UIView()
 vw.backgroundColor = UIColor(red: 37/255, green: 29/255, blue: 42/255, alpha: 1)
 vw.frame = CGRectMake(ITEM_WIDTH * CGFloat(one.tag), 0, ITEM_WIDTH, ITEM_HEIGHT)
 self.tabBarItemBgViews.append(vw)
 tabBar.insertSubview(vw, atIndex: 1)
 }

 //修改第一個(gè) item 的背景色為選中狀態(tài)顏色
 self.tabBarItemBgViews[0].backgroundColor = UIColor.blackColor()

 //自定義中間按鈕樣式
 self.tabBarItemBgViews[2].backgroundColor = UIColor(red: 17/255, green: 86/255, blue: 136/255, alpha: 1)
 //獲取第三個(gè) TabBarItem 對(duì)應(yīng)的 viewController,然后調(diào)用修改樣式函數(shù)
 let vc = self.viewControllers![2] as! ThirdViewController
 vc.initTabBarItem()
}

這里我們用到了之前提到的UITabBarControllerviewControllers屬性,通過下標(biāo)獲取到對(duì)應(yīng)的viewContrller,然后調(diào)用里面的函數(shù),完成樣式修改。

效果如圖:

[圖片上傳失敗...(image-c535b-1555559141135)]

  • 中間按鈕始終背景顏色為某種藍(lán)色、圖標(biāo)顏色為白色。 達(dá)成√??!

但是點(diǎn)擊中間的按鈕還是會(huì)進(jìn)入對(duì)應(yīng)的 tab 頁面。下下部分繼續(xù)解決這個(gè)問題。

imageWithRenderingMode(稍微詳細(xì)的介紹)

官方文檔:imageWithRenderingMode:

UIImage 自身有一個(gè)方法是 imageWithRenderingMode()。
有三個(gè)可選值:

  • .Automatic
  • .AlwaysOriginal:保持原樣
  • .AlwaysTemplate:留住圖片的樣板,忽略原圖顏色。(比如設(shè)置為 tabBarItem.image 的圖片,默認(rèn)是渲染模式(rendering mode)是 .AlwaysTemplate,只保留了形狀,默認(rèn)是灰色。)

如果想讓 tabBarItem.image 的顏色保持原圖顏色,就要用一個(gè) RenderingMode 為 .AlwaysOriginal 的圖片替換。也就是上面用到的那種方式。

如何自定義 TabBarItem 點(diǎn)擊事件,UITabBarControllerDelegate

之前提到, override func tabBarController(_ tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool,用于決定被選中的 tab 是否激活。當(dāng)返回值是 false 時(shí),保留在之前的 tab 頁面;當(dāng)返回值是 true 時(shí),激活當(dāng)前選中的 tab 頁面。

我們要做的是,當(dāng)被選中的是第三個(gè) tab 時(shí),保留之前的 tab 頁面。

思路就是這樣,怎么做呢?

1.設(shè)置 UITabBarControllerDelegate

class MainTabBarViewController: UITabBarController, UITabBarControllerDelegate {

 var tabBarItemBgViews: [UIView] = []

 override func viewDidLoad() {
 super.viewDidLoad()

 self.tabBar.tintColor = UIColor.whiteColor()
 for it in self.tabBar.items! {
 it.badgeValue = "100"
 }

 self.delegate = self

 }

MainTabBarViewController類添加UITabBarControllerDelegate委托,并在viewDidLoad中將自己的 delegate 設(shè)置為自己。

這個(gè)時(shí)候我們就可以在MainTabBarViewController中實(shí)現(xiàn)UITabBarControllerDelegate中的方法來監(jiān)控和響應(yīng)事件。

重寫如下方法:

func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
 if viewController.tabBarItem.tag == 2 {
 print(viewController.tabBarItem.tag)
 return false
 } else {
 return true
 }
}

當(dāng)被選中 tabBarItem.tag 等于 2 時(shí),即為中間按鈕時(shí),返回 false,保留之前的 tab 頁面;否則切換到新頁面。

效果如圖:

[圖片上傳失敗...(image-b26f87-1555559141135)]

其中print()方法是用來演示,我們可以在這里做一些其他的事情,比如像 instagram 一樣彈出拍照頁面。具體實(shí)現(xiàn)可以參考我之前的模擬微博項(xiàng)目。之后我也會(huì)繼續(xù)完成模擬 instagram 項(xiàng)目,到時(shí)候也可以在我 github 看到。

如圖,當(dāng)點(diǎn)擊中間按鈕時(shí),打印出了tag值。

[圖片上傳失敗...(image-aa0578-1555559141135)]

結(jié)束

以上就是對(duì)自定義 TabBar 樣式以及一點(diǎn)點(diǎn)事件相關(guān)的總結(jié)。

原文地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容