利用kvc對UITabBar上的UITabBarButton的嘗試修改.md

1、前言

  • 一次比較懶的想法,不想自定義UITabBar,也不想用第三方框架,因而想嘗試修改蘋果私有類來達到部分效果git

  • 效果以下
    • 點擊tabBar 上的按鈕,圖片有變大再變小的動畫
    • tabBar 上某個按鈕(購物車按鈕),點擊彈出控制器是modal出來的,並不屬於UITabBarController管理的
    • 上述購物車按鈕,是後面加到tabBar 上的自定義的按鈕UIButton,自己沒有右上角的badge的提示,懶得話,就用
      系統UITabBarButton 的badge
    • 點擊商品的加號按鈕,實現tabBar上購物車按鈕 顯示的badge的數字的改變,並實現變大變小的動畫

2、相關說明

  • 在swift3及xcode8 beta2 環境下github

  • 運行時遍歷出成員變量swift

var count:UInt32 = 0
        let ivarlist = class_copyIvarList(NSClassFromString("UITabBarButton")!, &count)

        for index in 0..<numericCast(count) {
            let ivar = ivarlist![index]
            let ivarStr = String.init(utf8String: ivar_getName(ivar!))
            print("\(ivarStr)")
        }
  • 一、UITabBarButton
Optional("_hitRect")
Optional("_info")
Optional("_vibrancyEffectView")
Optional("_label")
Optional("_badge")
Optional("_selectedIndicator")
Optional("_selected")
Optional("_infoInsets")
Optional("_selectedInfoOffset")
Optional("_infoOffset")
Optional("_customSelectedIndicatorImage")
Optional("_labelOffset")
Optional("_buttonTintColorsForState")
Optional("_contentTintColorsForState")
Optional("_badgeColor")
Optional("_badgeTextAttributesForState")
Optional("_showsHighlightedState")
Optional("_centerAllContents")
Optional("_appearanceGuideClass")
Optional("_tabBar")
  • 其中 "_info" ,"_label" ,"_badge" 這三個是咱們須要的xcode

    • "_info"是 UIImageView 就是顯示的圖片,"_label"是UILabel 顯示的文字
  • 二、"_badge"是什麼呢?它真實類型是什麼,子控件及屬性名是什麼app

let tab = window?.rootViewController as! TabBarViewController
        for view in tab.tabBar.subviews {    
            if view.isKind(of: NSClassFromString("UITabBarButton")! ) {
                let badgeView = view.value(forKeyPath: "_badge")
                    print("1\(badgeView!.self)")
                let badge = badgeView as! UIView
                    print("2\(badge.subviews)")              
                }
            }
        }
1 <_UIBadgeView: 0x7fd671c336c0; frame = (33 2; 18 18); text = '1'; userInteractionEnabled = NO; layer = <CALayer: 0x608000031240>>
2 [<UIImageView: 0x7fd671c09570; frame = (0 0; 18 18); opaque = NO; userInteractionEnabled = NO; tintColor = UIExtendedSRGBColorSpace 1 0.231373 0.188235 1; layer = <CALayer: 0x60800002e9a0>>, <UILabel: 0x7fd671c33ba0; frame = (6 1; 6 16); text = '1'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60800009c890>>]
var count:UInt32 = 0
let ivarlist = class_copyIvarList(NSClassFromString("_UIBadgeView")!, &count)        
for index in 0..<numericCast(count) {
   let ivar = ivarlist![index]
   let ivarStr = String.init(utf8String: ivar_getName(ivar!))
   print("badge= \(ivarStr)")
}


badge= Optional("_label")
badge= Optional("_background")
badge= Optional("_mergedTextAttributes")
badge= Optional("_text")
badge= Optional("_textAttributes")
badge= Optional("_backgroundColor")

3、作法及代碼

  • 自定義TabBarController裏,添加4個子控制器,其中第三個添加一個空的UIViewController,圖片爲UIImage(), 空的TabBarItem,只是讓這個系統生成的UITabBarButton佔好位置框架

  • 自定義UIButton,添加到TabBar上,蓋住上述 空的UITabBarButton上ide

  • appDelegate成爲TabBarController的代理,監控tabBar的點擊學習

