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];}


下面是其内部代码,我修改了其中的一个bug

在此贴出部分,剩余的都可以自己理解了

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