iOS实现垂直滑动条效果

我们知道在 iOS 开发中,有一个控件经常用到,那就是滑动条(UISlider),可以满足我们滑动取值的需求。但是现在有一个需求,就是需要一个垂直的滑动条,而 UISlider 并不能设置为垂直滑动,所以我们就需要自己定义一个控件来实现垂直的要求。

整理之后,我们可以得出需要以下的基本需求:

  • 可以上下滑动
  • 按钮可以自定义图片
  • 可以设置最小值
  • 可以设置最大值
  • 可以在滑动过程中获取实时的值
  • 可以在滑动结束时获取到最终的值
  • 可以设置进度背景色

我们的实现原理就是实现一个自定义的 UIView,然后在上面添加需要用到的控件,对控件添加一定的手势功能,从而实现垂直滑动。实现了一个单独的类,功能不多,但是能满足以上基本的需求,代码如下,代码中用到的宏可以自行替换,开箱即用,简单明了:

VerticalSlider.h

//
//  VerticalSlider.h
//  
//
//  Created by huang zhengguo on 2019/8/30.
//  Copyright  2019 huang zhengguo . All rights reserved.
//
 
#import <UIKit/UIKit.h>
 
NS_ASSUME_NONNULL_BEGIN
 
@interface VerticalSlider : UIView
 
@property (assign, nonatomic) float value;
@property (strong, nonatomic) UIImage *thumImage;
@property (assign, nonatomic) float minimumValue;
@property (assign, nonatomic) float maximumValue;
 
@property (copy, nonatomic) void (^passValue) (float);
@property (copy, nonatomic) void (^passEndValue) (float);
 
/**
 * 初始化滑动条
 *
 * @param frame 大小
 * @param title 标题
 * @param progressColor 进度颜色
 * @param thumImage 滑动按钮背景
 *
 * @return 垂直滑动条
 *
 */
- (instancetype)initWithFrame:(CGRect)frame title:(NSString *)title progressColor:(UIColor *)progressColor thumImage:(NSString *)thumImage;
 
@end
 
NS_ASSUME_NONNULL_END

VerticalSlider.m

//
//  VerticalSlider.m
// 
//
//  Created by huang zhengguo on 2019/8/30.
//  Copyright  2019 zhengguohuang. All rights reserved.
//
 
#import "VerticalSlider.h"
 
#define THUM_BTN_WIDTH 30.0
#define THUM_BTN_HEIGHT 50.0
 
@interface VerticalSlider()
 
@property (strong, nonatomic) UIButton *thumBtn;
// 使用两个label表示进度,一个背景,一个进度
@property (strong, nonatomic) UILabel *backLabel;
@property (strong, nonatomic) UILabel *progressLabel;
// 底部标题
@property (strong, nonatomic) UILabel *titleLabel;
// 值标题
@property (strong, nonatomic) UILabel *valueLabel;
 
@end
 
@implementation VerticalSlider
 
- (instancetype)initWithFrame:(CGRect)frame title:(NSString *)title progressColor:(UIColor *)progressColor thumImage:(NSString *)thumImage {
    if (self = [super initWithFrame:frame]) {
        // 滑动按钮
        self.thumBtn = [[UIButton alloc] init];
 
        [self.thumBtn setBackgroundImage:[UIImage imageNamed:thumImage] forState:UIControlStateNormal];
        self.thumBtn.translatesAutoresizingMaskIntoConstraints = NO;
        
        UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(thumbPanAction:)];
 
        [self.thumBtn addGestureRecognizer:panGestureRecognizer];
        [self addSubview:self.thumBtn];
 
        // 进度条
        self.backLabel = [[UILabel alloc] init];
 
        self.backLabel.backgroundColor = [progressColor colorWithAlphaComponent:0.3];
        self.backLabel.translatesAutoresizingMaskIntoConstraints = NO;
 
        [self addSubview:self.backLabel];
 
        self.progressLabel = [[UILabel alloc] init];
 
        self.progressLabel.backgroundColor = progressColor;
        self.progressLabel.translatesAutoresizingMaskIntoConstraints = NO;
 
        [self addSubview:self.progressLabel];
 
        // 底部标题
        self.titleLabel = [[UILabel alloc] init];
 
        self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
        self.titleLabel.textAlignment = NSTextAlignmentCenter;
        self.titleLabel.textColor = [UIColor whiteColor];
        self.titleLabel.text = title;
 
        [self addSubview:self.titleLabel];
 
        // 顶部值
        self.valueLabel = [[UILabel alloc] init];
 
        self.valueLabel.translatesAutoresizingMaskIntoConstraints = NO;
        self.valueLabel.textAlignment = NSTextAlignmentCenter;
        self.valueLabel.textColor = [UIColor whiteColor];
 
        [self addSubview:self.valueLabel];
 
        [self bringSubviewToFront:self.thumBtn];
 
        [self setConstraints];
        
        // 初始化数据
        self.value = 0.0;
    }
    
    return self;
}
 
