iOS利用UIView自定义播放器进度条

前段时间做项目遇到播放器自定义进度条的需求,刚开始想的是继承自系统的UISlider去做,内部重写系统方法完成样式定制。后来遇到头疼的问题,在iphone6、7手机上样式显示正常,遇到plus手机进度条样式显示有问题,缓存进度跟最底部的进度高度不匹配,有偏移,调了很多次,都以徒劳告终,后来没办法,自己自定义了一套进度条的实现方案,配合iOS自有手势操作,完成基本功能操作。下面介绍下自定义实现类:

这块功能主要涉及到两个类文件,ZTCSlider,ZTCProgressView:

一、ZTCSlider类,负责进度条交互操作、进度条变化

 ZTCSlider.h,介绍初始化方法,及相关操作回调,返回进度值;播放进度、缓存进度可通过外部调用setter方法传入

#import <UIKit/UIKit.h>

/**
 获取滑动进度回调

 @param value 滑动进度
 */
typedef void (^MGBroadcastGetSlideValueHandler)(CGFloat value);

/**
 开始拖动进度块回调
 */
typedef void (^MGBroadcastSliderPanBeginHandler)(void);

/**
 结束拖动进度块回调

 @param value 进度值
 */
typedef void (^MGBroadcastSliderPanEndHandler)(CGFloat value);


/**
 点击进度条某一部分

 @param value 进度值
 */
typedef void (^MGBroadcastSliderTapSliderHandler)(CGFloat value);

@interface ZTCSlider : UIView

/**
 滑条初始化方法

 @param sliderWidth 滑块宽度
 @param sliderColor 滑块颜色
 @param progressHeight 进度条高度
 @param progressBgColor 进度套背景色
 @param progressPlayedColor 进度条播放后的颜色
 @param progressCachedColor 进度条缓存的颜色
 @param isShowCorner 是否设置进度条圆角效果
 */
- (instancetype)initWithSliderWidth:(CGFloat)sliderWidth
                        sliderColor:(UIColor *)sliderColor
                     progressHeight:(CGFloat)progressHeight
                    progressBgColor:(UIColor *)progressBgColor
                progressPlayedColor:(UIColor *)progressPlayedColor
                 progressCachedColor:(UIColor *)progressCachedColor
                         showCorner:(BOOL)isShowCorner;

///播放值
@property (nonatomic, assign) CGFloat slideValue;

///缓存值
@property (nonatomic, assign) CGFloat cacheValue;

/**
 开始拖动的操作
 */
@property (nonatomic, copy) MGBroadcastSliderPanBeginHandler panBeginHandler;

/**
 结束拖动的操作
 */
@property (nonatomic, copy) MGBroadcastSliderPanEndHandler panEndHandler;

/**
 获取进度值
 */
@property (nonatomic, copy) MGBroadcastGetSlideValueHandler getSlideValueHandler;

/**
 点击进度条某个部分的操作
 */
@property (nonatomic, copy) MGBroadcastSliderTapSliderHandler tapSliderHandler;

@end

ZTCSlider.m

#import "ZTCSlider.h"
#import "ZTCProgressView.h"

@interface ZTCSlider()

///滑块
@property (nonatomic, strong) UIView *sliderView;

///h进度条
@property (nonatomic, strong) ZTCProgressView *progressView;

@property (nonatomic, strong) UIPanGestureRecognizer *panGesture;

@property (nonatomic, strong) UITapGestureRecognizer *tapGesture;

@property (nonatomic, assign) CGFloat sliderWidth;

@property (nonatomic, strong) UIColor *sliderColor;

@property (nonatomic, assign) CGFloat progressHeight;

@property (nonatomic, strong) UIColor *progressBgColor;

@property (nonatomic, strong) UIColor *progressPlayedColor;

@property (nonatomic, strong) UIColor *progressCachedColor;

@property (nonatomic, assign) BOOL isShowCorner;

@end

@implementation ZTCSlider

- (instancetype)initWithSliderWidth:(CGFloat)sliderWidth
                        sliderColor:(UIColor *)sliderColor
                     progressHeight:(CGFloat)progressHeight
                    progressBgColor:(UIColor *)progressBgColor
                progressPlayedColor:(UIColor *)progressPlayedColor
                progressCachedColor:(UIColor *)progressCachedColor
                       showCorner:(BOOL)isShowCorner {
    self = [super init];
    if (self) {
        _sliderWidth = sliderWidth;
        _sliderColor = sliderColor;
        _progressHeight = progressHeight;
        _progressBgColor = progressBgColor;
        _progressPlayedColor = progressPlayedColor;
        _progressCachedColor = progressCachedColor;
        _isShowCorner = isShowCorner;
        [self setUpUI];
    }
    return self;
}

