您的位置:时时app平台注册网站 > 编程知识 > iOS开采之UI篇(7)—— UIButton【时时app平台注册

iOS开采之UI篇(7)—— UIButton【时时app平台注册

2019-09-19 06:45

JMBaseButtonConfig和JMBaseButton可以说是一体的,因为它就是一个配置文件,用于对JMBaseButton的样式配置。

三、按钮属性

一般来说,我们设置对象属性都会使用点语法,但对于UIButton来说,有些属性是不能使用点语法的。

从零开始

/** badgeValue样式 */typedef NS_ENUM(NSInteger, JMBadgeValueType) { JMBadgeValueTypePoint, //点 JMBadgeValueTypeNew, //new JMBadgeValueTypeNumber, //number};/** badgeValue 动画样式 */typedef NS_ENUM(NSInteger, JMBadgeValueAnimationType) { JMBadgeValueAnimationTypeNone, //无动画 JMBadgeValueAnimationTypeShake, //抖动 JMBadgeValueAnimationTypeOpacity, //透明过渡动画 JMBadgeValueAnimationTypeScale, //缩放动画};@interface JMBadgeValue : UIView/** badgeL */@property (nonatomic, strong) UILabel *badgeL;/** type */@property (nonatomic, assign) JMBadgeValueType type;/** animationType */@property (nonatomic, assign) JMBadgeValueAnimationType animationType;/** badgeValue */@property (nonatomic, copy) NSString *badgeValue;@property (nonatomic, assign) CGFloat badgeRadius;- showAnimation;@end

设置属性注意事项

1. UIButton的非私有属性(继承而来的属性),可以使用点语法。
如:

  btn.frame = CGRectMake(100, 100, 200, 50);      // 设置位置尺寸
  btn.backgroundColor = [UIColor purpleColor];    // 设置背景颜色
  btn.alpha = 1.0f;                               // 设置透明度
  btn.layer.cornerRadius = 5.0;                   // 设置圆角(5.0是圆角的弧度)
  btn.layer.borderWidth = 1.0f;                   // 设置边框宽度
  btn.layer.borderColor = [UIColor blackColor].CGColor;   //设置边框颜色
  btn.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;         // 设置垂直方向对齐方式
  btn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;     // 设置水平方向对齐模式

2. UIButton的私有属性大多为只读,不能使用点语法进行set设置。
UIButton中所有只读属性:

@property(nonatomic,readonly) UIButtonType buttonType;
@property(nullable, nonatomic,readonly,strong) NSString *currentTitle;             // normal/highlighted/selected/disabled. can return nil
@property(nonatomic,readonly,strong) UIColor  *currentTitleColor;        // normal/highlighted/selected/disabled. always returns non-nil. default is white(1,1)
@property(nullable, nonatomic,readonly,strong) UIColor  *currentTitleShadowColor;  // normal/highlighted/selected/disabled.
@property(nullable, nonatomic,readonly,strong) UIImage  *currentImage;             // normal/highlighted/selected/disabled. can return nil
@property(nullable, nonatomic,readonly,strong) UIImage  *currentBackgroundImage;   // normal/highlighted/selected/disabled. can return nil
@property(nullable, nonatomic,readonly,strong) NSAttributedString *currentAttributedTitle NS_AVAILABLE_IOS(6_0);  // normal/highlighted/selected/disabled. can return nil
@property(nullable, nonatomic,readonly,strong) UILabel     *titleLabel NS_AVAILABLE_IOS(3_0);
@property(nullable, nonatomic,readonly,strong) UIImageView *imageView  NS_AVAILABLE_IOS(3_0);

3. UIButton有几种显示状态(普通、高亮、选中等),每种状态对应的某些属性是不同的,如果使用点语法就无法指明是哪种状态下的属性
比如设置以下属性的时候需同时表明是在哪个状态(State):

// 设置标题
- (void)setTitle:(nullable NSString *)title forState:(UIControlState)state;                     // default is nil. title is assumed to be single line
// 设置标题颜色
- (void)setTitleColor:(nullable UIColor *)color forState:(UIControlState)state UI_APPEARANCE_SELECTOR; // default if nil. use opaque white
// 设置标题阴影颜色
- (void)setTitleShadowColor:(nullable UIColor *)color forState:(UIControlState)state UI_APPEARANCE_SELECTOR; // default is nil. use 50% black
// 设置前景图片
- (void)setImage:(nullable UIImage *)image forState:(UIControlState)state;                      // default is nil. should be same size if different for different states
// 设置背景图片
- (void)setBackgroundImage:(nullable UIImage *)image forState:(UIControlState)state UI_APPEARANCE_SELECTOR; // default is nil
// 设置带属性的标题(NSAttributedString类型),设置麻烦不常用。
- (void)setAttributedTitle:(nullable NSAttributedString *)title forState:(UIControlState)state NS_AVAILABLE_IOS(6_0); // default is nil. title is assumed to be single line
代码实现:
  1. 首先不妨先建立三个基础文件,然后在丰富代码。其中, IWCustomButton 继承自 UIButtonIWCustomTabBarView 继承自 UIViewIWCustomTabBarController 继承自 UITabBarController

  2. 修改 AppDelegate 文件中 didFinishLaunchingWithOptions 方法,保证启动时没有异常:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        //  创建Window
        window = UIWindow(frame: UIScreen.main.bounds)
        //  初始化一个tabbar
        let customTabBar = IWCustomTabBarController()
        //  设置根控制器
        window?.rootViewController = customTabBar

        window?.makeKeyAndVisible()

        return true
    }
  1. 首先在 IWCustomTabBarController 文件中添加代码:
//  IWCustomTabBarController.swift
import UIKit
class IWCustomTabBarController: UITabBarController {

    //  MARK: - Properties
    //  图片
    fileprivate let tabBarImageNames = ["tb_home","tb_person"]
    fileprivate let tabBarTitles = ["首页","我的"]

    //  MARK: - LifeCycle
    override func viewDidLoad() {
        super.viewDidLoad()

        //  自定义 TabBar 外观
        createCustomTabBar(addHeight: 0)

        //  创建子控制器
        addDefaultChildViewControllers()

        //  设置每一个子页面的按钮展示
        setChildViewControllerItem()
    }

    //  MARK: - Private Methods

    /// 添加默认的页面
   fileprivate func addDefaultChildViewControllers() {
        let vc1 = UIViewController()
        vc1.view.backgroundColor = UIColor.white

        let vc2 = UIViewController()
        vc2.view.backgroundColor = UIColor.lightGray

        viewControllers = [vc1, vc2]
    }

    /// 设置外观
    ///
    /// - parameter addHeight: 增加高度,0 为默认
    fileprivate let customTabBarView = IWCustomTabBarView()
    fileprivate func createCustomTabBar(addHeight: CGFloat) {

        //  改变tabbar 大小
        var oriTabBarFrame = tabBar.frame
        oriTabBarFrame.origin.y -= addHeight
        oriTabBarFrame.size.height  = addHeight
        tabBar.frame = oriTabBarFrame

        customTabBarView.frame = tabBar.bounds
        customTabBarView.frame.origin.y -= addHeight
        customTabBarView.backgroundColor = UIColor.groupTableViewBackground
        customTabBarView.frame.size.height = tabBar.frame.size.height   addHeight
        customTabBarView.isUserInteractionEnabled = true
        tabBar.addSubview(customTabBarView)
    }

    /// 设置子页面的item项
    fileprivate func setChildViewControllerItem() {
        guard let containViewControllers = viewControllers else {
            print("⚠️  设置子页面 item 项失败  ⚠️")
            return
        }

        if containViewControllers.count != tabBarImageNames.count {
            fatalError("子页面数量和设置的tabBarItem数量不一致,请检查!!")
        }

        //  遍历子页面
        for (index, singleVC) in containViewControllers.enumerated() {
            singleVC.tabBarItem.image = UIImage(named: tabBarImageNames[index])
            singleVC.tabBarItem.selectedImage = UIImage(named: tabBarImageNames[index]   "_selected")
            singleVC.tabBarItem.title = tabBarTitles[index]
        }
    }
}

上面就是一个基本的纯代码创建的 UITabBarController 的实际效果了,运行后,查看效果:

时时app平台注册网站 1

基本的运行效果

简单说一下上面的代码:createCustomTabBar(_: ) 方法传入的参数主要是为了控制高度,尝试改变参数值可看到效果。 addDefaultChildViewControllers() 方法是添加子页面,这里需要说的是并没有在这个方法里设置文字和图片,而是选择重新创建一个方法 setChildViewControllerItem() ,因为后面我们需要获取页面有几个 UITabBarItem
现在明显的问题就是我们的原始图片是红色的,为什么现在都是灰、蓝色,因为 UITabBar 使用图片时渲染了,如果我们需要使用原始图片,则对 UIImage 方法扩展:

extension UIImage {
    var originalImage: UIImage {
        return self.withRenderingMode(.alwaysOriginal)
    }
}

然后修改遍历子页面的代码:

//  遍历子页面
        for (index, singleVC) in containViewControllers.enumerated() {
            singleVC.tabBarItem.image = UIImage(named: tabBarImageNames[index]).originalImage
            singleVC.tabBarItem.selectedImage = UIImage(named: tabBarImageNames[index]   "_selected").originalImage
            singleVC.tabBarItem.title = tabBarTitles[index]
        }

运行后便可查看到原始的图片效果。

  1. 编写文件 IWCustomTabBarView :
import UIKit
//  自定义按钮功能
enum IWCustomButtonOperation {
  case customRecordingVideo       //  录像
  case customTakePhoto            //  拍照
  case customMakeTape             //  录音
}
/// 页面按钮点击协议
protocol IWCustomTabBarViewDelegate {

  /// 点击tabBar 管理下的按钮
  ///
  /// - parameter customTabBarView:     当前视图
  /// - parameter didSelectedButtonTag: 点击tag,这个是区分标识
  func iwCustomTabBarView(customTabBarView: IWCustomTabBarView, _ didSelectedButtonTag: Int)

  /// 点击自定义的纯按钮
  ///
  /// - parameter customTabBarView:      当前视图
  /// - parameter didSelectedOpertaionButtonType: 按钮类型,拍照、摄像、录音
  func iwCustomTabBarView(customTabBarView: IWCustomTabBarView, _ didSelectedOpertaionButtonType: IWCustomButtonOperation)
}
class IWCustomTabBarView: UIView {
  //  MARK: - Properties
  //  协议
  var delegate: IWCustomTabBarViewDelegate?
  //  操作按钮数组
  fileprivate var operationButtons = [IWCustomButton]()
  //  tabbar 管理的按钮数组
  fileprivate var customButtons = [IWCustomButton]()
  //  自定义按钮图片、标题
  fileprivate let operationImageNames = ["tb_normol","tb_normol","tb_normol"]
  fileprivate let operationTitls = ["摄像", "拍照", "录音"]

