自定义购物数量按钮

.h

#import <UIKit/UIKit.h>

@protocol PPNumberButtonDelegate <NSObject>
@optional
/** 加减按钮点击响应的代理回调*/
- (void)pp_numberButton:(__kindof UIView *)numberButton number:(NSString *)number;
@end

IB_DESIGNABLE
@interface PPNumberButton : UIView
/**
 *  通过类方法创建一个按钮实例对象
 */
+ (instancetype)numberButtonWithFrame:(CGRect)frame;

/** 加减按钮的Block回调*/
@property (nonatomic, copy) void(^numberBlock)(NSString *number);
/** 代理*/
@property (nonatomic, weak) id<PPNumberButtonDelegate> delegate;

#pragma mark - 自定义样式属性设置
/** 是否开启抖动动画,默认NO*/
@property (nonatomic, assign ) IBInspectable BOOL shakeAnimation;
/** 为YES时,初始化时减号按钮隐藏(饿了么/百度外卖/美团外卖按钮模式),default is NO*/
@property (nonatomic, assign ) IBInspectable BOOL decreaseHide;

/** 设置边框的颜色,如果没有设置颜色,就没有边框 */
@property (nonatomic, strong ) IBInspectable UIColor *borderColor;

/** 输入框中的内容 */
@property (nonatomic, copy   ) NSString *currentNumber;
/** 输入框中的字体大小 */
@property (nonatomic, assign ) IBInspectable CGFloat inputFieldFont;

/** 加减按钮的字体大小 */
@property (nonatomic, assign ) IBInspectable CGFloat buttonTitleFont;
/** 加按钮背景图片 */
@property (nonatomic, strong ) IBInspectable UIImage *increaseImage;
/** 减按钮背景图片 */
@property (nonatomic, strong ) IBInspectable UIImage *decreaseImage;
/** 加按钮标题 */
@property (nonatomic, copy   ) IBInspectable NSString *increaseTitle;
/** 减按钮标题 */
@property (nonatomic, copy   ) IBInspectable NSString *decreaseTitle;

/** 最小值, default is 1 */
@property (nonatomic, assign ) IBInspectable NSInteger minValue;
/** 最大值 */
@property (nonatomic, assign ) NSInteger maxValue;

@end

#pragma mark - NSString分类
@interface NSString (PPNumberButton)
/**
 字符串 nil, @"", @"  ", @"\n" Returns NO;
 其他 Returns YES.
 */
- (BOOL)isNotBlank;
@end
.m

#import "PPNumberButton.h"

#ifdef DEBUG
#define PPLog(...) printf("[%s] %s [第%d行]: %s\n", __TIME__ ,__PRETTY_FUNCTION__ ,__LINE__, [[NSString stringWithFormat:__VA_ARGS__] UTF8String])
#else
#define PPLog(...)
#endif

@interface PPNumberButton () <UITextFieldDelegate>
/** 减按钮*/
@property (nonatomic, strong) UIButton *decreaseBtn;
/** 加按钮*/
@property (nonatomic, strong) UIButton *increaseBtn;
/** 数量展示/输入框*/
@property (nonatomic, strong) UITextField *textField;
/** 快速加减定时器*/
@property (nonatomic, strong) NSTimer *timer;
/** 控件自身的宽*/
@property (nonatomic, assign) CGFloat width;
/** 控件自身的高*/
@property (nonatomic, assign) CGFloat height;

@end

@implementation PPNumberButton

#pragma mark - 初始化
- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame])
    {
        [self setupUI];
        //整个控件的默认尺寸(和某宝上面的按钮同样大小)
        if(CGRectIsEmpty(frame)) {self.frame = CGRectMake(0, 0, 110, 30);};
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)coder
{
    if (self = [super initWithCoder:coder])
    {
        [self setupUI];
    }
    return self;
}

+ (instancetype)numberButtonWithFrame:(CGRect)frame
{
    return [[PPNumberButton alloc] initWithFrame:frame];
}
#pragma mark - 设置UI子控件
- (void)setupUI
{
    self.backgroundColor = [UIColor whiteColor];
    self.layer.cornerRadius = 3.f;
    self.clipsToBounds = YES;

    _minValue = 1;
    _maxValue = NSIntegerMax;
    _inputFieldFont = 15;
    _buttonTitleFont = 17;

    //加,减按钮
    _increaseBtn = [self creatButton];
    _decreaseBtn = [self creatButton];
    [self addSubview:_decreaseBtn];
    [self addSubview:_increaseBtn];

    //数量展示/输入框
    _textField = [[UITextField alloc] init];
    _textField.delegate = self;
    _textField.textAlignment = NSTextAlignmentCenter;
    _textField.keyboardType = UIKeyboardTypeNumberPad;
    _textField.font = [UIFont systemFontOfSize:_inputFieldFont];
    _textField.text = [NSString stringWithFormat:@"%ld",_minValue];

    [self addSubview:_textField];
}

