// // ZFSliderView.m // ZFPlayer // // Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng ) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import "ZFSliderView.h" #import "UIView+ZFFrame.h" /** 滑块的大小 */ static const CGFloat kSliderBtnWH = 19.0; /** 进度的高度 */ static const CGFloat kProgressH = 1.0; /** 拖动slider动画的时间*/ static const CGFloat kAnimate = 0.3; @implementation ZFSliderButton // 重写此方法将按钮的点击范围扩大 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { CGRect bounds = self.bounds; // 扩大点击区域 bounds = CGRectInset(bounds, -20, -20); // 若点击的点在新的bounds里面。就返回yes return CGRectContainsPoint(bounds, point); } @end @interface ZFSliderView () /** 进度背景 */ @property (nonatomic, strong) UIImageView *bgProgressView; /** 缓存进度 */ @property (nonatomic, strong) UIImageView *bufferProgressView; /** 滑动进度 */ @property (nonatomic, strong) UIImageView *sliderProgressView; /** 滑块 */ @property (nonatomic, strong) ZFSliderButton *sliderBtn; @property (nonatomic, strong) UIView *loadingBarView; @property (nonatomic, assign) BOOL isLoading; @property (nonatomic, strong) UITapGestureRecognizer *tapGesture; @end @implementation ZFSliderView - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.allowTapped = YES; self.animate = YES; [self addSubViews]; } return self; } - (void)awakeFromNib { [super awakeFromNib]; self.allowTapped = YES; self.animate = YES; [self addSubViews]; } - (void)layoutSubviews { [super layoutSubviews]; if (isnan(self.value) || isnan(self.bufferValue)) return; CGFloat min_x = 0; CGFloat min_y = 0; CGFloat min_w = 0; CGFloat min_h = 0; CGFloat min_view_w = self.bounds.size.width; CGFloat min_view_h = self.bounds.size.height; min_x = 0; min_w = min_view_w; min_y = 0; min_h = self.sliderHeight; self.bgProgressView.frame = CGRectMake(min_x, min_y, min_w, min_h); min_x = 0; min_y = 0; min_w = self.thumbSize.width; min_h = self.thumbSize.height; self.sliderBtn.frame = CGRectMake(min_x, min_y, min_w, min_h); self.sliderBtn.zf_centerX = self.bgProgressView.zf_width * self.value; min_x = 0; min_y = 0; if (self.sliderBtn.hidden) { min_w = self.bgProgressView.zf_width * self.value; } else { min_w = self.sliderBtn.zf_centerX; } min_h = self.sliderHeight; self.sliderProgressView.frame = CGRectMake(min_x, min_y, min_w, min_h); min_x = 0; min_y = 0; min_w = self.bgProgressView.zf_width * self.bufferValue; min_h = self.sliderHeight; self.bufferProgressView.frame = CGRectMake(min_x, min_y, min_w, min_h); min_w = 0.1; min_h = self.sliderHeight; min_x = (min_view_w - min_w)/2; min_y = (min_view_h - min_h)/2; self.loadingBarView.frame = CGRectMake(min_x, min_y, min_w, min_h); self.bgProgressView.zf_centerY = min_view_h * 0.5; self.bufferProgressView.zf_centerY = min_view_h * 0.5; self.sliderProgressView.zf_centerY = min_view_h * 0.5; self.sliderBtn.zf_centerY = min_view_h * 0.5; } /** 添加子视图 */ - (void)addSubViews { self.thumbSize = CGSizeMake(kSliderBtnWH, kSliderBtnWH); self.sliderHeight = kProgressH; self.backgroundColor = [UIColor clearColor]; [self addSubview:self.bgProgressView]; [self addSubview:self.bufferProgressView]; [self addSubview:self.sliderProgressView]; [self addSubview:self.sliderBtn]; [self addSubview:self.loadingBarView]; // 添加点击手势 self.tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)]; [self addGestureRecognizer:self.tapGesture]; // 添加滑动手势 UIPanGestureRecognizer *sliderGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(sliderGesture:)]; [self addGestureRecognizer:sliderGesture]; } #pragma mark - Setter - (void)setMaximumTrackTintColor:(UIColor *)maximumTrackTintColor { _maximumTrackTintColor = maximumTrackTintColor; self.bgProgressView.backgroundColor = maximumTrackTintColor; } - (void)setMinimumTrackTintColor:(UIColor *)minimumTrackTintColor { _minimumTrackTintColor = minimumTrackTintColor; self.sliderProgressView.backgroundColor = minimumTrackTintColor; } - (void)setBufferTrackTintColor:(UIColor *)bufferTrackTintColor { _bufferTrackTintColor = bufferTrackTintColor; self.bufferProgressView.backgroundColor = bufferTrackTintColor; } - (void)setLoadingTintColor:(UIColor *)loadingTintColor { _loadingTintColor = loadingTintColor; self.loadingBarView.backgroundColor = loadingTintColor; } - (void)setMaximumTrackImage:(UIImage *)maximumTrackImage { _maximumTrackImage = maximumTrackImage; self.bgProgressView.image = maximumTrackImage; self.maximumTrackTintColor = [UIColor clearColor]; } - (void)setMinimumTrackImage:(UIImage *)minimumTrackImage { _minimumTrackImage = minimumTrackImage; self.sliderProgressView.image = minimumTrackImage; self.minimumTrackTintColor = [UIColor clearColor]; } - (void)setBufferTrackImage:(UIImage *)bufferTrackImage { _bufferTrackImage = bufferTrackImage; self.bufferProgressView.image = bufferTrackImage; self.bufferTrackTintColor = [UIColor clearColor]; } - (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state { [self.sliderBtn setBackgroundImage:image forState:state]; } - (void)setThumbImage:(UIImage *)image forState:(UIControlState)state { [self.sliderBtn setImage:image forState:state]; } - (void)setValue:(float)value { if (isnan(value)) return; value = MIN(1.0, value); _value = value; if (self.sliderBtn.hidden) { self.sliderProgressView.zf_width = self.bgProgressView.zf_width * value; } else { self.sliderBtn.zf_centerX = self.bgProgressView.zf_width * value; self.sliderProgressView.zf_width = self.sliderBtn.zf_centerX; } } - (void)setBufferValue:(float)bufferValue { if (isnan(bufferValue)) return; bufferValue = MIN(1.0, bufferValue); _bufferValue = bufferValue; self.bufferProgressView.zf_width = self.bgProgressView.zf_width * bufferValue; } - (void)setAllowTapped:(BOOL)allowTapped { _allowTapped = allowTapped; if (!allowTapped) { [self removeGestureRecognizer:self.tapGesture]; } } - (void)setSliderHeight:(CGFloat)sliderHeight { if (isnan(sliderHeight)) return; _sliderHeight = sliderHeight; self.bgProgressView.zf_height = sliderHeight; self.bufferProgressView.zf_height = sliderHeight; self.sliderProgressView.zf_height = sliderHeight; } - (void)setSliderRadius:(CGFloat)sliderRadius { if (isnan(sliderRadius)) return; _sliderRadius = sliderRadius; self.bgProgressView.layer.cornerRadius = sliderRadius; self.bufferProgressView.layer.cornerRadius = sliderRadius; self.sliderProgressView.layer.cornerRadius = sliderRadius; self.bgProgressView.layer.masksToBounds = YES; self.bufferProgressView.layer.masksToBounds = YES; self.sliderProgressView.layer.masksToBounds = YES; } - (void)setIsHideSliderBlock:(BOOL)isHideSliderBlock { _isHideSliderBlock = isHideSliderBlock; // 隐藏滑块,滑杆不可点击 if (isHideSliderBlock) { self.sliderBtn.hidden = YES; self.bgProgressView.zf_left = 0; self.bufferProgressView.zf_left = 0; self.sliderProgressView.zf_left = 0; self.allowTapped = NO; } } /** * Starts animation of the spinner. */ - (void)startAnimating { if (self.isLoading) return; self.isLoading = YES; self.bufferProgressView.hidden = YES; self.sliderProgressView.hidden = YES; self.sliderBtn.hidden = YES; self.loadingBarView.hidden = NO; [self.loadingBarView.layer removeAllAnimations]; CAAnimationGroup *animationGroup = [[CAAnimationGroup alloc] init]; animationGroup.duration = 0.4; animationGroup.beginTime = CACurrentMediaTime() + 0.4; animationGroup.repeatCount = MAXFLOAT; animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; CABasicAnimation *scaleAnimation = [CABasicAnimation animation]; scaleAnimation.keyPath = @"transform.scale.x"; scaleAnimation.fromValue = @(1000.0f); scaleAnimation.toValue = @(self.zf_width * 10); CABasicAnimation *alphaAnimation = [CABasicAnimation animation]; alphaAnimation.keyPath = @"opacity"; alphaAnimation.fromValue = @(1.0f); alphaAnimation.toValue = @(0.0f); [animationGroup setAnimations:@[scaleAnimation, alphaAnimation]]; [self.loadingBarView.layer addAnimation:animationGroup forKey:@"loading"]; } /** * Stops animation of the spinnner. */ - (void)stopAnimating { self.isLoading = NO; self.bufferProgressView.hidden = NO; self.sliderProgressView.hidden = NO; self.sliderBtn.hidden = self.isHideSliderBlock; self.loadingBarView.hidden = YES; [self.loadingBarView.layer removeAllAnimations]; } #pragma mark - User Action - (void)sliderGesture:(UIGestureRecognizer *)gesture { switch (gesture.state) { case UIGestureRecognizerStateBegan: { [self sliderBtnTouchBegin:self.sliderBtn]; } break; case UIGestureRecognizerStateChanged: { [self sliderBtnDragMoving:self.sliderBtn point:[gesture locationInView:self.bgProgressView]]; } break; case UIGestureRecognizerStateEnded: { [self sliderBtnTouchEnded:self.sliderBtn]; } break; default: break; } } - (void)sliderBtnTouchBegin:(UIButton *)btn { if ([self.delegate respondsToSelector:@selector(sliderTouchBegan:)]) { [self.delegate sliderTouchBegan:self.value]; } if (self.animate) { [UIView animateWithDuration:kAnimate animations:^{ btn.transform = CGAffineTransformMakeScale(1.2, 1.2); }]; } } - (void)sliderBtnTouchEnded:(UIButton *)btn { if ([self.delegate respondsToSelector:@selector(sliderTouchEnded:)]) { [self.delegate sliderTouchEnded:self.value]; } if (self.animate) { [UIView animateWithDuration:kAnimate animations:^{ btn.transform = CGAffineTransformIdentity; }]; } } - (void)sliderBtnDragMoving:(UIButton *)btn point:(CGPoint)touchPoint { // 点击的位置 CGPoint point = touchPoint; // 获取进度值 由于btn是从 0-(self.width - btn.width) CGFloat value = (point.x - btn.zf_width * 0.5) / self.bgProgressView.zf_width; // value的值需在0-1之间 value = value >= 1.0 ? 1.0 : value <= 0.0 ? 0.0 : value; if (self.value == value) return; self.isForward = self.value < value; self.value = value; if ([self.delegate respondsToSelector:@selector(sliderValueChanged:)]) { [self.delegate sliderValueChanged:value]; } } - (void)tapped:(UITapGestureRecognizer *)tap { CGPoint point = [tap locationInView:self.bgProgressView]; // 获取进度 CGFloat value = (point.x - self.sliderBtn.zf_width * 0.5) * 1.0 / self.bgProgressView.zf_width; value = value >= 1.0 ? 1.0 : value <= 0 ? 0 : value; self.value = value; if ([self.delegate respondsToSelector:@selector(sliderTapped:)]) { [self.delegate sliderTapped:value]; } } #pragma mark - getter - (UIView *)bgProgressView { if (!_bgProgressView) { _bgProgressView = [UIImageView new]; _bgProgressView.backgroundColor = [UIColor grayColor]; _bgProgressView.contentMode = UIViewContentModeScaleAspectFill; _bgProgressView.clipsToBounds = YES; } return _bgProgressView; } - (UIView *)bufferProgressView { if (!_bufferProgressView) { _bufferProgressView = [UIImageView new]; _bufferProgressView.backgroundColor = [UIColor whiteColor]; _bufferProgressView.contentMode = UIViewContentModeScaleAspectFill; _bufferProgressView.clipsToBounds = YES; } return _bufferProgressView; } - (UIView *)sliderProgressView { if (!_sliderProgressView) { _sliderProgressView = [UIImageView new]; _sliderProgressView.backgroundColor = [UIColor redColor]; _sliderProgressView.contentMode = UIViewContentModeScaleAspectFill; _sliderProgressView.clipsToBounds = YES; } return _sliderProgressView; } - (ZFSliderButton *)sliderBtn { if (!_sliderBtn) { _sliderBtn = [ZFSliderButton buttonWithType:UIButtonTypeCustom]; [_sliderBtn setAdjustsImageWhenHighlighted:NO]; } return _sliderBtn; } - (UIView *)loadingBarView { if (!_loadingBarView) { _loadingBarView = [[UIView alloc] init]; _loadingBarView.backgroundColor = [UIColor whiteColor]; _loadingBarView.hidden = YES; } return _loadingBarView; } @end