#pragma mark --- 按钮拖动方法
- (void)thumbPanAction:(UIPanGestureRecognizer *)panGestureRecognizer {
    // 转换坐标
    CGPoint point = [panGestureRecognizer translationInView:self];
    
    CGFloat yOriginPoint = panGestureRecognizer.view.frame.origin.y + point.y;
    if (yOriginPoint >=self.backLabel.frame.origin.y && yOriginPoint <= (self.backLabel.frame.origin.y + self.backLabel.frame.size.height - THUM_BTN_HEIGHT)) {
        panGestureRecognizer.view.frame = CGRectMake(panGestureRecognizer.view.frame.origin.x, panGestureRecognizer.view.frame.origin.y + point.y, THUM_BTN_WIDTH, THUM_BTN_HEIGHT);
        
        self.value = 1.0 - (yOriginPoint - self.backLabel.frame.origin.y) / (self.backLabel.frame.size.height - THUM_BTN_HEIGHT);
        if (self.passValue) {
            KMYLOG(@"colorValue = %f", self.value);
            self.passValue(self.value);
        }
    }
 
    // 转换成原来坐标系的坐标
    [panGestureRecognizer setTranslation:CGPointMake(0, 0) inView:self];
    
    if (panGestureRecognizer.state == UIGestureRecognizerStateEnded) {
        if (self.passEndValue) {
            KMYLOG(@"结束滑动");
            // 转为字符串,又转为float,是为了去的两位小数的浮点数
            self.passEndValue([[NSString stringWithFormat:@"%.2f", self.value] floatValue]);
        }
    }
}
 
- (void)setValue:(float)value {
    _value = value;
 
    self.thumBtn.frame = CGRectMake(self.thumBtn.frame.origin.x, (self.backLabel.frame.size.height - THUM_BTN_HEIGHT) * (1 - value) + self.backLabel.frame.origin.y, THUM_BTN_WIDTH, THUM_BTN_HEIGHT);
    self.progressLabel.frame = CGRectMake(self.progressLabel.frame.origin.x, self.thumBtn.frame.origin.y + THUM_BTN_HEIGHT, self.progressLabel.frame.size.width, self.backLabel.frame.origin.y + self.backLabel.frame.size.height - self.thumBtn.frame.origin.y - THUM_BTN_HEIGHT);
    self.valueLabel.text = [NSString stringWithFormat:@"%.0f%%", value * 100];
}
 
