iOS - 第三方库 SAVideoRangeSlider 的代码学习记录
来源:互联网 发布:java 字段 编辑:程序博客网 时间:2024/06/06 07:18
今天,工作当中用到了第三方代码SAVideoRangeSlider,现在特此记录一下学习的内容
整体来说这个代码还是比较简单的
这是结果:
下面是我对代码的注释理解
- (void)viewDidLoad{ [super viewDidLoad]; self.myActivityIndicator.hidden = YES; //临时路径 NSString *tempDir = NSTemporaryDirectory(); self.tmpVideoPath = [tempDir stringByAppendingPathComponent:@"tmpMov.mov"]; //加载本地mov NSBundle *mainBundle = [NSBundle mainBundle]; self.originalVideoPath = [mainBundle pathForResource: @"thaiPhuketKaronBeach" ofType: @"MOV"]; NSURL *videoFileUrl = [NSURL fileURLWithPath:self.originalVideoPath]; //创建SAVideoRangeSlider if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { self.mySAVideoRangeSlider = [[SAVideoRangeSlider alloc] initWithFrame:CGRectMake(10, 200, self.view.frame.size.width-20, 70) videoUrl:videoFileUrl ]; [self.mySAVideoRangeSlider setPopoverBubbleSize:200 height:100]; } else { self.mySAVideoRangeSlider = [[SAVideoRangeSlider alloc] initWithFrame:CGRectMake(10, 100, self.view.frame.size.width-20, 50) videoUrl:videoFileUrl ]; //设置字体大小,泡泡大小 self.mySAVideoRangeSlider.bubleText.font = [UIFont systemFontOfSize:12]; [self.mySAVideoRangeSlider setPopoverBubbleSize:120 height:60]; } // Yellow 设置颜色 self.mySAVideoRangeSlider.topBorder.backgroundColor = [UIColor colorWithRed: 0.996 green: 0.951 blue: 0.502 alpha: 1]; self.mySAVideoRangeSlider.bottomBorder.backgroundColor = [UIColor colorWithRed: 0.992 green: 0.902 blue: 0.004 alpha: 1]; //设置代理 self.mySAVideoRangeSlider.delegate = self; //加入视图 [self.view addSubview:self.mySAVideoRangeSlider];}- (void)didReceiveMemoryWarning{ [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}#pragma mark - IBAction//播放视频- (IBAction)showOriginalVideo:(id)sender { [self playMovie:self.originalVideoPath]; }//播放剪裁好的视频- (IBAction)showTrimmedVideo:(UIButton *)sender { //如果有 删除临时文件 [self deleteTmpFile]; //把文件地址转为url NSURL *videoFileUrl = [NSURL fileURLWithPath:self.originalVideoPath]; //转为AVAsset资源对象 AVAsset *anAsset = [[AVURLAsset alloc] initWithURL:videoFileUrl options:nil]; NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:anAsset]; if ([compatiblePresets containsObject:AVAssetExportPresetMediumQuality]) { //创建一个输出 self.exportSession = [[AVAssetExportSession alloc] initWithAsset:anAsset presetName:AVAssetExportPresetPassthrough]; // Implementation continues. //设置url以及类型 NSURL *furl = [NSURL fileURLWithPath:self.tmpVideoPath]; self.exportSession.outputURL = furl; self.exportSession.outputFileType = AVFileTypeQuickTimeMovie; //设置剪裁时间 CMTime start = CMTimeMakeWithSeconds(self.startTime, anAsset.duration.timescale); CMTime duration = CMTimeMakeWithSeconds(self.stopTime-self.startTime, anAsset.duration.timescale); CMTimeRange range = CMTimeRangeMake(start, duration); self.exportSession.timeRange = range; self.trimBtn.hidden = YES; self.myActivityIndicator.hidden = NO; [self.myActivityIndicator startAnimating]; [self.exportSession exportAsynchronouslyWithCompletionHandler:^{ switch ([self.exportSession status]) { case AVAssetExportSessionStatusFailed: NSLog(@"Export failed: %@", [[self.exportSession error] localizedDescription]); break; case AVAssetExportSessionStatusCancelled: NSLog(@"Export canceled"); break; default: NSLog(@"NONE"); dispatch_async(dispatch_get_main_queue(), ^{ [self.myActivityIndicator stopAnimating]; self.myActivityIndicator.hidden = YES; self.trimBtn.hidden = NO; [self playMovie:self.tmpVideoPath]; }); break; } }]; } }#pragma mark - Other//如果有文件 删除临时文件-(void)deleteTmpFile{ NSURL *url = [NSURL fileURLWithPath:self.tmpVideoPath]; NSFileManager *fm = [NSFileManager defaultManager]; BOOL exist = [fm fileExistsAtPath:url.path]; NSError *err; if (exist) { [fm removeItemAtURL:url error:&err]; NSLog(@"file deleted"); if (err) { NSLog(@"file remove error, %@", err.localizedDescription ); } } else { NSLog(@"no file by that name"); }}-(void)playMovie: (NSString *) path{ NSURL *url = [NSURL fileURLWithPath:path]; MPMoviePlayerViewController *theMovie = [[MPMoviePlayerViewController alloc] initWithContentURL:url]; [self presentMoviePlayerViewControllerAnimated:theMovie]; theMovie.moviePlayer.movieSourceType = MPMovieSourceTypeFile; [theMovie.moviePlayer play];}#pragma mark - SAVideoRangeSliderDelegate- (void)videoRange:(SAVideoRangeSlider *)videoRange didChangeLeftPosition:(CGFloat)leftPosition rightPosition:(CGFloat)rightPosition{ self.startTime = leftPosition; self.stopTime = rightPosition; self.timeLabel.text = [NSString stringWithFormat:@"%f - %f", leftPosition, rightPosition];}- (void)viewDidUnload { [self setMyActivityIndicator:nil]; [self setTrimBtn:nil]; [super viewDidUnload];}
在此贴出部分,剩余的都可以自己理解了
SAVideoRangeSlider.h
@protocol SAVideoRangeSliderDelegate;@interface SAVideoRangeSlider : UIView@property (nonatomic, weak) id <SAVideoRangeSliderDelegate> delegate; //代理@property (nonatomic) CGFloat leftPosition; //左边位置@property (nonatomic) CGFloat rightPosition; //右边位置@property (nonatomic, strong) UILabel *bubleText; //泡泡上显示的label@property (nonatomic, strong) UIView *topBorder; //顶部view@property (nonatomic, strong) UIView *bottomBorder; //底部view@property (nonatomic, assign) NSInteger maxGap;@property (nonatomic, assign) NSInteger minGap;//初始化方法- (id)initWithFrame:(CGRect)frame videoUrl:(NSURL *)videoUrl;//设置泡泡的大小- (void)setPopoverBubbleSize: (CGFloat) width height:(CGFloat)height;@end@protocol SAVideoRangeSliderDelegate <NSObject>@optional//显示时间的代理- (void)videoRange:(SAVideoRangeSlider *)videoRange didChangeLeftPosition:(CGFloat)leftPosition rightPosition:(CGFloat)rightPosition;//拖动结束后的代理- (void)videoRange:(SAVideoRangeSlider *)videoRange didGestureStateEndedLeftPosition:(CGFloat)leftPosition rightPosition:(CGFloat)rightPosition;@end
SAVideoRangeSlider.m
#import "SAVideoRangeSlider.h"@interface SAVideoRangeSlider ()@property (nonatomic, strong) AVAssetImageGenerator *imageGenerator;//获得Asset的图像缩略图,预览图@property (nonatomic, strong) UIView *bgView;@property (nonatomic, strong) UIView *centerView;@property (nonatomic, strong) NSURL *videoUrl;@property (nonatomic, strong) SASliderLeft *leftThumb;//左边框@property (nonatomic, strong) SASliderRight *rightThumb;//右边框@property (nonatomic) CGFloat frame_width;@property (nonatomic) Float64 durationSeconds;@property (nonatomic, strong) SAResizibleBubble *popoverBubble;//泡泡label@end@implementation SAVideoRangeSlider#define SLIDER_BORDERS_SIZE 6.0f#define BG_VIEW_BORDERS_SIZE 3.0f/**初始化方法@param frame slider的范围@param videoUrl 视频资源的url@returns 实例 */- (id)initWithFrame:(CGRect)frame videoUrl:(NSURL *)videoUrl{ self = [super initWithFrame:frame]; if (self) { _frame_width = frame.size.width; int thumbWidth = ceil(frame.size.width*0.05);//返回大于或者等于指定表达式的最小整数 //创建背景框 _bgView = [[UIControl alloc] initWithFrame:CGRectMake(thumbWidth-BG_VIEW_BORDERS_SIZE, 0, frame.size.width-(thumbWidth*2)+BG_VIEW_BORDERS_SIZE*2, frame.size.height)]; _bgView.layer.borderColor = [UIColor grayColor].CGColor; _bgView.layer.borderWidth = BG_VIEW_BORDERS_SIZE; [self addSubview:_bgView]; _videoUrl = videoUrl; _topBorder = [[UIView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, SLIDER_BORDERS_SIZE)]; _topBorder.backgroundColor = [UIColor colorWithRed: 0.996 green: 0.951 blue: 0.502 alpha: 1]; [self addSubview:_topBorder]; _bottomBorder = [[UIView alloc] initWithFrame:CGRectMake(0, frame.size.height-SLIDER_BORDERS_SIZE, frame.size.width, SLIDER_BORDERS_SIZE)]; _bottomBorder.backgroundColor = [UIColor colorWithRed: 0.992 green: 0.902 blue: 0.004 alpha: 1]; [self addSubview:_bottomBorder]; //创建一个左边拖动的View,解决按不到的问题,thumbWidth+10 _leftThumb = [[SASliderLeft alloc] initWithFrame:CGRectMake(0, 0, thumbWidth+10, frame.size.height)]; _leftThumb.contentMode = UIViewContentModeLeft; _leftThumb.userInteractionEnabled = YES; _leftThumb.clipsToBounds = YES; _leftThumb.backgroundColor = [UIColor clearColor]; _leftThumb.layer.borderWidth = 0; [self addSubview:_leftThumb]; //添加一个拖动手势 UIPanGestureRecognizer *leftPan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleLeftPan:)]; [_leftThumb addGestureRecognizer:leftPan]; //创建一个右边拖动的View,解决按不到的问题,thumbWidth+10 _rightThumb = [[SASliderRight alloc] initWithFrame:CGRectMake(0, 0, thumbWidth+10, frame.size.height)]; _rightThumb.contentMode = UIViewContentModeRight; _rightThumb.userInteractionEnabled = YES; _rightThumb.clipsToBounds = YES; _rightThumb.backgroundColor = [UIColor clearColor]; [self addSubview:_rightThumb]; UIPanGestureRecognizer *rightPan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleRightPan:)]; [_rightThumb addGestureRecognizer:rightPan]; //初始化左右的位置 _rightPosition = frame.size.width; _leftPosition = 0; //创建中心的view,加入手势 _centerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)]; _centerView.backgroundColor = [UIColor clearColor]; [self addSubview:_centerView]; //加入手势 UIPanGestureRecognizer *centerPan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleCenterPan:)]; [_centerView addGestureRecognizer:centerPan]; //创建泡泡label _popoverBubble = [[SAResizibleBubble alloc] initWithFrame:CGRectMake(0, -50, 100, 50)]; _popoverBubble.alpha = 0; _popoverBubble.backgroundColor = [UIColor clearColor]; [self addSubview:_popoverBubble]; _bubleText = [[UILabel alloc] initWithFrame:_popoverBubble.frame]; _bubleText.font = [UIFont boldSystemFontOfSize:20]; _bubleText.backgroundColor = [UIColor clearColor]; _bubleText.textColor = [UIColor blackColor]; _bubleText.textAlignment = UITextAlignmentCenter; [_popoverBubble addSubview:_bubleText]; [self getMovieFrame]; [self setNeedsDisplay]; } return self;}- (id)initWithFrame:(CGRect)frame{ self = [super initWithFrame:frame]; if (self) { // Initialization code } return self;}//设置泡泡的大小-(void)setPopoverBubbleSize: (CGFloat) width height:(CGFloat)height{ CGRect currentFrame = _popoverBubble.frame; currentFrame.size.width = width; currentFrame.size.height = height; currentFrame.origin.y = -height; _popoverBubble.frame = currentFrame; currentFrame.origin.x = 0; currentFrame.origin.y = 0; _bubleText.frame = currentFrame; }//设置最大剪裁时间-(void)setMaxGap:(NSInteger)maxGap{ _leftPosition = 0; _rightPosition = _frame_width*maxGap/_durationSeconds; _maxGap = maxGap;}//设置最小剪裁时间-(void)setMinGap:(NSInteger)minGap{ _leftPosition = 0; _rightPosition = _frame_width*minGap/_durationSeconds; _minGap = minGap;}//通知界面刷新截选得时间- (void)delegateNotification{ if ([_delegate respondsToSelector:@selector(videoRange:didChangeLeftPosition:rightPosition:)]){ [_delegate videoRange:self didChangeLeftPosition:self.leftPosition rightPosition:self.rightPosition]; } }#pragma mark - Gestures- (void)handleLeftPan:(UIPanGestureRecognizer *)gesture{ if (gesture.state == UIGestureRecognizerStateBegan || gesture.state == UIGestureRecognizerStateChanged) { CGPoint translation = [gesture translationInView:self];//返回偏移量 _leftPosition += translation.x; if (_leftPosition < 0) { _leftPosition = 0; } //判断可否移动 if ( (_rightPosition-_leftPosition <= _leftThumb.frame.size.width+_rightThumb.frame.size.width) || ((self.maxGap > 0) && (self.rightPosition-self.leftPosition > self.maxGap)) || ((self.minGap > 0) && (self.rightPosition-self.leftPosition < self.minGap)) ){ _leftPosition -= translation.x; } // 注意一旦你完成上述的移动,将translation重置为0十分重要。否则translation每次都会叠加,很快你的view就会移除屏幕! [gesture setTranslation:CGPointZero inView:self]; [self setNeedsLayout]; [self delegateNotification]; } _popoverBubble.alpha = 1; [self setTimeLabel]; if (gesture.state == UIGestureRecognizerStateEnded){ [self hideBubble:_popoverBubble]; }}- (void)handleRightPan:(UIPanGestureRecognizer *)gesture{ if (gesture.state == UIGestureRecognizerStateBegan || gesture.state == UIGestureRecognizerStateChanged) { CGPoint translation = [gesture translationInView:self]; _rightPosition += translation.x; if (_rightPosition < 0) { _rightPosition = 0; } if (_rightPosition > _frame_width){ _rightPosition = _frame_width; } if (_rightPosition-_leftPosition <= 0){ _rightPosition -= translation.x; } if ((_rightPosition-_leftPosition <= _leftThumb.frame.size.width+_rightThumb.frame.size.width) || ((self.maxGap > 0) && (self.rightPosition-self.leftPosition > self.maxGap)) || ((self.minGap > 0) && (self.rightPosition-self.leftPosition < self.minGap))){ _rightPosition -= translation.x; } [gesture setTranslation:CGPointZero inView:self]; [self setNeedsLayout]; [self delegateNotification]; } _popoverBubble.alpha = 1; [self setTimeLabel]; if (gesture.state == UIGestureRecognizerStateEnded){ [self hideBubble:_popoverBubble]; }}- (void)handleCenterPan:(UIPanGestureRecognizer *)gesture{ if (gesture.state == UIGestureRecognizerStateBegan || gesture.state == UIGestureRecognizerStateChanged) { CGPoint translation = [gesture translationInView:self]; _leftPosition += translation.x; _rightPosition += translation.x; if (_rightPosition > _frame_width || _leftPosition < 0){ _leftPosition -= translation.x; _rightPosition -= translation.x; } [gesture setTranslation:CGPointZero inView:self]; [self setNeedsLayout]; [self delegateNotification]; } _popoverBubble.alpha = 1; [self setTimeLabel]; if (gesture.state == UIGestureRecognizerStateEnded){ [self hideBubble:_popoverBubble]; } }//重绘整个截选框的位置- (void)layoutSubviews{ CGFloat inset = _leftThumb.frame.size.width / 2; _leftThumb.center = CGPointMake(_leftPosition+inset, _leftThumb.frame.size.height/2); _rightThumb.center = CGPointMake(_rightPosition-inset, _rightThumb.frame.size.height/2); _topBorder.frame = CGRectMake(_leftThumb.frame.origin.x + _leftThumb.frame.size.width, 0, _rightThumb.frame.origin.x - _leftThumb.frame.origin.x - _leftThumb.frame.size.width/2, SLIDER_BORDERS_SIZE); _bottomBorder.frame = CGRectMake(_leftThumb.frame.origin.x + _leftThumb.frame.size.width, _bgView.frame.size.height-SLIDER_BORDERS_SIZE, _rightThumb.frame.origin.x - _leftThumb.frame.origin.x - _leftThumb.frame.size.width/2, SLIDER_BORDERS_SIZE); _centerView.frame = CGRectMake(_leftThumb.frame.origin.x + _leftThumb.frame.size.width, _centerView.frame.origin.y, _rightThumb.frame.origin.x - _leftThumb.frame.origin.x - _leftThumb.frame.size.width, _centerView.frame.size.height); CGRect frame = _popoverBubble.frame; frame.origin.x = _centerView.frame.origin.x+_centerView.frame.size.width/2-frame.size.width/2; _popoverBubble.frame = frame;}#pragma mark - Video-(void)getMovieFrame{ //得到url的资源,转为asset AVAsset *myAsset = [[AVURLAsset alloc] initWithURL:_videoUrl options:nil]; //初始化AVAssetImageGenerator self.imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:myAsset]; //定义最大尺寸 if ([self isRetina]){ self.imageGenerator.maximumSize = CGSizeMake(_bgView.frame.size.width*2, _bgView.frame.size.height*2); } else { self.imageGenerator.maximumSize = CGSizeMake(_bgView.frame.size.width, _bgView.frame.size.height); } int picWidth = 49; // First image //创建第一张预览图 NSError *error; CMTime actualTime; CGImageRef halfWayImage = [self.imageGenerator copyCGImageAtTime:kCMTimeZero actualTime:&actualTime error:&error]; if (halfWayImage != NULL) { UIImage *videoScreen; if ([self isRetina]){ videoScreen = [[UIImage alloc] initWithCGImage:halfWayImage scale:2.0 orientation:UIImageOrientationUp]; } else { videoScreen = [[UIImage alloc] initWithCGImage:halfWayImage]; } UIImageView *tmp = [[UIImageView alloc] initWithImage:videoScreen]; [_bgView addSubview:tmp]; //得到预览图的标准宽 picWidth = tmp.frame.size.width; CGImageRelease(halfWayImage); } //得到秒数 _durationSeconds = CMTimeGetSeconds([myAsset duration]); //通过view的宽得到预览图的张数 int picsCnt = ceil(_bgView.frame.size.width / picWidth); NSLog(@"%d",picsCnt); //创建一个可变数组 NSMutableArray *allTimes = [[NSMutableArray alloc] init]; int time4Pic = 0; //得到每张预览图应该有的CMTime for (int i=1; i<picsCnt; i++){ time4Pic = i*picWidth; CMTime timeFrame = CMTimeMakeWithSeconds(_durationSeconds/_bgView.frame.size.width*time4Pic, 600); [allTimes addObject:[NSValue valueWithCMTime:timeFrame]]; } NSArray *times = allTimes; __block int i = 1; //根据CMTime 取出image [self.imageGenerator generateCGImagesAsynchronouslyForTimes:times completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error) { if (result == AVAssetImageGeneratorSucceeded) { UIImage *videoScreen; if ([self isRetina]){ videoScreen = [[UIImage alloc] initWithCGImage:image scale:2.0 orientation:UIImageOrientationUp]; } else { videoScreen = [[UIImage alloc] initWithCGImage:image]; } //这里修改了预览图加载慢的问题,放到主线程加载 dispatch_async(dispatch_get_main_queue(), ^{ UIImageView *tmp = [[UIImageView alloc] initWithImage:videoScreen]; int all = (i+1)*tmp.frame.size.width; CGRect currentFrame = tmp.frame; currentFrame.origin.x = i*currentFrame.size.width; //如果最后一张图超出范围,缩小 if (all > _bgView.frame.size.width){ int delta = all - _bgView.frame.size.width; currentFrame.size.width -= delta; } tmp.frame = currentFrame; i++; [_bgView addSubview:tmp]; }); } if (result == AVAssetImageGeneratorFailed) { NSLog(@"Failed with error: %@", [error localizedDescription]); } if (result == AVAssetImageGeneratorCancelled) { NSLog(@"Canceled"); } }];}#pragma mark - Properties//返回左边时间范围- (CGFloat)leftPosition{ return _leftPosition * _durationSeconds / _frame_width;}//返回右边时间范围- (CGFloat)rightPosition{ return _rightPosition * _durationSeconds / _frame_width;}#pragma mark - Bubble//隐藏泡泡的方法- (void)hideBubble:(UIView *)popover{ [UIView animateWithDuration:0.4 delay:0 options:UIViewAnimationCurveEaseIn | UIViewAnimationOptionAllowUserInteraction animations:^(void) { _popoverBubble.alpha = 1.0;//取消泡泡label的消失 } completion:nil]; //通过代理显示在界面上label的时间位置 if ([_delegate respondsToSelector:@selector(videoRange:didGestureStateEndedLeftPosition:rightPosition:)]){ [_delegate videoRange:self didGestureStateEndedLeftPosition:self.leftPosition rightPosition:self.rightPosition]; }}//刷新label的时间方法-(void) setTimeLabel{ self.bubleText.text = [self trimIntervalStr];}//显示截取时间的秒数-(NSString *)trimDurationStr{ int delta = floor(self.rightPosition - self.leftPosition); return [NSString stringWithFormat:@"%d", delta];}//获得时间-(NSString *)trimIntervalStr{ NSString *from = [self timeToStr:self.leftPosition]; NSString *to = [self timeToStr:self.rightPosition]; return [NSString stringWithFormat:@"%@ - %@", from, to];}#pragma mark - Helpers//获得时间- (NSString *)timeToStr:(CGFloat)time{ // time - seconds NSInteger min = floor(time / 60); NSInteger sec = floor(time - min * 60); NSString *minStr = [NSString stringWithFormat:min >= 10 ? @"%i" : @"0%i", min]; NSString *secStr = [NSString stringWithFormat:sec >= 10 ? @"%i" : @"0%i", sec]; return [NSString stringWithFormat:@"%@:%@", minStr, secStr];}//是否是高清屏-(BOOL)isRetina{ return ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0));}@end
SAResizibleBubble.m
#import "SAResizibleBubble.h"@implementation SAResizibleBubble- (id)initWithFrame:(CGRect)frame{ self = [super initWithFrame:frame]; if (self) { // Initialization code } return self;}- (void)drawRect:(CGRect)rect{ //// General Declarations //得到颜色空间 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = UIGraphicsGetCurrentContext(); //// Color Declarations //泡泡上面的颜色 UIColor* bubbleGradientTop = [UIColor colorWithRed: 1 green: 0.939 blue: 0.743 alpha: 1]; //泡泡底部的颜色 UIColor* bubbleGradientBottom = [UIColor colorWithRed: 1 green: 0.817 blue: 0.053 alpha: 1]; //高亮的颜色,背景阴影 UIColor* bubbleHighlightColor = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1]; //画笔颜色,一圈阴影 UIColor* bubbleStrokeColor = [UIColor colorWithRed: 0.173 green: 0.173 blue: 0.173 alpha: 1]; //// Gradient Declarations NSArray* bubbleGradientColors = [NSArray arrayWithObjects: (id)bubbleGradientTop.CGColor, (id)bubbleGradientBottom.CGColor, nil]; //泡泡渐变的位置 CGFloat bubbleGradientLocations[] = {0, 1}; //创建一个渐变对象 CGGradientRef bubbleGradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)bubbleGradientColors, bubbleGradientLocations); //// Shadow Declarations UIColor* outerShadow = [UIColor blackColor]; CGSize outerShadowOffset = CGSizeMake(0.1, 6.1); CGFloat outerShadowBlurRadius = 13; UIColor* highlightShadow = bubbleHighlightColor; CGSize highlightShadowOffset = CGSizeMake(0.1, 2.1); CGFloat highlightShadowBlurRadius = 0; //// Frames CGRect bubbleFrame = self.bounds; //// Subframes //箭头frame CGRect arrowFrame = CGRectMake(CGRectGetMinX(bubbleFrame) + floor((CGRectGetWidth(bubbleFrame) - 59) * 0.50462 + 0.5), CGRectGetMinY(bubbleFrame) + CGRectGetHeight(bubbleFrame) - 46, 59, 46); //// Bubble Drawing //画泡泡 UIBezierPath* bubblePath = [UIBezierPath bezierPath]; [bubblePath moveToPoint: CGPointMake(CGRectGetMaxX(bubbleFrame) - 12, CGRectGetMinY(bubbleFrame) + 28.5)]; [bubblePath addLineToPoint: CGPointMake(CGRectGetMaxX(bubbleFrame) - 12, CGRectGetMaxY(bubbleFrame) - 27.5)]; [bubblePath addCurveToPoint: CGPointMake(CGRectGetMaxX(bubbleFrame) - 25, CGRectGetMaxY(bubbleFrame) - 14.5) controlPoint1: CGPointMake(CGRectGetMaxX(bubbleFrame) - 12, CGRectGetMaxY(bubbleFrame) - 20.32) controlPoint2: CGPointMake(CGRectGetMaxX(bubbleFrame) - 17.82, CGRectGetMaxY(bubbleFrame) - 14.5)]; [bubblePath addLineToPoint: CGPointMake(CGRectGetMinX(arrowFrame) + 40.5, CGRectGetMaxY(arrowFrame) - 13.5)]; [bubblePath addLineToPoint: CGPointMake(CGRectGetMinX(arrowFrame) + 29.5, CGRectGetMaxY(arrowFrame) - 0.5)]; [bubblePath addLineToPoint: CGPointMake(CGRectGetMinX(arrowFrame) + 18.5, CGRectGetMaxY(arrowFrame) - 13.5)]; [bubblePath addLineToPoint: CGPointMake(CGRectGetMinX(bubbleFrame) + 26.5, CGRectGetMaxY(bubbleFrame) - 14.5)]; [bubblePath addCurveToPoint: CGPointMake(CGRectGetMinX(bubbleFrame) + 13.5, CGRectGetMaxY(bubbleFrame) - 27.5) controlPoint1: CGPointMake(CGRectGetMinX(bubbleFrame) + 19.32, CGRectGetMaxY(bubbleFrame) - 14.5) controlPoint2: CGPointMake(CGRectGetMinX(bubbleFrame) + 13.5, CGRectGetMaxY(bubbleFrame) - 20.32)]; [bubblePath addLineToPoint: CGPointMake(CGRectGetMinX(bubbleFrame) + 13.5, CGRectGetMinY(bubbleFrame) + 28.5)]; [bubblePath addCurveToPoint: CGPointMake(CGRectGetMinX(bubbleFrame) + 26.5, CGRectGetMinY(bubbleFrame) + 15.5) controlPoint1: CGPointMake(CGRectGetMinX(bubbleFrame) + 13.5, CGRectGetMinY(bubbleFrame) + 21.32) controlPoint2: CGPointMake(CGRectGetMinX(bubbleFrame) + 19.32, CGRectGetMinY(bubbleFrame) + 15.5)]; [bubblePath addLineToPoint: CGPointMake(CGRectGetMaxX(bubbleFrame) - 25, CGRectGetMinY(bubbleFrame) + 15.5)]; [bubblePath addCurveToPoint: CGPointMake(CGRectGetMaxX(bubbleFrame) - 12, CGRectGetMinY(bubbleFrame) + 28.5) controlPoint1: CGPointMake(CGRectGetMaxX(bubbleFrame) - 17.82, CGRectGetMinY(bubbleFrame) + 15.5) controlPoint2: CGPointMake(CGRectGetMaxX(bubbleFrame) - 12, CGRectGetMinY(bubbleFrame) + 21.32)]; [bubblePath closePath]; CGContextSaveGState(context); CGContextSetShadowWithColor(context, outerShadowOffset, outerShadowBlurRadius, outerShadow.CGColor); CGContextBeginTransparencyLayer(context, NULL); [bubblePath addClip]; CGRect bubbleBounds = CGPathGetPathBoundingBox(bubblePath.CGPath); CGContextDrawLinearGradient(context, bubbleGradient, CGPointMake(CGRectGetMidX(bubbleBounds), CGRectGetMinY(bubbleBounds)), CGPointMake(CGRectGetMidX(bubbleBounds), CGRectGetMaxY(bubbleBounds)), 0); CGContextEndTransparencyLayer(context); ////// Bubble Inner Shadow CGRect bubbleBorderRect = CGRectInset([bubblePath bounds], -highlightShadowBlurRadius, -highlightShadowBlurRadius); bubbleBorderRect = CGRectOffset(bubbleBorderRect, -highlightShadowOffset.width, -highlightShadowOffset.height); bubbleBorderRect = CGRectInset(CGRectUnion(bubbleBorderRect, [bubblePath bounds]), -1, -1); UIBezierPath* bubbleNegativePath = [UIBezierPath bezierPathWithRect: bubbleBorderRect]; [bubbleNegativePath appendPath: bubblePath]; bubbleNegativePath.usesEvenOddFillRule = YES; CGContextSaveGState(context); { CGFloat xOffset = highlightShadowOffset.width + round(bubbleBorderRect.size.width); CGFloat yOffset = highlightShadowOffset.height; CGContextSetShadowWithColor(context, CGSizeMake(xOffset + copysign(0.1, xOffset), yOffset + copysign(0.1, yOffset)), highlightShadowBlurRadius, highlightShadow.CGColor); [bubblePath addClip]; CGAffineTransform transform = CGAffineTransformMakeTranslation(-round(bubbleBorderRect.size.width), 0); [bubbleNegativePath applyTransform: transform]; [[UIColor grayColor] setFill]; [bubbleNegativePath fill]; } CGContextRestoreGState(context); CGContextRestoreGState(context); [bubbleStrokeColor setStroke]; bubblePath.lineWidth = 1; [bubblePath stroke]; //// Cleanup CGGradientRelease(bubbleGradient); CGColorSpaceRelease(colorSpace); }@end
参考文章:UIBezierPath贝塞尔弧线常用方法记 UIBezierPath的基本使用 UIBezierPath类 CMTimeMake和CMTimeMakeWithSeconds 详解
0 0
- iOS - 第三方库 SAVideoRangeSlider 的代码学习记录
- iOS - 第三方库 SAVideoRangeSlider 的代码学习记录
- iOS 第三方控件GIFView的学习记录
- iOS第三方库的填坑记录
- iOS开发之第三方库学习
- 第三方库RATreeView的使用记录
- 常用的第三方库记录
- IOS第三方代码收集
- IOS第三方库
- iOS第三方库
- IOS 第三方库
- ios 第三方库
- iOS 第三方库
- iOS 第三方库
- IOS 第三方库
- IOS第三方库
- ios 第三方库
- iOS 第三方库
- Netty4 1_netty入门
- 交换机环路问题一例
- 黑马程序员_7k面试题之交通灯管理系统
- Version-levelDB源码解析
- 单片机汇编指令中RETI和RET的区别
- iOS - 第三方库 SAVideoRangeSlider 的代码学习记录
- VersionEdit-levelDB源码解析
- windows下的makefile教程
- 计算机视觉与模式识别 code
- 常用git操作
- 2013 Asia Regional Contest C题 解题报告
- Skip List
- xargs应用举例
- 极限优化:Haar特征的另一种的快速计算方法—boxfilter