前言:弱者為什么執拗?
? ? 大三下在學swift的時候,給一款APP建過言,說他的APP返回按鈕在按鈕形狀下是一團,換成圖片就好了。當時沒學多久,想的也十分天真。對方很給力,下個版本就改了。等到自己學Objective-c實習遇到這個問題的時候,發現用圖片的確可以,但是有個新問題,返回手勢不起作用了!這就很尷尬了!我一定要與“按鈕形狀”斗爭到底!
按鈕形狀
? ? iOS在7.1版本出的按鈕形狀,目的是為了讓按鈕更加顯眼。
圖1~3分別是按鈕、UIBarButtonItem、TabBar的普通狀態和按鈕形狀狀態,可以輕松的發現,按鈕的按鈕形狀是文字下面多了個下劃線,UIBarButtonItem和TabBar仿佛是多了個背景顏色。
返回按鈕
? ? 我在實習的時候,項目一剛開始是這么解決返回按鈕的:
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0,-60)forBarMetrics:UIBarMetricsDefault];
把返回按鈕的title上移60,移除屏幕,就剩下一個返回圖標。這個方法在非按鈕形狀下顯示沒問題,在按鈕形狀下,返回按鈕就是一團,如圖4,顯然不怎么美觀,尤其是當父控制器title比較長的時候。
? ? 之后我嘗試了多種辦法,比如,在父控制器push子控制器的時候,把父控制器的title設置為空,但是當pop回來時發現上一級控制器title沒了(當然了,你把它設置為空的);改進,在子控制器viewWillAppear的時候把設置自己的title,在父控制器viewDidDisapper的時候把父控制器title設置為空(要判斷是push還是pop,為什么就不說了,反正也不用這個方法),缺點是返回按鈕會閃一下;把返回按鈕換成圖片,缺點是側滑返回手勢沒了。為此我還研究了其他很多APP的返回按鈕、TabBar、UIBarButtonItem,依然沒有結果。
? ? 沒辦法,只好去看本地頭文件里面簡短的介紹、官方API文檔。發現如下幾個東西:backItem、backBarButtonItem、navigationItem.title、navigationController.title、viewController.title。
backItem,只讀,先不管。
對于backBarButtonItem,頭文件中注釋說:
Bar button item to use for the back button in the child navigation item.
用于子控制器的返回按鈕。哇塞,一臉懵逼,從來沒用過這個呀。繼續看。
對于navigationItem.title,頭文件中注釋說:
Title when topmost on the stack. default is nil
當控制器在棧頂的時候顯示的標題,默認為空。說的十分明白。
對于navigationController.title,不好意思這個用command+左鍵找到的是UIAlertController.title,viewController.title點進去也是一樣的,原來navigationController的繼承ViewController的,可能鏈接有點錯誤。
對于viewController.title,頭文件中注釋說
Localized title for use by a parent controller.
懵逼,去看看官方開發者API文檔吧。
? ? 先看backBarButtonItem,翻一下就是:的確是用來設置子控制器的返回按鈕,在當其需要一個返回按鈕的時候。但默認為空。當它為空的時候,會用navigationItem的title屬性來建一個返回按鈕,如果你想自定義圖片或者標題的返回按鈕,你可以賦值一個普通的BarButtonItem給backBarButtonItem來替換。
? ? 說的很明白,順著它給的超鏈接來看navigationItem.title,翻一下:說它擺在navigationBar的中間,默認為空。當一個控制器有子控制器,并且navigationItem.title為空的時候哇,系統會用“back”來作為子控制器返回按鈕的text。
? ? 也許你平時是直接對viewController.title或者navigationController.title進行賦值,發現顯示也是沒問題的,同時影響著父控制器頂部導航欄標題和子控制器的返回按鈕的標題,這又是怎么回事呢?
你可以在UINavigationController中The Left Item完善對返回按鈕的認識,在The Middle Item可以看到對titleView、title的解釋,我們重點來看title。翻一下:如果沒有設置自定義titleView,那么navigation bar就是展示一個label包含viewController的默認標題。這個標題是從自己的viewController的title屬性獲取的,如果你想自定義,你可以設置navigationItem的title屬性。
最后來到了UIViewController.title,翻一下:用人可讀的語言設置title來描述你的view,如果viewController有navigation item 或者 tab-bar item,那么會都影響這個兩個的title。UINavigationController.title官方直接超鏈接的是UIViewController.title,故不建議對UINavigationController.title進行操作,如有需要,可以對UIViewController.title進行操作。
好了,到了這,也許有點亂,理一下。ViewController的title屬性無疑擁有最高影響力,它影響自己控制的navigation item 和 tab-bar item的title;navigation item的title默認為空,會先受titleView的影響,如果沒有自定義titleView才顯示ViewController.title,你也可以設置navigationItem.title來自定義標題,最終顯示后設置的那個標題。backBarButtonItem默認為空,子控制器的返回按鈕的title受其影響,當其為空時,返回按鈕會去找navigation item的title,navigation item的title也為空是,會顯示“back”,中文顯示“返回”。
問:自定義了titleView,同時設置了navigationItem.title或者viewController.title,請問父控制器頂部導航欄顯示誰的標題?子控制器的返回按鈕顯示什么title。
答:父控制器顯示自定義titleView的,子控制器返回按鈕顯示navigationItem.title或者viewController.title。
OK,說了這么多,為了達到我們初始目的,只需要自定義一個UIBarButtonItem賦值給父控制器的backBarButtonItem就行了!經測試,只有title有效,action、Target無效。有空的同學可以嘗試用- (instancetype)initWithCustomView:(UIView*)customView;來自定義返回按鈕樣式,反正我沒嘗試成功。
那么,我們在平時開發中應該對哪個設置標題呢?看下官方建議或者打開你的storyBoard:
如圖5,官方建議是對NavigationItem進行設置,當你額外需要,你可以設置TitleView給NavigationItem,比如帶屬性的文字、把title變成一個可交互的Button等等。
Button、TabBar和UIBarButtonItem
解決完返回按鈕,還剩下button、TabBar和UIBarButtonItem的按鈕形狀。實習的時候,由于找不到方法解決“按鈕形狀”,一是問題不好描述,二是關注的人少,我一度認為按鈕形狀狀態下有下劃線的就是Button,背景有顏色的就是UIBarButtonItem、TabBar,因此我一度認為微博、淘寶等客戶端是完全自定義了NavigationController、TabBarController,前者導航欄上的Item沒有下劃線、也沒有背景顏色,后者導航欄全都是下劃線,前者底部TabBar有下劃線沒有背景顏色,難道是用的Button?真不愧大牛啊!當然也有客戶TabBar在按鈕形狀狀態下不顯示背景顏色、也沒有下劃線的,說明還是有方法的,更加堅定了我繼續研究的決心。
一次偶然的機會,我用Reveal發現了TabBar在按鈕形狀下顯示背景顏色的view:
我欣喜若狂,只要把這個View設置一個透明色不就好了,然后我就去找這個View,很遺憾,沒找到,卻找到了selectionIndicatorImage,肯定是親戚!趕緊設置TabBarButton:
self.selectionIndicatorImage = [UIImage new];
我是自定義的TabBar,所以這樣設置的。一運行,哈哈,背景顏色果然不見了!什么原理呢?
看下官方解釋吧selectionIndicatorImage,翻一下:用這個屬性來自定義選擇圖片,你的圖片將顯示在TabBar底部但是呢隱藏在TabBar的Contents的下面。默認為空,當為空時,被選定時就是顯示高亮。沒跑了就是它,選擇指示器圖片,但是官方也沒提按鈕形狀。我們可以用它來做點選中TabBar的效果。那么其他兩個呢?
用Reveal看按鈕,就看不出什么鬼了,怎么辦呢?TabBar是因為設置了一個Image,那么Button呢會不會也這樣呢?抱著試一試的心態:
[testButton setBackgroundImage:[UIImage new] forState:UIControlStateNormal];
然后,然后下劃線就沒了!!!怎么回事?官方的對背景圖片的說明也未提及按鈕形狀/下劃線/高亮,可能和TabBar差不多吧。那么對于UIBarButtonItem只需要initWithCustomView:一個帶有背景圖片的button,那么在按鈕形狀下就不再有背景顏色了。有種戛然而止的感覺。
總結
1、子控制器的返回按鈕title優先來自父控制器的backBarButtonItem.title;
2、backBarButtonItem默認為空,當其為空,返回按鈕就去找NavigationItem.title;
3、ViewController.title會同時影響NavigationItem.title、tabBar的title,如果有的話;
4、針對第三點,后設置的那個會最終顯示出來
5、針對第二點,當NavigationItem.title也為空時,返回按鈕顯示“back”,中文“返回”;
6、當backBarButtonItem.title為空字符串時,子控制器返回按鈕只顯示圖標,并且沒有按鈕形狀下的一塊背景顏色(修改NavigationItem的title不建議);
7、要修改控制器的頂部導航欄標題建議對NavigationItem.title進行修改;
8、去除TabBar的按鈕形狀設置它的selectionIndicatorImage;
9、去除UIButton的按鈕形狀,setBackgroundImage:[UIImage new] forState:UIControlStateNormal;
10、去除UIBarButtonItem的按鈕形狀,initWithCustomView:一個帶有背景圖片的button即可;
后記
寫完了,越寫到后面越發現一股弱者的氣息,希望能幫到人。拜了個拜。@徐星星去哪了