  //  MARK: - Init
  override init(frame: CGRect) {
      super.init(frame: frame)

      //  添加自定义按钮
      addOperationButtons()
  }

  required init?(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      print("IWCustomTabBarView 页面 init(coder:) 方法没有实现")
  }

  /// 布局控件
  override func layoutSubviews() {
      super.layoutSubviews()

      //  设置位置
      let btnY: CGFloat = 0
      let btnWidth = bounds.width / CGFloat(subviews.count)
      let btnHeight = bounds.height

      //  这里其实就两个
      for (index, customButton) in customButtons.enumerated() {

          switch index {
          case 0:
              customButton.frame = CGRect(x: 0, y: 0, width: btnWidth, height: btnHeight)
              customButton.tag = index
          case 1:
              customButton.frame = CGRect(x: btnWidth * 4, y: 0, width: btnWidth, height: btnHeight)
              customButton.tag = index
          default:
              break
          }
      }

      //  这里有三个
      for (index, operBtn) in operationButtons.enumerated() {
          let btnX = (CGFloat(index)   1) * btnWidth
          operBtn.frame = CGRect(x: btnX, y: btnY, width: btnWidth, height: btnHeight)
      }
  }

  //  MARK: - Public Methods
  /// 根据原始的 TabBarItem 设置自定义Button
  ///
  /// - parameter originalTabBarItem: 原始数据
  func addCustomTabBarButton(by originalTabBarItem: UITabBarItem) {
      //  添加初始按钮
      let customButton = IWCustomButton()
      customButtons.append(customButton)
      addSubview(customButton)

      //  添加点击事件
      customButton.addTarget(self, action: #selector(customButtonClickedAction(customBtn:)), for: .touchUpInside)

      //  默认展示第一个页面
      if customButtons.count == 1 {
          customButtonClickedAction(customBtn: customButton)
      }
  }

  //  MARK: - Private Methods

  /// 添加操作按钮
  fileprivate func addOperationButtons() {
      for index in 0 ..< 3 {
          let operationBtn = IWCustomButton()
          operationButtons.append(operationBtn)
          operationBtn.setImage(UIImage(named: operationImageNames[index]), for: .normal)
          operationBtn.setImage(UIImage(named: operationImageNames[index]), for: .highlighted)
          operationBtn.setTitle(operationTitls[index], for: .normal)
          operationBtn.tag = 100   index
          operationBtn.addTarget(self, action: #selector(operationButtonClickedAction(operBtn:)), for: .touchUpInside)
          addSubview(operationBtn)
      }
  }

  ///  操作按钮点击事件
  @objc fileprivate func operationButtonClickedAction(operBtn: IWCustomButton) {
      switch operBtn.tag {
      case 100:
          delegate?.iwCustomTabBarView(customTabBarView: self, .customRecordingVideo)
      case 101:
          delegate?.iwCustomTabBarView(customTabBarView: self, .customTakePhoto)
      case 102:
          delegate?.iwCustomTabBarView(customTabBarView: self, .customMakeTape)
      default:
          break
      }
  }

  //  保证按钮的状态正常显示
  fileprivate var lastCustomButton = IWCustomButton()

  ///  tabbar 管理下按钮的点击事件
  @objc fileprivate func customButtonClickedAction(customBtn: IWCustomButton) {
      delegate?.iwCustomTabBarView(customTabBarView: self, customBtn.tag)

      lastCustomButton.isSelected = false
      customBtn.isSelected = true
      lastCustomButton = customBtn
  }
}

IWCustomTabBarController 文件的 setChildViewControllerItem() 方法中,修改遍历子页面的代码,获取当前的 UITabBarItem

// 遍历子页面 for (index, singleVC) in containViewControllers.enumerated() { 
singleVC.tabBarItem.image = UIImage(named: tabBarImageNames[index]) 
singleVC.tabBarItem.selectedImage = UIImage(named: tabBarImageNames[index]   "_selected") 
singleVC.tabBarItem.title = tabBarTitles[index]
//  添加相对应的自定义按钮
            customTabBarView.addCustomTabBarButton(by: singleVC.tabBarItem)
 }

运行后,看到效果好像乱乱的,暂时不用在意,在后面的代码中会慢慢整理出理想的效果。

时时app平台注册网站 2

乱糟糟的

简单分析上面的代码:这里我在中间加入了三个自定义的按钮。这样的话,最下面应该是有5个按钮的。当然也可以加入一个或者两个等,只需要修改上面对应的数值就可以了。这里面比较主要的就是自定义协议 IWCustomTabBarViewDelegate 和布局方法 layoutSubviews,布局方法里如果能理解两个 for 循环和对应数组中的数据来源、作用,那么问题就简单很多了。

这里要说一个属性 lastCustomButton ,这个属性会让我们避免不必要的遍历按钮,有些时候多个按钮只能有一个被选中时,有种常见的方法就是遍历按钮数组,令其中一个 isSelected = true ,其他按钮的 isSelected = false ,而这个属性就能取代遍历。

其实存在的问题也很明显,就是这么写的话很难去扩展,譬如如果上面的代码已经完成了,但是临时需要减少一个自定义按钮,那么就需要改动多个地方。这里只是提供一种自定义的思路,只是说还有很多可以优化的地方。

  1. 关于自定义的 UIButotn ,是个很有意思的地方。因为视觉上的改变都是在这里发生,先使用默认的设置:
import UIKit
class IWCustomButton: UIButton {

