前段时间做项目遇到播放器自定义进度条的需求,刚开始想的是继承自系统的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)sliderWidthsliderColor:(UIColor *)sliderColorprogressHeight:(CGFloat)progressHeightprogressBgColor:(UIColor *)progressBgColorprogressPlayedColor:(UIColor *)progressPlayedColorprogressCachedColor:(UIColor *)progressCachedColorshowCorner:(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)sliderWidthsliderColor:(UIColor *)sliderColorprogressHeight:(CGFloat)progressHeightprogressBgColor:(UIColor *)progressBgColorprogressPlayedColor:(UIColor *)progressPlayedColorprogressCachedColor:(UIColor *)progressCachedColorshowCorner:(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:_progressBgColorprogressPlayedColor:_progressPlayedColorprogressCachedColor:_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 *)progressBgColorprogressPlayedColor:(UIColor *)progressPlayedColorprogressCachedColor:(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 *)progressBgColorprogressPlayedColor:(UIColor *)progressPlayedColorprogressCachedColor:(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手势操作范围小的问题,也完美的解决了;蓝色是已经播放的进度,深灰色是背景颜色,缓存进度由于我这里没有嵌入播放器,没有展示出来。可根据需要嵌入播放器中来加载显示缓存进度。
如果觉得《iOS利用UIView自定义播放器进度条》对你有帮助,请点赞、收藏,并留下你的观点哦!