iOS: 教你給UI控制元件新增Badge(訊息提醒小圓點)

weixin_34377065發表於2017-06-22

PPBadgeView

1、前言

最近專案的一個需求是在UIView, UITabBarItem, UIBarButtonItem 這三種型別的控制元件上新增訊息提醒小圓點(Badge),一開始找了一個星星很多的Badge庫 RKNotificationHub,但發現其不能很好的滿足專案需求(在UITabBarItem表現不是很好), 於是就自己動手寫了PPBadgeView,可以很方便的為UIView, UITabBarItem, UIBarButtonItem及其子類新增Badge, 支援 Objective-C與Swift 上效果圖:

2、原理

PPBadgeView的實現原理很簡單: 將一個UILabel控制元件(Badge,也可為其它)新增到UIView(及子類)的物件上 , 所以我們的關鍵點是要找到這個UIView物件! 像UIControl、UILabel、UIButton、UIImageView...這些控制元件就不用說了,都是繼承UIView,可直接在它們的身上新增Badge,但是 UITabBarItem , UIBarButtonItem 就不同了,它們兩個並不是繼承的UIView,該怎麼辦?

2.1、給UITabBarItem新增Badge

通過系統的API可以看到 UITabBarItem 繼承關係:
UIBarButtonItem --> UIBarItem --> NSObject ,
並沒有看見UIView(及子類) , UITabBarItem/UIBarItem的公開屬性裡也沒發現可用的UIView...不急,還記得Xcode自帶的UI檢視除錯神器嗎?


執行Demo後點選開啟,可清楚的看到底部欄的UITabBarItem內有一個UITabBarButton,其下屬還有一個UITabBarSwappableImageView的圖片控制元件,我們要找的就是這個UITabBarSwappableImageView

接下來就是要獲取這個UITabBarSwappableImageView,我們可以使用Runtime + KVC 的方式:

  1. 先利用runtime獲的UITabBarButton的物件名稱(具體方法可以檢視這篇部落格:三分鐘教會你runtime獲取屬性和成員變數), 最後列印的結果為(因列印的內容太多,這裡只貼出最關鍵的結果):
    列印結果: UITabBarItem內的成員變數型別為: @"UITabBarButton",名字為: _view
  2. 再使用KVC取出這個UITabBarButton物件,遍歷出UITabBarSwappableImageView物件
     UIView *tabBarButton = [tabBarItem valueForKey:@"_view"];
     for (UIView *subView in tabBarButton.subviews) {
         if ([subView isKindOfClass:NSClassFromString(@"UITabBarSwappableImageView")]) {
             return subView;
         }
     }複製程式碼
    找到了Badge可以依靠的UIView, 剩下新增Badge的工作是不是變得容易很多了,這裡不多說, 在PPBadgeView裡有詳細程式碼.

2.2、給UIBarButtonItem新增Badge

通過系統的API可以看到 UIBarButtonItem 繼承關係:
UIBarButtonItem --> UIBarItem --> NSObject
和UITabBarItem一樣其公開的屬性也是沒有可用的UIView物件的,不過有了上面的?經驗,獲取UIBarButtonItem中的UIView(及子類)也是同樣的做法, 看圖:
我們要找的就是UINavigationButton中的UIImageView屬性了

很奇怪,利用runtime獲取出來UINavigationButton類名稱為"UIView"
列印結果: UIBarButtonItem內的成員變數型別為: @"UIView",名字為: _view
這裡需要注意的是, UINavigationButton中的UIImageView物件的layer預設masksToBounds = YES,在取出的時候需要設定masksToBounds = NO:

    UIView *navigationButton = [barButtonItem valueForKey:@"_view"];
    for (UIView *subView in navigationButton.subviews) {
        if ([subView isKindOfClass:NSClassFromString(@"UIImageView")]) {
            subView.layer.masksToBounds = NO;
            return subView;
        }
    }複製程式碼

當然,你也可以直接使用navigationButton來做Badge的父檢視...

=========================

2017-06-27更新: UITabBarItem分類/擴充移除掉直接比對"私有屬性API:UITabBarSwappableImageView"的程式碼,規避上架稽核被拒的風險.非常感謝@iOS程式犭袁 大大的指出的這個問題

=========================

結束

PPBadgeView現已託管到GitHub維護,有Objective-C和Swift雙版本,支援CocoaPods匯入,地址: github.com/jkpang/PPBa…

我的GitHub主頁: github.com/jkpang

相關文章