    override init(frame: CGRect) {
        super.init(frame: frame)
        titleLabel?.textAlignment = .center
        setTitleColor(UIColor.gray, for: .normal)
        setTitleColor(UIColor.red, for: .selected)
        titleLabel?.font = UIFont.italicSystemFont(ofSize: 12)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        print("⚠️⚠️⚠️ init(coder:) 方法没有实现")
    }

    /// 根据传入的 UITabBarItem 设置数据显示
    ///
    /// - parameter tabBarItem: 数据来源
    func setTabBarItem(tabBarItem: UITabBarItem) {
        setTitle(tabBarItem.title, for: .normal)
        setImage(tabBarItem.image, for: .normal)
        setImage(tabBarItem.selectedImage, for: .highlighted)
        setImage(tabBarItem.selectedImage, for: .selected)
    }
}

修改 IWCustomTabBarView 文件的 addCustomTabBarButton(by: ) 方法:

    //  MARK: - Public Methods
    /// 根据原始的 TabBarItem 设置自定义Button
    ///
    /// - parameter originalTabBarItem: 原始数据
    func addCustomTabBarButton(by originalTabBarItem: UITabBarItem) {
        //  添加初始按钮
        let customButton = IWCustomButton()
        customButtons.append(customButton)
        addSubview(customButton)

        //  传值
        customButton.setTabBarItem(tabBarItem: originalTabBarItem)

        //  添加点击事件
        customButton.addTarget(self, action: #selector(customButtonClickedAction(customBtn:)), for: .touchUpInside)

        //  默认展示第一个页面
        if customButtons.count == 1 {
            customButtonClickedAction(customBtn: customButton)
        }
    }

看看运行结果:

时时app平台注册网站 3

自定义按钮后

首先,我们发现了乱的原因,就是自定义的按钮和原本的 UITabBarItem 的显示起了冲突。那么先修改这个问题:在 IWCustomTabBarController 方法中页面即将出现时添加方法:

  override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        //  移除原生的 TabBarItem ,否则会出现覆盖现象
        tabBar.subviews.forEach { (subView) in
            if subView is UIControl {
                subView.removeFromSuperview()
            }
        }
    }

那么上面重复显示的原生项此时就移除了。下一个问题:发现自定义按钮图像的大小不一致。其实中间图片本身的大小就是比两边的大的。以 2x.png 为例,中间的图标是 70x70,而两边的是 48x48。如果在没有文字显示的情况下,在按钮的初始化方法中添加 imageView?.contentMode = .center ,图片居中展示,自定义按钮到这个地方就可以结束了(可以尝试不要 title ,查看运行效果)。甚至可以在自定义按钮的初始化方法里使用仿射变换来放大、缩小图片。

这里为了控制图片、文字的位置,重写 UIButton 的两个方法:

    /// 重写 UIButton 的 UIImageView 位置
    ///
    /// - parameter contentRect: 始位置
    ///
    /// - returns: 修改后
    override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
        let imageWidth = contentRect.size.height * 4 / 9
        let imageHeight = contentRect.size.height
        return CGRect(x: bounds.width / 2 - imageWidth / 2, y: imageHeight / 9, width: imageWidth, height: imageWidth)
    }

    /// 重写 UIButton 的 TitleLabel 的位置
    ///
    /// - parameter contentRect: 原始位置
    ///
    /// - returns: 修改后
    override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
        let titleWidth = contentRect.size.width
        let titleHeight = contentRect.size.height / 3
        return CGRect(x: bounds.width / 2 - titleWidth / 2, y: bounds.height - titleHeight, width: titleWidth, height: titleHeight)
    }  

对上面代码做简单地说明,首先说方法中 contentRect 这个变量,它的 size 是这个 UIButton 的大小,而不是单独的 UIImageView ,或者 titleLabel 的大小。上面的一些具体数值,譬如 4 / 9 等这种奇葩的比例数值,仅仅是我根据自己的审美观随便写入的一些数值,至于到具体的开发中,可以固定大小,也可以使用更加细致的比例,因为 tabBar 默认的高度是 49 ,那么很多数据就可以使用了。现在看看效果:

时时app平台注册网站 4