- (void)setUpUI {
    [self addSubview:self.progressView];
    [self addSubview:self.sliderView];
    self.userInteractionEnabled = YES;
    [self addGestureRecognizer:self.panGesture];
    [self addGestureRecognizer:self.tapGesture];
}

- (void)setSlideValue:(CGFloat)slideValue {
    if (_slideValue != slideValue) {
        _slideValue = slideValue;
        CGRect frame = self.sliderView.frame;
        frame.origin.x = _slideValue *(CGRectGetWidth(self.bounds) - CGRectGetWidth(self.sliderView.bounds));
        self.progressView.playValue = _slideValue;
    }
}

- (void)setCacheValue:(CGFloat)cacheValue {
    if (_cacheValue != cacheValue) {
        _cacheValue = cacheValue;
        self.progressView.cacheValue = cacheValue;
    }
}

- (void)tapGesture:(UITapGestureRecognizer *)tap {
    CGPoint point = [tap locationInView:tap.view];
    
    if (point.x <= _sliderWidth / 2) {
        point.x = _sliderWidth / 2;
    }
    
    if (point.x > CGRectGetWidth(self.bounds) - _sliderWidth / 2) {
        point.x = CGRectGetWidth(self.bounds) - _sliderWidth / 2;
    }
    
    CGPoint center = self.sliderView.center;
    center.x = point.x;
    self.sliderView.center = center;
    
    CGRect frame = self.sliderView.frame;
    
    CGFloat value =  frame.origin.x / (CGRectGetWidth(self.bounds) - CGRectGetWidth(self.sliderView.bounds));
    
    self.progressView.playValue = value;
    if (self.tapSliderHandler) {
        self.tapSliderHandler(value);
    }
}

- (void)panGesture:(UIPanGestureRecognizer *)pan {
    //移动的距离
    CGPoint point = [pan translationInView:pan.view];
    CGRect frame = self.sliderView.frame;
    frame.origin.x += point.x;
    
    if (frame.origin.x + CGRectGetWidth(frame) >= CGRectGetWidth(self.bounds)) {
        frame.origin.x = CGRectGetWidth(self.bounds) - CGRectGetWidth(frame);
    }
    
    if (frame.origin.x <= 0) {
        frame.origin.x = 0;
    }
    self.sliderView.frame = frame;

    if (pan.state == UIGestureRecognizerStateBegan) {
        if (self.panBeginHandler) {
            self.panBeginHandler();
        }
    }
    
    CGFloat value =  frame.origin.x / (CGRectGetWidth(self.bounds) - CGRectGetWidth(frame));
    
    self.progressView.playValue = value;
    if (self.getSlideValueHandler) {
        self.getSlideValueHandler(value);
    }
    
    if (pan.state == UIGestureRecognizerStateEnded) {
        if (self.panEndHandler) {
            self.panEndHandler(value);
        }
    }
    [pan setTranslation:CGPointZero inView:pan.view];
}

-(void)layoutSubviews {
    [super layoutSubviews];
    _progressView.frame = CGRectMake(0, (CGRectGetHeight(self.frame) - _progressHeight) * 0.5, CGRectGetWidth(self.frame), _progressHeight);
    _progressView.layer.cornerRadius = _isShowCorner ? _progressHeight * 0.5 : 0;
    _sliderView.frame = CGRectMake(0, (CGRectGetHeight(self.frame) - _sliderWidth) / 2, _sliderWidth, _sliderWidth);
}

- (ZTCProgressView *)progressView {
    if (!_progressView) {
        _progressView = [[ZTCProgressView alloc] initWithProgressBgColor:_progressBgColor
                                                             progressPlayedColor:_progressPlayedColor
                                                             progressCachedColor:_progressCachedColor];
        _progressView.clipsToBounds = YES;
    }
    return _progressView;
}

- (UIPanGestureRecognizer *)panGesture {
    if (!_panGesture) {
        _panGesture =[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGesture:)];
    }
    return _panGesture;
}

- (UITapGestureRecognizer *)tapGesture {
    if (!_tapGesture) {
        _tapGesture =[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesture:)];
        _tapGesture.numberOfTapsRequired = 1;
    }
    return _tapGesture;
}

- (UIView *)sliderView {
    if (!_sliderView) {
        _sliderView =[[UIView alloc] init];
        _sliderView.backgroundColor = _sliderColor ?: [UIColor whiteColor];
        _sliderView.layer.cornerRadius = _sliderWidth * 0.5;
        _sliderView.clipsToBounds = YES;
        _sliderView.layer.shadowOpacity = 0.8;
        _sliderView.layer.shadowOffset = CGSizeMake(0, 2);
        _sliderView.layer.shadowColor = [UIColor blackColor].CGColor;
    }
    return _sliderView;
}