- (void)setConstraints {
    NSArray *titleLabelArray = @[self.titleLabel, self.valueLabel];
    for (UILabel *label in titleLabelArray) {
        NSLayoutConstraint *labelLeadingLayoutConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0];
        NSLayoutConstraint *labelTrailingLayoutConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0];
        NSLayoutConstraint *labelHeightLayoutConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:30.0];
 
        [self addConstraints:@[labelLeadingLayoutConstraint, labelTrailingLayoutConstraint, labelHeightLayoutConstraint]];
 
        if (label == self.titleLabel) {
            NSLayoutConstraint *labelBottomLayoutConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0];
 
            [self addConstraint:labelBottomLayoutConstraint];
        } else if (label == self.valueLabel) {
            NSLayoutConstraint *labelTopLayoutConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0];
 
            [self addConstraint:labelTopLayoutConstraint];
        }
    }
    
    NSArray *labelArray = @[self.backLabel, self.progressLabel];
    for (UILabel *label in labelArray) {
        NSLayoutConstraint *progressCenterXLayoutConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];
        NSLayoutConstraint *progressBottomLayoutConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.titleLabel attribute:NSLayoutAttributeTop multiplier:1.0 constant:-8.0];
        NSLayoutConstraint *progressWidthLayoutConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:3.0];
 
        [self addConstraints:@[progressCenterXLayoutConstraint, progressBottomLayoutConstraint, progressWidthLayoutConstraint]];
 
        if (label == self.backLabel) {
            NSLayoutConstraint *progressTopLayoutConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.valueLabel attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0];
 
            [self addConstraint:progressTopLayoutConstraint];
        } else {
            NSLayoutConstraint *progressHeightLayoutConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:0.0];
 
            [self addConstraint:progressHeightLayoutConstraint];
        }
    }
 
    NSLayoutConstraint *thumBtnCenterXLayoutConstraint = [NSLayoutConstraint constraintWithItem:self.thumBtn attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];
    NSLayoutConstraint *thumBtnBottomLayoutConstraint = [NSLayoutConstraint constraintWithItem:self.thumBtn attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.backLabel attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0];
    NSLayoutConstraint *thumBtnWidthLayoutConstraint = [NSLayoutConstraint constraintWithItem:self.thumBtn attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:THUM_BTN_WIDTH];
    NSLayoutConstraint *thumBtnHeightLayoutConstraint = [NSLayoutConstraint constraintWithItem:self.thumBtn attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:THUM_BTN_HEIGHT];
 
    [self addConstraints:@[thumBtnCenterXLayoutConstraint, thumBtnBottomLayoutConstraint, thumBtnWidthLayoutConstraint, thumBtnHeightLayoutConstraint]];
}
 
- (void)layoutSubviews {
    [super layoutSubviews];
    
    self.thumBtn.frame = CGRectMake(self.thumBtn.frame.origin.x, (self.backLabel.frame.size.height - THUM_BTN_HEIGHT) * (1 - self.value) + self.backLabel.frame.origin.y, THUM_BTN_WIDTH, THUM_BTN_HEIGHT);
    self.progressLabel.frame = CGRectMake(self.progressLabel.frame.origin.x, self.thumBtn.frame.origin.y + THUM_BTN_HEIGHT, self.progressLabel.frame.size.width, self.backLabel.frame.origin.y + self.backLabel.frame.size.height - self.thumBtn.frame.origin.y - THUM_BTN_HEIGHT);
}
 
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/
 