修改自定义按钮后

  1. IWCustomTabBarController 文件中实现 IWCustomTabBarView 文件中的协议方法,首先添加协议,然后实现方法,别忘了令 customTabBarView.delegate = self
    //  MARK: - IWCustomTabBarViewDelegate
    ///  点击 tabbar 管理下的按钮
    func iwCustomTabBarView(customTabBarView: IWCustomTabBarView, _ didSelectedButtonTag: Int) {
        selectedIndex = didSelectedButtonTag
    }

    ///  点击自定义添加的的按钮
    func iwCustomTabBarView(customTabBarView: IWCustomTabBarView, _ didSelectedOpertaionButtonType: IWCustomButtonOperation) {
        switch didSelectedOpertaionButtonType {
        case .customRecordingVideo:
            print("摄像")
            let vc = UIViewController()
            vc.view.backgroundColor = UIColor.orange
            addBackButton(on: vc.view)
            present(vc, animated: true, completion: nil)
        case .customTakePhoto:
            print("拍照")
            let vc = UIViewController()
            vc.view.backgroundColor = UIColor.green
            addBackButton(on: vc.view)
            present(vc, animated: true, completion: nil)
        case .customMakeTape:
            print("录音")
            let vc = UIViewController()
            vc.view.backgroundColor = UIColor.cyan
            addBackButton(on: vc.view)
            present(vc, animated: true, completion: nil)
        }
    }

    fileprivate func addBackButton(on superView: UIView) {
        let btn = UIButton()
        btn.frame = CGRect(x: 100, y: 100, width: 100, height: 50)
        btn.backgroundColor = UIColor.blue
        btn.setTitle("返回", for: .normal)
        btn.setTitleColor(UIColor.white, for: .normal)
        btn.addTarget(self, action: #selector(dismissAction), for: .touchUpInside)
        superView.addSubview(btn)
    }
    @objc func dismissAction() {
        dismiss(animated: true, completion: nil)
    }

上面的代码,只单独说一点,就是协议方法 iwCustomTabBarView(customTabBarView : , _ didSelectedButtonTag) 中, selectedIndex 这个属性并非我们自己定义的变量,而是系统设置的,所以这时候 didSelectedButtonTag 所代表值就显得很有意思了,它正是我们在 UITabBar 管理下 ViewController 是下标值。看看这时候的效果吧:

时时app平台注册网站 5

完成后

  1. 最后再说一点,有时候我们需要给自定义的 IWCustomTabBarView 添加背景图片,那么这时候会出现一个问题,就是原本的 TabBar 的浅灰色背景始终会有一条线,此时在 IWCustomTabBarController 文件的 viewDidLoad() 方法中添加下面的代码即可。
        //  去除 TabBar 阴影
        let originalTabBar = UITabBar.appearance()
        originalTabBar.shadowImage = UIImage()
        originalTabBar.backgroundImage = UIImage()
#import <Foundation/Foundation.h>#import <UIKit/UIKit.h>@interface JMBaseButtonConfig : NSObjecttypedef NS_ENUM(NSInteger, JMButtonStyleType) { JMButtonStyleTypeTop, //图片在上, 文字在下 JMButtonStyleTypeLeft, //图片在左, 文字在右 JMButtonStyleTypeBottom, //图片在下, 文字在上 JMButtonStyleTypeRight, //图片在右, 文字在左};/** 初始化 */  (instancetype)buttonConfig;/** 按钮类型 (默认图片在左, 文字在右) */@property (nonatomic, assign) JMButtonStyleType styleType;/** padding (文字和图片之间的间距, 默认为6.f) */@property (nonatomic, assign) CGFloat padding;/************************ 文字相关属性设置 ************************//** 文字大小 */@property (nonatomic, strong) UIFont *titleFont;/** 文字  */@property (nonatomic, copy) NSString *title;/** 文字颜色 */@property (nonatomic, strong) UIColor *titleColor;/** 更改文字的origin(无特殊需求,请勿修改此属性, 注意不能设置为0,0) */@property (nonatomic, assign) CGPoint titleOrigin;/************************ 图片相关属性设置 ************************//** 图片 */@property (nonatomic, strong) UIImage *image;/** 图片的大小(无特殊需求, 请勿修改此属性) */@property (nonatomic, assign) CGSize imageSize;/** 图片的origin(无特殊需求,请勿修改此属性,注意不能设置为0,0) */@property (nonatomic, assign) CGPoint imageOrigin;/************************ 圆角相关属性设置 ************************//** 圆角类型 (默认 UIRectCornerAllCorners) */@property (nonatomic, assign) UIRectCorner corners;/** 圆角大小  */@property (nonatomic, assign) CGFloat cornerRadius;/************************ 背景相关属性设置 ************************//** 背景颜色  */@property (nonatomic, strong) UIColor *backgroundColor;/** 背景图片  */@property (nonatomic, strong) UIImage *backgroundImage;/************************ 边框相关属性设置 ************************//** 边框线条颜色 (默认 clearColor) */@property (nonatomic, strong) UIColor *borderColor;/** 边框线条宽度  */@property (nonatomic, assign) CGFloat borderWidth;@end

示例

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 实例化一个UIButton
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];

    /* 设置属性 */
    btn.frame = CGRectMake(0, 0, 100, 80 );      // 设置位置尺寸
    btn.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/2);
    btn.backgroundColor = [UIColor purpleColor];    // 设置背景颜色
    btn.alpha = 1.0f;                               // 设置透明度
    btn.layer.cornerRadius = 5.0;                   // 设置圆角(5.0是圆角的弧度)
    btn.layer.borderWidth = 1.0f;                   // 设置边框宽度
    btn.layer.borderColor = [UIColor blackColor].CGColor;   //设置边框颜色
    btn.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;         // 设置垂直方向对齐方式
    btn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;     // 设置水平方向对齐模式

    // 设置标题文字
    [btn setTitle:@"普通" forState:UIControlStateNormal];
    [btn setTitle:@"高亮" forState:UIControlStateHighlighted];
    [btn setTitle:@"选中" forState:UIControlStateSelected];
    [btn setTitle:@"选中状态下的高亮" forState:UIControlStateHighlighted|UIControlStateSelected];

    // 设置前景图
//    [btn setImage:[UIImage imageNamed:@"11"] forState:UIControlStateNormal];

    // 设置背景图
    [btn setBackgroundImage:[UIImage imageNamed:@"red"] forState:UIControlStateNormal];
    [btn setBackgroundImage:[UIImage imageNamed:@"goodFilling"] forState:UIControlStateHighlighted];
    [btn setBackgroundImage:[UIImage imageNamed:@"red"] forState:UIControlStateHighlighted|UIControlStateSelected];
    [btn setBackgroundImage:[UIImage imageNamed:@"red"] forState:UIControlStateSelected];

    /**
     添加点击事件

     addTarget:(参数1)    响应目标(由谁来执行响应方法)
     action:(参数2)   调用方法(即响应事件)
     forControlEvents:(参数3) 事件的类型
     */
    [btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];

    // 添加btn到self.view
    [self.view addSubview:btn];
}


#pragma mark - 按钮响应方法

