今天,工作当中用到了第三方代码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 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 详解