extension AppDelegate: UITabBarControllerDelegate {
    
    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        for view  in tabBarController.tabBar.subviews {
            if view.isKind(of: NSClassFromString("UITabBarButton")! ) {
                // UITabBarButton是繼承UIControl的, 經過 按鈕的狀態 才判別是不是如今點擊的按鈕
                let state = view.value(forKeyPath: "highlighted")
                let stateValue = state as! Int
                if stateValue == 1 {
                    // 拿到當前點擊的 按鈕的imageView
                    let imageView = view.value(forKeyPath: "_info")
                    guard let temp  = imageView else {
                        return
                    }
                    
                    let tabButtonImgView = temp as! UIImageView
                    // 添加動畫
                    UIView.animate(withDuration: 0.2, animations: {
                        tabButtonImgView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
                        }, completion: { (Bool) in
                            tabButtonImgView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
                    })
                }
            }
        }
   
    }
    
}
  • 因爲UITabBarButton的子控件都是懶加載的,須要用控制器的tabBarItem 模型對UITabBarButton進行設置動畫

    • 點擊商品加號按鈕,拿到 控制器tabBarItem,設置badgeValue ,然而購物車按鈕(本身添加的) 蓋住了部分 系統生成的badgeView,須要調整badgeView的位置
for button in tabBar.subviews {
            
            if button.isKind(of: NSClassFromString("UITabBarButton")! ) {
                let label = button.value(forKeyPath: "_label") as! UILabel
                //  print("\(label.text)")
                if label.text == "" { // 經過label.text 肯定是購物車按鈕 那位置的 UITabBarButton
                    
                    let badgeButton = button.value(forKeyPath: "_badge")
                    //   print("\(badgeButton!.self)")
                    guard let badgeView = badgeButton else {
                        return
                    }
                    
                    let badge = badgeView as! UIView
                    //  print("\(badge.subviews)")
                    badge.frame.origin.x = 52  // 可根據須要計算
                }
            }
        }
  • 點擊商品加號按鈕實現系統 badgeView的動畫
let tab = window?.rootViewController as! TabBarViewController
        
        for button in tab.tabBar.subviews {
            
            if button.isKind(of: NSClassFromString("UITabBarButton")! ) {
                let label = button.value(forKeyPath: "_label") as! UILabel
                // 外界經過 lable.text 區別出是咱們須要的 第三個UITabBarButton
                if label.text == "" {
                    let badgeButton = button.value(forKeyPath: "_badge")
                    guard let badgeView = badgeButton else {
                        return
                    }
                    // 拿到 badge 控件
                    let badge = badgeView as! UIView
                    //  num變量爲 點擊加號按鈕, 商品個數 計量
                    let str:NSString = NSString.init(format: "%d", num)

                    // kvc 設置屬性
                    badgeView.setValue(UIFont.systemFont(ofSize: 9), forKeyPath: "_label.font")
                    badgeView.setValue(str, forKeyPath: "_label.text")
                    
                    let tabLabal = badgeView.value(forKeyPath: "_label")
                    let label = tabLabal as! UILabel
                    // 拿到lebel 計算label 的大小、尺寸
                    let size = str.size(attributes: [NSFontAttributeName : UIFont.systemFont(ofSize: 9)])
                    label.frame = CGRect(x: (badge.frame.size.width - size.width) * 0.5, y: (badge.frame.size.height - size.height) * 0.5, width: size.width, height: size.height)
                    // 添加核心動畫
                    let scaleAni =  CABasicAnimation()
                    scaleAni.keyPath = "transform.scale"
                    scaleAni.fromValue = 1.0
                    scaleAni.toValue = 1.2
                    scaleAni.autoreverses = true
                    scaleAni.duration = 0.25
                    badge.layer.add(scaleAni, forKey: nil)
                    
                }
            }
        }

4、 問題

  • 剛學習swift 3.0, 各類不熟,各類不順,加上xcode8 beta版各類不穩定及bug,此次僅僅是嘗試swift3的改變而練習的
  • 系統UITabBarButton 裏面的子控件都是懶加載的,沒有先設置tabBarItem模型 ,用kvc 是賦不上值得,這點得注意

5、參考的 小demo

相關文章
相關標籤/搜索