- (void)btnAction:(UIButton *)btn {

    NSLog(@"点击了按钮");
}


@end

时时app平台注册网站 6

效果图

前言

很多时候,系统原生的 UITabBar 并不能满足我们的需求,譬如我们想要给图标做动态的改变,或者比较炫一点的展示,原生的处理起来都很麻烦。所以很多时候都需要自定义一个 UITabBar,里面的图标、颜色、背景等等都可以根据需求去改变。

效果展示:

时时app平台注册网站 7

自定义UITabBar

JMButton是一个自定义View,主要的功能是负责展示自定义Button, 自定义BadgeVlaue以及对点击事件, 下面是代码块中有一个初始化方式和一些对badgeValue的配置,如显示new关键字的BadgeValue- showNewBadgeValue;你还可以显示badgeValue的动画效果- showBadgeAnimation;

四、控制状态(UIControlState)

在set按钮的私有属性的时候,通常会一并forState指明在哪个控制状态。那么UIButton总共有几个状态呢?以下是UIControlState枚举值:

typedef NS_OPTIONS(NSUInteger, UIControlState) {
    UIControlStateNormal       = 0,             // 普通状态(按下之前)
    UIControlStateHighlighted  = 1 << 0,        // 高亮状态(按下ing)
    UIControlStateDisabled     = 1 << 1,        // 失效状态(enable=NO)
    UIControlStateSelected     = 1 << 2,        // 选中状态(selected=YES)
    UIControlStateFocused NS_ENUM_AVAILABLE_IOS(9_0) = 1 << 3,  // 聚焦状态(iOS9.0开始引入,应该和3D Touch有关)
    UIControlStateApplication  = 0x00FF0000,    // additional flags available for application use(不明觉厉)
    UIControlStateReserved     = 0xFF000000     // 苹果预留位,憋管它。。。
};

我们看到,与普通的枚举不同,UIControlState是位枚举。也就是说,按钮可以同时拥有不止一个状态!

  • if(isSelected == NO),有两种状态:UIControlStateNormal 和 UIControlStateHighlighted
  • if(isSelected == YES),也有两种状态 UIControlStateSelected 和 UIControlStateSelected | UIControlStateHighlighted

也就是说:当没有选中状态下,按下的时候对应高亮模式(UIControlStateHighlighted);而当按钮已经是选中状态,此时再按下按钮即对应高亮模式位和选中模式(UIControlStateSelected | UIControlStateHighlighted)。

先说一下思路

页面继承自 UITabBarController ,然后自定义一个 UIView ,添加到 TabBar 上。取消原本的控制按钮。创建自定义按钮,即重写 UIButtonimageView 、和 titleLabelframe ,完成图片、文字的重新布局。最后实现不同按钮的协议方法。

效果图中,只有两边的两个页面在 UITabBarController 的管理下,中间三个都是通过自定义按钮实现的模态页面,即 present 过去的。多用于拍摄图片、录制视频、发表动态等功能。

简单地展示下 Demo 的文件,因为代码中会出现图片名:

时时app平台注册网站 8

Demo文件

  1. 导入JMButton#import "JMButton.h"
  2. 初始化JMBootstrapButtonConfig并配置属性

UIButton中image与backgroundImage区别

由于System样式的按钮不显示前景图片(前文有演示),因此以下均是对Custom样式按钮进行探讨。
1. 层级关系
创建一个带有标题title,前景图片image和背景图片backgroundImage的按钮如下:

时时app平台注册网站 9

运行代码,然后捕捉视图的层级关系:

时时app平台注册网站 10

或者直接点击Xcode底部红框内按钮,然后可以查看当前视图的层级关系:

时时app平台注册网站 11

小结一下:

  • 视图Z轴方向由里向外依次为backgroundImage--> image-->title
  • image默认居左,title默认居右

2. 内容模式
backgroundImage的内容模式(contentMode)默认为UIViewContentModeScaleToFill,backgroundImage会跟随按钮的大小的改变而改变,也就是说背景图片始终等于按钮尺寸。
对于image就比较怪:当图片的宽(或高)小于按钮尺寸时,image显示的是原始宽(或高);当图片宽(或高)大于按钮尺寸时,image被压缩到与button等宽(或高)的尺寸。这种内容模式我叫不上名儿来。。。
对于image或者backgroundImage的contentMode属性,苹果没有提供修改的途径(至少我没找到)。所以当我们使用image的时候,最好先调好尺寸,对于比较大的图片,先进行必要的压缩裁切。(不过笔者通常不会这么干,如果需要自定义比较高的button,我就直接用view来做外观显示,然后再套上一个透明的button~)

总结

  1. 按钮的Z轴方向由里向外依次为backgroundImage--> image-->title
  2. image默认居左,title默认居右
  3. backgroundImage尺寸永远等于按钮尺寸
  4. 对于image:当图片的宽(或高)小于按钮尺寸时,image显示的是原始宽(或高);当图片宽(或高)大于按钮尺寸时,image被压缩到与button等宽(或高)的尺寸。
  5. image和backgroundImage的contentMode属性无法修改。

完了

五、添加事件