//设置加减按钮的公共方法
- (UIButton *)creatButton
{
    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    button.titleLabel.font = [UIFont boldSystemFontOfSize:_buttonTitleFont];
    [button setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
    [button addTarget:self action:@selector(touchDown:) forControlEvents:UIControlEventTouchDown];
    [button addTarget:self action:@selector(touchUp:) forControlEvents:UIControlEventTouchUpOutside|UIControlEventTouchUpInside|UIControlEventTouchCancel];
    return button;
}

#pragma mark - layoutSubviews
- (void)layoutSubviews
{
    [super layoutSubviews];

    _width =  self.frame.size.width;
    _height = self.frame.size.height;
    _textField.frame = CGRectMake(_height, 0, _width - 2*_height, _height);
    _increaseBtn.frame = CGRectMake(_width - _height, 0, _height, _height);

    // 当按钮为"减号按钮隐藏模式(饿了么/百度外卖/美团外卖按钮样式)"
    if (_decreaseHide)
    {
        _textField.hidden = YES;
        _textField.text = [NSString stringWithFormat:@"%ld",_minValue-1];
        _decreaseBtn.alpha = 0;
        _decreaseBtn.frame = CGRectMake(_width-_height, 0, _height, _height);
        self.backgroundColor = [UIColor clearColor];
    }
    else
    {
        _decreaseBtn.frame = CGRectMake(0, 0, _height, _height);
    }
}

#pragma mark - UITextFieldDelegate
- (void)textFieldDidEndEditing:(UITextField *)textField
{
    NSString *minValueString = [NSString stringWithFormat:@"%ld",_minValue];
    NSString *maxValueString = [NSString stringWithFormat:@"%ld",_maxValue];

    [textField.text isNotBlank] == NO || textField.text.integerValue < _minValue ? _textField.text = minValueString : nil;
    textField.text.integerValue > _maxValue ? _textField.text = maxValueString : nil;
    _numberBlock ? _numberBlock(_textField.text) : nil;
    _delegate ? [_delegate pp_numberButton:self number:_textField.text] : nil;
}

#pragma mark - 加减按钮点击响应
/**
 点击: 单击逐次加减,长按连续快速加减
 */
- (void)touchDown:(UIButton *)sender
{
    [_textField resignFirstResponder];

    if (sender == _increaseBtn)
    {
        _timer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(increase) userInfo:nil repeats:YES];
    }
    else
    {
        _timer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(decrease) userInfo:nil repeats:YES];
    }
    [_timer fire];
}

/**
 手指松开
 */
- (void)touchUp:(UIButton *)sender
{
    [self cleanTimer];
}

/**
 加运算
 */
- (void)increase
{
    [_textField.text isNotBlank] == NO ? _textField.text = [NSString stringWithFormat:@"%ld",_minValue] : nil;
    NSInteger number = [_textField.text integerValue] + 1;

    if (number <= _maxValue)
    {
        // 当按钮为"减号按钮隐藏模式",且输入框值==设定最小值,减号按钮展开
        if (_decreaseHide && number == _minValue)
        {
            [self rotationAnimationMethod];
            [UIView animateWithDuration:0.25 animations:^{
                _decreaseBtn.alpha = 1;
                _decreaseBtn.frame = CGRectMake(0, 0, _height, _height);
            } completion:^(BOOL finished) {
                _textField.hidden = NO;
            }];
        }

        _textField.text = [NSString stringWithFormat:@"%ld", number];
        _numberBlock ? _numberBlock(_textField.text) : nil;
        _delegate ? [_delegate pp_numberButton:self number:_textField.text] : nil;
    }
    else
    {
        if (_shakeAnimation) { [self shakeAnimationMethod]; }
        PPLog(@"已超过最大数量%ld",_maxValue);
    }
}

/**
 减运算
 */
- (void)decrease
{
    [_textField.text isNotBlank] == NO ? _textField.text = [NSString stringWithFormat:@"%ld",_minValue] : nil;
    NSInteger number = [_textField.text integerValue] - 1;

    if (number >= _minValue)
    {
        _textField.text = [NSString stringWithFormat:@"%ld", number];
        _numberBlock ? _numberBlock(_textField.text) : nil;
        _delegate ? [_delegate pp_numberButton:self number:_textField.text] : nil;
    }
    else
    {
        // 当按钮为"减号按钮隐藏模式",且输入框值 < 设定最小值,减号按钮隐藏
        if (_decreaseHide && number < _minValue) {
            _textField.hidden = YES;
            [self rotationAnimationMethod];
            [UIView animateWithDuration:0.25 animations:^{
                _decreaseBtn.alpha = 0;
                _decreaseBtn.frame = CGRectMake(_width-_height, 0, _height, _height);
            } completion:^(BOOL finished) {
                _textField.text = [NSString stringWithFormat:@"%ld",_minValue-1];
            }];
            return;
        }
        if (_shakeAnimation) { [self shakeAnimationMethod]; }
        PPLog(@"数量不能小于%ld",_minValue);
    }
}