@end

二、ZTCProgressView,负责进度条的显示样式

ZTCProgressView.h

#import <UIKit/UIKit.h>

@interface ZTCProgressView : UIView

/**
 设置进度条样式

 @param progressBgColor 进度条背景色,最底部
 @param progressPlayedColor 进度条已经播放的颜色
 @param progressCachedColor 视频已缓存部分的颜色
 */
- (instancetype)initWithProgressBgColor:(UIColor *)progressBgColor
                    progressPlayedColor:(UIColor *)progressPlayedColor
                    progressCachedColor:(UIColor *)progressCachedColor;


///播放进度
@property (nonatomic, assign) CGFloat playValue;

///加载进度
@property (nonatomic, assign) CGFloat cacheValue;

@end

ZTCProgressView.m

#import "ZTCProgressView.h"

@interface ZTCProgressView()

@property (nonatomic, strong) UIView *cacheView;

@property (nonatomic, strong) UIView *playProgressView;

@property (nonatomic, strong) UIColor *progressBgColor;

@property (nonatomic, strong) UIColor *progressPlayedColor;

@property (nonatomic, strong) UIColor *progressCachedColor;

@end

@implementation ZTCProgressView

- (instancetype)initWithProgressBgColor:(UIColor *)progressBgColor
                    progressPlayedColor:(UIColor *)progressPlayedColor
                    progressCachedColor:(UIColor *)progressCachedColor {
    self = [super init];
    if (self) {
        _progressBgColor = progressBgColor;
        _progressPlayedColor = progressPlayedColor;
        _progressCachedColor = progressCachedColor;
        [self setUpUI];
    }
    return self;
}

- (void)setUpUI {
    self.backgroundColor = _progressBgColor;
    [self addSubview:self.cacheView];
    [self addSubview:self.playProgressView];
}

- (void)setPlayValue:(CGFloat)playValue {
    _playValue = playValue;
    CGRect frame = self.playProgressView.frame;
    frame.size.width = playValue * CGRectGetWidth(self.bounds);
    self.playProgressView.frame = frame;
}

- (void)setCacheValue:(CGFloat)cacheValue {
    _cacheValue = cacheValue;
    CGRect frame = self.cacheView.frame;
    frame.size.width = cacheValue * CGRectGetWidth(self.bounds);;
    self.cacheView.frame = frame;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    self.playProgressView.frame = CGRectMake(0, 0, _playValue * CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds));
    self.cacheView.frame = CGRectMake(0, 0, _cacheValue * CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds));
}

- (UIView *)cacheView {
    if (!_cacheView) {
        _cacheView = [[UIView alloc] init];
        _cacheView.backgroundColor = _progressCachedColor;
    }
    return _cacheView;
}

- (UIView *)playProgressView {
    if (!_playProgressView) {
        _playProgressView =[[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, self.bounds.size.height)];
        _playProgressView.backgroundColor = _progressPlayedColor;
    }
    return _playProgressView;
}

@end

三、ZTCSlider实现调用:

ZTCSlider *slider = [[ZTCSlider alloc] initWithSliderWidth:25 sliderColor:[UIColor whiteColor] progressHeight:5 progressBgColor:[UIColor darkGrayColor] progressPlayedColor:[UIColor blueColor] progressCachedColor:[UIColor lightGrayColor] showCorner:YES];
    
    [slider setPanBeginHandler:^{
        NSLog(@"开始拖动");
    }];
    
    [slider setTapSliderHandler:^(CGFloat value) {
        NSLog(@"点击位置:%f", value);
    }];
    
    [slider setGetSlideValueHandler:^(CGFloat value) {
        NSLog(@"获取进度:%f", value);
    }];
    
    [slider setPanEndHandler:^(CGFloat value) {
        NSLog(@"拖动结束位置:%f", value);
    }];
    
    slider.frame = CGRectMake(50, 200, 275, 35);
    slider.backgroundColor = [UIColor orangeColor];
    [self.view addSubview:slider];

注:实际开发中,进度条大多会跟播放器同步使用,ZTCSlider类中的进度条操作回调传值可用来控制播放器的进度。

显示效果:

说明:这里的橙色是创建的slider的实际大小,所以对于系统的UISlider手势操作范围小的问题,也完美的解决了;蓝色是已经播放的进度,深灰色是背景颜色,缓存进度由于我这里没有嵌入播放器,没有展示出来。可根据需要嵌入播放器中来加载显示缓存进度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值