前面铺垫了这么多,接下来该讲讲UIButton的主要作用了——响应事件。要响应事件,首先需给button添加对指定事件的监听机制(add target/action),即添加事件。
UIButton能响应的事件(UIControlEvents)有很多,枚举值如下:

    UIControlEventTouchDown             // 单点触摸按下(用户点触屏幕,或者又有新手指落下的时候)
    UIControlEventTouchDownRepeat       // 多点触摸按下(点触计数大于1:用户按下第二、三、或第四根手指的时候)
    UIControlEventTouchDragInside       // 当一次触摸在控件内拖动
    UIControlEventTouchDragOutside      // 当一次触摸在控件外拖动
    UIControlEventTouchDragEnter        // 当一次触摸从控件外拖动到内部
    UIControlEventTouchDragExit         // 当一次触摸从控件内部拖动到外部
    UIControlEventTouchUpInside         // 所有在控件之内触摸抬起
    UIControlEventTouchUpOutside        // 所有在控件之外触摸抬起(点触起始必须落在控件内部才会发送通知)
    UIControlEventTouchCancel           // 所有触摸取消事件(如一次触摸因为放上了太多手指而被取消,或者被上锁或者电话呼入打断)

    UIControlEventValueChanged          // 当控件的值发生改变
    UIControlEventPrimaryActionTriggered NS_ENUM_AVAILABLE_IOS(9_0)    // 当控件的首要行为被触发,例如button的点击事件,slider的滑动事件(iOS9.0引入)

    UIControlEventEditingDidBegin       // 当控件中文本开始编辑时
    UIControlEventEditingChanged        // 当控件中文本发生改变时
    UIControlEventEditingDidEnd         // 当控件中文本编辑结束时
    UIControlEventEditingDidEndOnExit   // 当文本控件内通过按下回车键(或等价行为)结束编辑时

    UIControlEventAllTouchEvents        // 所有触摸事件均触发通知
    UIControlEventAllEditingEvents      // 所有文本编辑事件均触发通知
    UIControlEventApplicationReserved   // 苹果预留给应用使用,不去理会
    UIControlEventSystemReserved        // 苹果预留给内部框架使用,不去理会
    UIControlEventAllEvents             // 所有事件均触发通知

添加事件:

    /**
     添加点击事件

     addTarget:(参数1)    响应目标(由谁来执行响应方法)
     action:(参数2)   调用方法(即响应事件)
     forControlEvents:(参数3) 事件的类型
     */
    [btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];

响应方法:

#pragma mark - 按钮响应方法

- (void)btnAction:(UIButton *)btn {

    NSLog(@"点击了按钮");
}

JMBootstrapButton继承自JMBaseButton,在拥有JMBaseButton的属性下扩展了自己的样式。JMBootstrapButtonConfig继承自JMBaseButtonConfig,在拥有JMBaseButtonConfig的属性下扩展了自己的样式。

System和Custom样式对比:

1. 对于字体颜色
System默认普通状态下为蓝色;Custom默认白色。
2. 对于前景image
Custom样式下可以显示前景图片,见图Custom;System样式不能显示前景图片,变成了图System这衰样...

时时app平台注册网站 12

Custom

时时app平台注册网站 13

System

3. 对于属性adjustsImageWhenHighlighted
@property(nonatomic) BOOL adjustsImageWhenHighlighted; // default is YES. if YES, image is drawn darker when highlighted(pressed)
从官方注释中可以看到,adjustsImageWhenHighlighted为YES时,使得高亮状态(按钮按下)前景图片和背景图片均会变暗。如果将这个属性值设为NO,按下按钮图片就不会变暗了。
经我亲测,System样式下adjustsImageWhenHighlighted默认为NO,而Custom样式下adjustsImageWhenHighlighted默认才为YES,即此default值说的是Custom样式。
按道理按下System样式的按钮,图片应该不会变暗才对(因为adjustsImageWhenHighlighted默认值为NO),但是它还是变暗了!这是为什么呢?还请接着看第4点。

4. 对于高亮状态下
如果是System样式,高亮状态下(即按下按钮)不止图片变暗,文字也跟着变暗了。即使设置了adjustsImageWhenHighlighted=NO,或者设置了高亮状态图片与普通状态图片一致,也是不行。说明System在高亮状态下还会改变另外一个属性使得按钮变暗。但是笔者却找不出是哪个属性改变了,估计看官已经气愤难当磨刀霍霍了...
如果是Custom,文字不管在哪种状态都不会变暗。而图片会因为adjustsImageWhenHighlighted默认值为YES变暗,设置为NO就不变暗了。

JMButton *btn = [[JMButton alloc] initWithFrame:CGRectMake(100, 100, 100, 50) ButtonConfig:buttonConfig];[btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];[self.view addSubView:btn];

二、按钮样式(UIButtonType)

UIButtonType有六种枚举类型,对应外观如下:

时时app平台注册网站 14

之所以选择样式,是为了使用该样式下已经默认设置好的一些属性。
如Add Contact是一个“ ”添加图形。而Detail Disclosure、Info Light及Info Dark都是一个“i”信息图形,Info Light和Info Dark这俩货之间我实在找不到有什么异样,但是这俩货与Detail Disclosure或者其他样式的区别是:这俩货默认属性showsTouchWhenHighlighted=YES,这就使得在点击按钮的时候(高亮状态),这俩货会散发出白色的光芒!将背景色调成黑色看看效果:

时时app平台注册网站 15

点击Info Light样式按钮

最后来说说System和Custom这两个样式。对于System样式,系统为我们预设了一些属性(详见下文对比部分)。Custom顾名思义是自定义的,可设置更多属性。如果我们在storyboard中设置Detail Disclosure、Info Light、Info Dark或者Add Contact按钮的title、backgroundImage等属性,会发现他们马上变成Custom样式;而如果设置了所有非Custom按钮的image(前景图片)属性,那么这些按钮也都将变成Custom样式。说明Custom可操作的属性还是比较多的。