@end

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • iOS Segment带滑动条切换效果

    本文实例为大家分享了vue + element ui实现锚点定位的具体代码,供大家参考,具体内容如下 #import "ViewController.h"   @interface ViewController ()   @property (nonatomic,strong) NSArray *arrTitle;   @property (nonatomic,strong) UIView *flyBar;   @end   @implementation ViewController

  • iOS实现双向滑动条效果

    最近做项目,碰到一种双向滑动条,自己实现了一下,随便写一下思路,方便以后开发,避免重复写代码,以后粘贴就行了.封装了一下,代码如下: #import <UIKit/UIKit.h> typedef NSString* (^HLDoubleSlideViewSwitchStrBock)(CGFloat count); @interface HLDoubleSlideView : UIView @property(nonatomic,assign)CGFloat maxValue; @proper

  • iOS实现垂直滑动条效果

    我们知道在 iOS 开发中,有一个控件经常用到,那就是滑动条(UISlider),可以满足我们滑动取值的需求.但是现在有一个需求,就是需要一个垂直的滑动条,而 UISlider 并不能设置为垂直滑动,所以我们就需要自己定义一个控件来实现垂直的要求. 整理之后,我们可以得出需要以下的基本需求: 可以上下滑动 按钮可以自定义图片 可以设置最小值 可以设置最大值 可以在滑动过程中获取实时的值 可以在滑动结束时获取到最终的值 可以设置进度背景色 我们的实现原理就是实现一个自定义的 UIView,然后在上

  • iOS实现多个垂直滑动条并列视图

    本文实例为大家分享了iOS实现多个垂直滑动条并列视图的具体代码,供大家参考,具体内容如下 上一篇文章我们实现了一个垂直滑动条的类 (VerticalSlider),用来满足垂直滑动的需求.那么这篇文章我们来把多个垂直滑动条放到一起,可以在一个视图上并排多个垂直滑动条,也算是一个实际应用的场景. 需求: 同时展示多个垂直滑动条 每个滑动条高度和视图高度相同,随视图高度自动变化 所有滑动条宽度相同,宽度为视图宽度除以滑动条个数 根据提供的滑动条的值更新视图 传递滑动条的索引和值 需求还是比较简单的,

  • iOS实现音频进度条效果

    前几天开发群里有一个老兄问了一个开发问题,他们的需求是要做一个类似音频进度条的东西,我感觉设计还不错,于是就写了个小demo供大家参考,在争得了他的同意的情况下写下这篇文章. 话不多说先上效果图 看到这个效果的时候我感觉相对比较难的点有两点: 一.是这个进度条的进度颜色变化,这里思路还是比较清晰的,直接用layer的mask来做就可以. 二.第二点就是这个各各条条的高度不一致又没有规律可言,在各个方法中我最终选择用随机数来做.   好了思路清晰了,那就开始撸代码了. 首先创建一个View CYX

  • Android SeekBar实现滑动条效果

    本文实例为大家分享了Android SeekBar实现滑动条效果的具体代码,供大家参考,具体内容如下 SeekBar是ProgressBar的一个子类,下面我们用一个可以改变并显示当前进度的拖动条例子来演示一下它的使用: 1.main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/

  • 使用Javascript开发sliding-nav带滑动条效果的导航插件

    本文介绍如何使用纯Javascript来开发一款简单的JS插件,本插件可以实现鼠标悬停在导航上时,下方的滑动条自动从当前菜单滑动到所选菜单当中去. 本项目的源代码寄宿于GitHub,记得点小星星哦: https://github.com/dosboy0716/sliding-nav 一.前言 效果如下图: 二.使用方法 本插件只需要如下的三步,就可以在您的项目中使用: 1.在</body>标记结束前,引用sliding-nav.js文件 2.在需要滑动条的菜单容器上加类名 sliding-na

  • 高仿网易新闻顶部滑动条效果实现代码

    这个是网易新闻的主界面,我们知道底部可以用tabhost实现,这个很容易,我们在其他软件中也会经常用到. 至于顶部的滑动条,个人感觉还是比较漂亮的所以今天也模仿了下,网易顶部滑动条的效果,由于初次模仿这种效果,可能有些地方还不够完美,不过基本已经实现,希望大家能够喜欢. 废话不多说,下面上代码: 首先是布局layout下的main.xml 复制代码 代码如下: <?xmlversion="1.0"encoding="utf-8"?> <Relati

  • 基于iOS实现音乐震动条效果

    一.简单分析 音乐震动条不需要与用户交互.我们可以使用复制层来操作.添加震动条.添加动画. 复制层说明 //创建复制层 -(void)createRepl{ //复制层 CAReplicatorLayer * repL = [CAReplicatorLayer layer]; repL.frame = self.contentV.bounds; //复制6份 repL.instanceCount = 6; //形变,每一个形变都是相对于上一个复制出来的子层开始的 repL.instanceTra

  • JS实现滑动条案例

    本文实例为大家分享了JS实现滑动条效果的具体代码,供大家参考,具体内容如下 在完成这个案例之前需要看一下这个博客:JS案例-添加缓动画 这个案例会用到上面博客的缓动画函数.实现效果如下: 完整代码如下: html代码: <!DOCTYPE html> <html lang="en">   <head>     <meta charset="UTF-8">     <meta http-equiv="X-

随机推荐