/**
 清除定时器
 */
- (void)cleanTimer
{
    if (_timer.isValid)
    {
        [_timer invalidate];
        _timer = nil;
    }
}

#pragma mark - 加减按钮的属性设置
- (void)setMinValue:(NSInteger)minValue
{
    _minValue = minValue;
    _textField.text = [NSString stringWithFormat:@"%ld",minValue];
}

- (void)setBorderColor:(UIColor *)borderColor
{
    _borderColor = borderColor;

    _decreaseBtn.layer.borderColor = [borderColor CGColor];
    _increaseBtn.layer.borderColor = [borderColor CGColor];
    self.layer.borderColor = [borderColor CGColor];

    _decreaseBtn.layer.borderWidth = 0.5;
    _increaseBtn.layer.borderWidth = 0.5;
    self.layer.borderWidth = 0.5;
}

- (void)setButtonTitleFont:(CGFloat)buttonTitleFont
{
    _buttonTitleFont = buttonTitleFont;
    _increaseBtn.titleLabel.font = [UIFont boldSystemFontOfSize:buttonTitleFont];
    _decreaseBtn.titleLabel.font = [UIFont boldSystemFontOfSize:buttonTitleFont];
}

- (void)setIncreaseTitle:(NSString *)increaseTitle
{
    _increaseTitle = increaseTitle;
    [_increaseBtn setTitle:increaseTitle forState:UIControlStateNormal];
}

- (void)setDecreaseTitle:(NSString *)decreaseTitle
{
    _decreaseTitle = decreaseTitle;
    [_decreaseBtn setTitle:decreaseTitle forState:UIControlStateNormal];
}

- (void)setIncreaseImage:(UIImage *)increaseImage
{
    _increaseImage = increaseImage;
    [_increaseBtn setBackgroundImage:increaseImage forState:UIControlStateNormal];
}

- (void)setDecreaseImage:(UIImage *)decreaseImage
{
    _decreaseImage = decreaseImage;
    [_decreaseBtn setBackgroundImage:decreaseImage forState:UIControlStateNormal];
}

#pragma mark - 输入框中的内容设置
- (NSString *)currentNumber
{
    return _textField.text;
}

- (void)setCurrentNumber:(NSString *)currentNumber
{
    _textField.text = currentNumber;
}

- (void)setInputFieldFont:(CGFloat)inputFieldFont
{
    _inputFieldFont = inputFieldFont;
    _textField.font = [UIFont systemFontOfSize:inputFieldFont];
}
#pragma mark - 核心动画
/**
 抖动动画
 */
- (void)shakeAnimationMethod
{
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position.x"];
    CGFloat positionX = self.layer.position.x;
    animation.values = @[@(positionX-10),@(positionX),@(positionX+10)];
    animation.repeatCount = 3;
    animation.duration = 0.07;
    animation.autoreverses = YES;
    [self.layer addAnimation:animation forKey:nil];
}
/**
 旋转动画
 */
- (void)rotationAnimationMethod
{
    CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
    rotationAnimation.toValue = @(M_PI*2);
    rotationAnimation.duration = 0.25;
    rotationAnimation.fillMode = kCAFillModeForwards;
    rotationAnimation.removedOnCompletion = NO;
    [_decreaseBtn.layer addAnimation:rotationAnimation forKey:nil];
}

@end

#pragma mark - NSString分类

@implementation NSString (PPNumberButton)
- (BOOL)isNotBlank
{
    NSCharacterSet *blank = [NSCharacterSet whitespaceAndNewlineCharacterSet];
    for (NSInteger i = 0; i < self.length; ++i)
    {
        unichar c = [self characterAtIndex:i];
        if (![blank characterIsMember:c])
        {
            return YES;
        }
    }
    return NO;
}

@end

在需要的地方创建按钮就行了.

 PPNumberButton *numberButton = [PPNumberButton numberButtonWithFrame:CGRectMake(100, 100, 110, 30)];
    // 开启抖动动画
    numberButton.shakeAnimation = YES;
    // 设置最小值
    numberButton.minValue = 1;
    // 设置最大值
    numberButton.maxValue = 10;
    // 设置输入框中的字体大小
    numberButton.inputFieldFont = 23;
    numberButton.increaseTitle = @"+";
    numberButton.decreaseTitle = @"-";

    numberButton.numberBlock = ^(NSString *num){
        NSLog(@"%@",num);
    };
    [self.view addSubview:numberButton];

源码地址:

GitHub: https://github.com/jkpang;

时间: 2025-01-05 05:34:12

自定义购物数量按钮的相关文章

自定义新浪微博分享按钮样式

自定义新浪微博分享按钮样式 新浪开放平台虽然有分享按钮的组件(http://open.weibo.com/sharebutton)并且提供了两种格式的应用方法:WBML和JS,但还是无法据自己的需求做到自定义样式. 为了解决该问题,我们首先来看下新浪所生成的JS代码: <script type="text/javascript" charset="utf-8"> (function(){ var _w = 32 , _h = 32; var param

[WinForm][DevExpress]自定义GridControl中按钮文字内容

最近项目开发中,使用到了GridControl的FindPanel,这样可以很好的对数据进行筛选,可是所展现的按钮文字是英文,如图: 那怎么定义两个按钮问题,以符合项目需求了?经过一番搜索发现利用GridLocalizer可以很好实现: 核心代码: public class BuilderGridLocalizer : GridLocalizer { Dictionary<GridStringId, string> CusLocalizedKeyValue = null; /// <su

Android 自定义UI圆角按钮

Android实际开发中我们一般需要圆角的按钮,一般情况下我们可以让美工做出来相应的按钮图片,然后放上去即可,另外我们可以在布局文件中直接设置,也可以达到一样的效果.下面讲解在布局文件中自定义圆角按钮的小Demo. 代码很简单,实现效果图: 源代码: 源代码: 这里主要是xml布局文件实现: MainActivity: package com.android_drawableresource; import android.app.Activity; import android.os.Bund

iOS自定义的UISwitch按钮

UISwitch开关控件 开关代替了点选框.开关是到目前为止用起来最简单的控件,不过仍然可以作一定程度的定制化. 一.创建 UISwitch* mySwitch = [[ UISwitchalloc]initWithFrame:CGRectMake(200.0,10.0,0.0,0.0)]; 是不是很奇怪,大小竟然是0.0×0.0,没错,系统会自动帮你决定最佳的尺寸,你自己写的尺寸会被忽略掉,你只要定义好相对父视图的位置就好了.默认尺寸为79 * 27. 二.显示控件 [ parrentView

山寨“饿了么”应用中添加菜品数量按钮效果

山寨“饿了么”应用中添加菜品数量按钮效果 本人视频教程系类   iOS中CALayer的使用 最终效果: 山寨源头: 源码:(此源码解决了重用问题,可以放心的放在cell中使用) AddAndDeleteButton.h 与 AddAndDeleteButton.m // // AddAndDeleteButton.h // LabelControll // // Created by YouXianMing on 14/12/11. // Copyright (c) 2014年 YouXian

制作自定义背景Button按钮、自定义形状Button的全攻略(转)

在Android开发应用中,默认的Button是由系统渲染和管理大小的.而我们看到的成功的移动应用,都是有着酷炫的外观和使用体验的.因此,我们在开发产品的时候,需要对默认按钮进行美化.在本篇里,笔者结合在应用开发中的经验,探讨一下自定义背景的按钮.自定义形状按钮的实现方法. 首先看实现效果截图: 自定义背景的按钮目前有2种方式实现,矢量和位图. 1. 矢量图形绘制的方式 矢量图形绘制的方式实现简单,适合对于按钮形状和图案要求不高的场合.步骤如下: (a) 使用xml定义一个圆角矩形,外围轮廓线实

js学习-购物数量+1,-1

看到商城里面,可以加减购物数量,就想学习一下 代码如下: <!DOCTYPE html><html><head> <meta charset="utf-8" /> <title>js点击加1的实现</title> <style type="text/css"> .jia{height:16px;width: auto;text-align:center;display: inline

实际iOS编程中遇到的自定义导航栏按钮,导致手势返回失效的解决方法

1\在实际编程过程中往往需要自定义导航栏上面的按钮,也就用: - (instancetype)initWithCustomView:(UIView *)customView; 但用了这个方法后可能会导致iOS7,8的手势返回失效,解决方法就是在自定义的导航栏的viewDidLoad方法中添加如下代码 注意:只有用系统的导航栏,或者继承于系统的导航栏才可以用Push方法,并且自带返回手势. - (void)viewDidLoad { [super viewDidLoad]; __weak type

小程序自定义多选按钮 给后台传值方法

小程序自定义多选按钮 1.html部分 <checkbox-group bindchange="checkboxChange" class = "flex"> <label class="checkbox {{item.checked?'active':''}}" wx:for="{{items}}" bindtap="addclass" data-index="{{index}