时时app平台注册网站 16JMButtonAll.gif

内容对齐方式

因为UIButton继承自UIControl,而UIControl提供两个属性UIControlContentHorizontalAlignment和UIControlContentVerticalAlignment分别用来设置水平方向和垂直方向的对齐方式,对应效果如下:

时时app平台注册网站 17

JMBaseButton继承自UIButton, 对按钮进行了高度自定义, 包括图片上下左右的控制、图片和文字的间距控制、圆角、背景、边框等,JMBaseButton和JMBaseButtonConfig是不可分离的,后面会讲解到JMBaseButtonConfig,提供了工厂方法和实例方法进行初始化。

读取属性

1. 获取当前状态的属性
使用点语法获取只读属性,如:

btn. currentTitle、btn. currentImage等等

2. 获取指定状态的属性
使用对象方法返回属性:

- (nullable NSString *)titleForState:(UIControlState)state;          // these getters only take a single state value
- (nullable UIColor *)titleColorForState:(UIControlState)state;
- (nullable UIColor *)titleShadowColorForState:(UIControlState)state;
- (nullable UIImage *)imageForState:(UIControlState)state;
- (nullable UIImage *)backgroundImageForState:(UIControlState)state;
- (nullable NSAttributedString *)attributedTitleForState:(UIControlState)state NS_AVAILABLE_IOS(6_0);

版本

Xcode 9.1

继承关系:
UIButton : UIControl : UIView : UIResponder : NSObject

@interface JMButton : UIView- (instancetype)initWithFrame:frame ButtonConfig:(JMBaseButtonConfig *)buttonConfig;/** 点击事件 @param target 事件目标 @param action 事件方法 @param controlEvents 事件类型 */- addTarget:target action:action forControlEvents:(UIControlEvents)controlEvents;/********** 按钮角标配置及显示, 默认不显示角标, 如需要可通过如下方法设置 (frame必需要有值) **********//***** 请注意, 如果需要自定义角标的样式(如调用时下面五个属性时必需在show方法之前设置调用 *****//** 角标的文字颜色 (默认 #FFFFFF) */@property (nonatomic, strong) UIColor *badgeTextColor;/** 角标的背景颜色 (默认 #FF4040) */@property (nonatomic, strong) UIColor *badgeBackgroundColor;/** badgeTextFont (如没有特殊需求, 请勿修改此属性, 此属性只有在控制器加载完成后有效)*/@property (nonatomic, strong) UIFont *badgeTextFont;/** badgeSize (如没有特殊需求, 请勿修改此属性, 此属性只有在控制器加载完成后有效)*/@property (nonatomic, assign) CGSize badgeSize;/** badgeOffset (如没有特殊需求, 请勿修改此属性, 此属性只有在控制器加载完成后有效) */@property (nonatomic, assign) CGPoint badgeOffset;/** badge圆角大小 (如没有特殊需求, 请勿修改此属性, 此属性只有在控制器加载完成后有效, 一般配合badgeSize或badgeOffset使用) */@property (nonatomic, assign) CGFloat badgeRadius;/***** 请注意, 如果需要自定义角标的样式(如调用时上面五个属性时必需在show方法之前设置调用 *****//** 动画类型 */@property (nonatomic, assign) JMBadgeValueAnimationType badgeAnimationType;/** badgeValue(角标数值, 仅对 数值样式有效) */@property (nonatomic, copy) NSString *badgeValue;/** 显示小红点样式 */- showPointBadgeValue;/** 显示New样式 */- showNewBadgeValue;/** 显示数值样式 @param badgeValue 数值 */- showNumberBadgeValue:(NSString *)badgeValue;/** 删除小红点 */- removeBadgeValue;/** 展示动画 */- showBadgeAnimation;@end

一、创建方法

通常,我们创建一个对象会使用[[class alloc] init];方法,但对于UIButton是不推荐使用此方法的。原因有二:

  • UIButton有个按钮样式属性buttonType需要在初始化的时候设定好,代码后期无法改变。假如使用alloc创建,那么buttonType默认为Custom模式。(关于buttonType详见后文)
  • MRC下,如果使用 [[UIButton alloc] init]的方式,需要进行release释放操作;如果使用[UIButton buttonWithType:(UIButtonType)]这种方式,不需要进行release操作。

所以,代码创建UIButton方法为:
[UIButton buttonWithType:(UIButtonType)]

  1. 初始化JMButton并添加到视图

在项目开发中无时不刻的用到了UIButton,如果需要实现一些特殊的效果就需要自定义按钮(例如图片在上,图片在下,配置角标等),如果每一个效果都要自定义按钮的话,那文件显然就很多了,所以我对button进行了封装, 在开发中带来了便捷并减少代码量)

#import <UIKit/UIKit.h>#import "JMBaseButtonConfig.h"@interface JMBaseButton : UIButton  (instancetype)buttonFrame:frame ButtonConfig:(JMBaseButtonConfig *)buttonConfig;- (instancetype)initWithFrame:frame ButtonConfig:(JMBaseButtonConfig *)buttonConfig;@end

时时app平台注册网站 18JMButton.png

JMBootstrapButtonConfig *buttonConfig = [JMBootstrapButtonConfig buttonConfig];buttonConfig.bootstrapType = JMBootstrapTypeDefault;

JMbadgeValue继承自UIlabel,内置三种样式和三种动画, 是按钮上的一个角标, 独立于JMButton, 默认不展示这个角标的

本文由时时app平台注册网站发布于编程知识,转载请注明出处:iOS开采之UI篇(7)—— UIButton【时时app平台注册

关键词: