多媒体和断点续传
来源:互联网 发布:怎么给淘宝刷好评赚钱 编辑:程序博客网 时间:2024/06/16 12:43
1. 根TabBar
RootViewController.m
// 1. 系统相册/拍照// 2. 音频播放// 3. 视须播放// 4. 断点续传[@interface RootViewController : UITabBarController]#import "RootViewController.h"#import "PhotoViewController.h"#import "AudioViewController.h"#import "VideoViewController.h"#import "ResumeDownloadController.h"//给UIViewController做一个扩展方便创建视图控制器//作用:让它具有TabBarItem@interface UIViewController (item)//如果item不加,括号里面的内容为空,表示这是一个匿名类别,匿名类别中加的东西只是为了表示是私有的,而扩展不一样,扩展是让原来的类具有一些额外的功能+ (UIViewController *)viewControllerWithTitle:(NSString *)title image:(UIImage *)image selectedImage:(UIImage *)selectedImage;@end@implementation UIViewController (item)+ (UIViewController *)viewControllerWithTitle:(NSString *)title image:(UIImage *)image selectedImage:(UIImage *)selectedImage{ UITabBarItem *item = [[UITabBarItem alloc] initWithTitle:title image:image selectedImage:selectedImage]; UIViewController *controller = [[self alloc] init]; //这里self代表类本身,因为这是个+方法,出现在+方法中的self代类本身,当然,也可以这样写: [[[self class] alloc] init] autorelease]; controller.tabBarItem = item; return controller;}@end// -----------------------------------------------@interface RootViewController ()@end@implementation RootViewController- (id)init{ if (self = [super init]) { [self setUp]; } return self;}- (void)setUp{ //设置TabBar内容* NSMutableArray *viewControllers = [NSMutableArray array]; NSArray *array = @[[PhotoViewController class],[AudioViewController class],[VideoViewController class],[ResumeDownloadController class]]; NSArray *titles = @[@"相册",@"音频",@"视频",@"断点续传"]; UIImage *image = [UIImage imageNamed:@"star@2x.png"]; UIImage *images = [UIImage imageNamed:@"stared@2x.png"]; NSArray *iconImage = @[image,image,image,image]; NSArray *selectImage = @[images,images,images,images];//图片渲染 ? for (NSInteger i = 0; i < array.count; i++) { Class cls = array[i]; UIViewController *controller = [cls viewControllerWithTitle:titles[i] image:iconImage[i] selectedImage:selectImage[i]];//通过我们对UIViewController的扩展方法方便的新建视图控制器对象 [viewControllers addObject:controller]; } self.viewControllers = viewControllers;}
2. 相册
PhotoViewController.m
/* UIImagePickerController是继承于UINavigationController, 所以它本身是一个导航控制器,不要push它,push导航控制器本身不合理,所以我们一般把它present出来 当我们从系统相册中取出来图片(或者是拍照)的时候,通过代理方法把资源传给我们,所以我们要设置代理并实现代理方法,来获取UIImagePickerController对象给我们的东西,由于UIImagePickerController继承于UINavigationController, 要遵守UINavigationControllerDelegate协议, 不然有警告(其实不理它也没关系)*/#import "PhotoViewController.h"#import <MobileCoreServices/MobileCoreServices.h>//这里面预置一些宏和其他功能 比如kUTTypeImage#import "ImageTool.h"//图片剪裁@interface PhotoViewController ()<UIImagePickerControllerDelegate,UINavigationControllerDelegate,UIActionSheetDelegate>@property (weak, nonatomic) IBOutlet UIImageView *imageView;@end@implementation PhotoViewController- (void)viewDidLoad { [super viewDidLoad];}//从系统图片库或相册中去图片- (IBAction)getPhotoFromAlbum:(id)sender { [self loadImagePickerControllerWithSourceType:UIImagePickerControllerSourceTypePhotoLibrary];}//拍照(要真机)- (IBAction)getPhotoFromCamera:(id)sender { [self loadImagePickerControllerWithSourceType:UIImagePickerControllerSourceTypeCamera];}//给imageView添加一个手势,当点击这个imageView的时候抽发此方法- (IBAction)tapOnImageView:(id)sender { //菜单栏 UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"图片来源" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"相册", @"图片库", @"拍照", nil]; [actionSheet showInView:self.view];}#pragma mark - 调用取图片或是拍照方法*//系统相册取图片或拍照要用UIImagePickerController, 有一个sourceType,这个属性决定是取图片还是拍照- (void)loadImagePickerControllerWithSourceType:(UIImagePickerControllerSourceType)sourceType{ if ([UIImagePickerController isSourceTypeAvailable:sourceType]) {//如果当前机器支持sourceType(比如拍照,模拟器是不支持的) UIImagePickerController *controller = [[UIImagePickerController alloc] init]; controller.sourceType = sourceType; controller.allowsEditing = YES;//允许imagePickerController内置编辑功能 controller.delegate = self; [self presentViewController:controller animated:YES completion:nil]; }}#pragma mark - <UIImagePickerControllerDelegate>//选中了一个资源(choose)- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{ NSLog(@"%@",info);//info存储的就是选中的资源的信息 NSString *mediaType = info[UIImagePickerControllerMediaType];//取选中资源的类型 if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {//如果选中的是图片资源 UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage]; //从info中取出图片资源 UIImagePickerControllerEditedImage 编辑后的图片 UIImagePickerControllerOriginalImage 原始图片 self.imageView.image = image; //图片剪裁* //[[ImageTool shareTool] resizeImageToSize:CGSizeMake(100, 100) sizeOfImage:image]; } [picker dismissViewControllerAnimated:YES completion:nil];}//当点击cancel的时候- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker{ [picker dismissViewControllerAnimated:YES completion:nil];}#pragma mark - <UIActionSheetDelegate>tap手势 1. (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{ NSLog(@"%ld %@",buttonIndex,[actionSheet buttonTitleAtIndex:buttonIndex]); switch (buttonIndex) { case 0://相册 { [self loadImagePickerControllerWithSourceType:UIImagePickerControllerSourceTypeSavedPhotosAlbum]; } break; case 1://图片库 { [self loadImagePickerControllerWithSourceType:UIImagePickerControllerSourceTypePhotoLibrary]; } break; case 2://拍照 { [self loadImagePickerControllerWithSourceType:UIImagePickerControllerSourceTypeCamera]; } break; default: break; }}
3. 音频Audio
AudioViewController.m
#import "AudioViewController.h"#import <AVFoundation/AVFoundation.h>#import "ZZLrcParser.h"//歌词解析@interface AudioViewController ()<AVAudioPlayerDelegate>@property (weak, nonatomic) IBOutlet UILabel *lrcLabel;//显示歌词@property (weak, nonatomic) IBOutlet UIProgressView *progressView;@property (weak, nonatomic) IBOutlet UISlider *volumeSlider;//音量@property (nonatomic) AVAudioPlayer *player;//音频播放器,播放本地音乐@property (nonatomic) ZZLrcParser *lrcParser;//歌词解析器对象@property (nonatomic) NSTimer *timer;//定时器@end@implementation AudioViewController- (void)viewDidLoad { [super viewDidLoad]; [self initLrc];//初始化歌词 [self initPlayer];//初始化播放器}#pragma mark - 初始化歌词- (void)initLrc{ NSString *filePath = [[NSBundle mainBundle] pathForResource:@"北京北京" ofType:@"lrc"];//*在这不要写错后缀 self.lrcParser = [[ZZLrcParser alloc] initWithFilePath:filePath];}#pragma mark - 初始化播放器- (void)initPlayer{ NSString *filePath = [[NSBundle mainBundle] pathForResource:@"北京北京" ofType:@"mp3"]; self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:filePath] error:nil]; self.player.delegate = self; [self.player prepareToPlay];//准备播放}//播放- (IBAction)play:(id)sender { [self.player play];//开始播放 if (_timer == nil) { self.timer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES]; // 每隔一小段时间,通过timerAction:方法更新显示的歌词,更新进度条 } [self.timer setFireDate:[NSDate date]]; // 触发定时器方法,写成[NSDate distantPast]也行}- (void)timerAction:(NSTimer *)timer{ // 1. 更新进度条 player.currentTime当前播放时间 player.duration整个歌曲共占用的时候 self.progressView.progress = self.player.currentTime/self.player.duration; self.lrcLabel.text = [self.lrcParser lrcByTime:self.player.currentTime];}//暂停- (IBAction)pause:(id)sender { [self.player pause]; [self.timer setFireDate:[NSDate distantFuture]];//暂停定时器}//停止- (IBAction)stop:(id)sender { [self.player stop]; self.player.currentTime = 0.0;//播放进度归零 self.progressView.progress = 0.0;//进度条归零 [self.timer invalidate];//定时器置空 self.timer = nil;}//静音 switch- (IBAction)silenceSwitch:(UISwitch *)aSwitch { self.player.volume =aSwitch.isOn ? 0.0 : self.volumeSlider.value;//条件运算符判断}//声音滑块- (IBAction)volumeSlider:(UISlider *)slider { self.player.volume = slider.value;//player.volume范围范和默认的slider范围都是[0.0~1.0]}#pragma mark - <AVAudioPlayerDelegate>//当成功播放完成一首歌后,调用方法- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{ NSLog(@"播放完成");}//当系统级别的功能接入(来电话了),播放器被打断时,调用此方法- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player{ NSLog(@"来电话播放器被打断");}//当播放器结束被打断,调用该方法 1. (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withFlags:(NSUInteger)flags{ //继续播放 [self play:nil];}
4. 视频Video
VideoViewController.m
#import "VideoViewController.h"#import <MediaPlayer/MediaPlayer.h>//@interface VideoViewController ()@end@implementation VideoViewController- (void)viewDidLoad { [super viewDidLoad];}//播放本地视频- (IBAction)playLocalVideo:(id)sender { NSString *filePath = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"mp4"]; //NSLog(@"%@",filePath); [self loadMoviePlayWithUrl:filePath];}- (void)loadMoviePlayWithUrl:(NSString *)url { //MPMoviePlayerViewController 视频播放视图控制器,本身带了一个视图,控制视频的暂停、播放、放大缩小等东西,不可定制 //可以播放本地/服务器上的在线视频 //MPMoviePlayerViewController自带了一个MPMoviePlayerController对象控制视频的播放 NSURL *movieUrl = nil; if ([url hasPrefix:@"http://"] || [url hasPrefix:@"https://"]) { movieUrl = [NSURL URLWithString:url]; }else{ movieUrl = [NSURL fileURLWithPath:url];//注意一下,用的是路径 } MPMoviePlayerViewController *player = [[MPMoviePlayerViewController alloc] initWithContentURL:movieUrl]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playEnd:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; // 向通知中心注册,当视频播放完成后,回调self的playEnd:方法,视频播放完成后,通知中心发送一个通知给我们(通过playEnd:) player.moviePlayer.shouldAutoplay = YES;//自动播放 [self presentViewController:player animated:YES completion:nil];}//播放结束- (void)playEnd:(NSNotification *)notification { // NSLog(@"播放完成"); // NSLog(@"info: %@", notification.userInfo); // 获取返回的原因 NSInteger type = [notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue]; switch (type) { case 0: NSLog(@"正常结束返回"); break; case 1: NSLog(@"异常"); break; case 2: NSLog(@"点击Done 返回"); break; default: break; } [[NSNotificationCenter defaultCenter] removeObserver:self];//从通知中心移除}//播放远程视频- (IBAction)palyNetVideo:(id)sender { [self loadMoviePlayWithUrl:@"http://123.56.117.179:8080/video/ts/lv2.m3u8"];}
5. 断点续传
ResumeDownloadController.m
#import "ResumeDownloadController.h"#import "FileDownload.h"#define URL @"http://dlsw.baidu.com/sw-search-sp/soft/2a/25677/QQ_V4.0.0.1419920162.dmg";@interface ResumeDownloadController ()<FileDownloadDelegate>@property (weak, nonatomic) IBOutlet UIProgressView *progressView;// 进度条@property (weak, nonatomic) IBOutlet UILabel *percentLabel;// 下载百分比@property (nonatomic) FileDownload *downloader;@end@implementation ResumeDownloadController- (void)viewDidLoad { [super viewDidLoad];}- (FileDownload *)downloader{ if (_downloader == nil) { _downloader = [[FileDownload alloc] init]; _downloader.urlStr = URL; _downloader.delegate = self; } return _downloader;}//开始下载- (IBAction)start:(id)sender { [self.downloader start];}//停止下载- (IBAction)stop:(id)sender { [self.downloader stop];}#pragma mark - <FileDownloadDelegate>- (void)fileDownload:(FileDownload *)downloader failWithError:(NSError *)error { NSLog(@"%@", error);}- (void)fileDownload:(FileDownload *)downloader downloadSize:(long long)downloadSize totalSize:(long long)totalSize{ //更新进度条 float progress = 1.0 * downloadSize/totalSize;//0.0~1.0 self.progressView.progress = progress; NSString *str = [NSString stringWithFormat:@"%.2f%%",progress*100]; self.percentLabel.text = str;}- (void)fileDownloadDidFinish:(FileDownload *)downloader { NSLog(@"Download finish"); UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"您已下载完成" message:nil delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; [alertView show]; self.progressView.progress = 1.0; self.percentLabel.text = @"100%";}
5.1 封装下载类
FileDownload.h
#import <Foundation/Foundation.h>@class FileDownload;@protocol FileDownloadDelegate <NSObject>- (void)fileDownload:(FileDownload *)downloader failWithError:(NSError *)error;- (void)fileDownload:(FileDownload *)downloader downloadSize:(long long)downloadSize totalSize:(long long)totalSize; // 每当下载一点数据,通过这个代理方法告诉其他对象- (void)fileDownloadDidFinish:(FileDownload *)downloader;@end@interface FileDownload : NSObject@property (nonatomic, copy) NSString *urlStr;@property (nonatomic, weak) id<FileDownloadDelegate> delegate;- (void)start;// 开始下载- (void)stop;// 停止下载@end
FileDownload.m
#import "FileDownload.h"#import "NSString+util.h"//Md5加密@interface FileDownload ()<NSURLConnectionDataDelegate>@property (nonatomic,copy) NSString *downloadDirPath;// 下载的文件夹路径,这个路径可是指定写死的,也就是说通过ZZFileDownload下载的文件都放在这个文件夹中@property (nonatomic,copy) NSString *filePath;// 文件路径,下载数据写入到filePath中@property (nonatomic) NSFileHandle *writeHandle;// 文件句柄,用它往文件中写入服务器返回过来的数据@property (nonatomic) long long totalFileSize;// 文件总大小@property (nonatomic) long long downloadFileSize;// 当前已下载的文件大小@property (nonatomic) NSURLConnection *connection;// 网络连接类@end@implementation FileDownload// 文件夹路径(指定)- (NSString *)downloadDirPath{ if (_downloadDirPath == nil) { //获取文件的路径 NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];//取到沙盒里面Document目录 NSString *dirPath = [docPath stringByAppendingPathComponent:@"FileDownload"];//在Document文件夹下存在一个叫ZZFileDownload的文件夹,把我们下载的东西全部放在这个文件夹下,方便管理, /* 注意:必须写成这样才保证每次运行存储的是同一文件夹的 stringByAppendingPathComponent */ BOOL flag = [[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:nil]; if (flag) { NSLog(@"创建文件夹成功"); _downloadDirPath = dirPath; } } return _downloadDirPath;}//开始下载- (void)start{ if ([self prepareDownload]) {//在向服务器正式下载之前做好准备工作 /* NSURLConnection实现断点续传 Range头域 Range头域可以请求实体的⼀个或者多个子范围: 表⽰头500个字节:bytes=0-499 表⽰第二个500字节:bytes=500-999 表⽰最后500个字节:bytes=-500 表⽰500字节以后的范围:bytes=500- 第⼀个和最后⼀个字节:bytes=0-0,-1 同时指定⼏个范围:bytes=500-600,601-999 实现断点下载就是在httpRequest中加⼊入 Range 头。 [request addValue:@"bytes=500-" forHTTPHeaderField:@"Range"]; 至于能否正常实现断点续传,还要看服务器是否⽀支持。 如果⽀支持,⼀一切没问题。 如果不⽀支持可能出现两种情况,1.不理会你得range值,每次都重 新下载数据;2.直接下载失败。 */ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.urlStr]]; // 重点:设置请求的Range头域,告诉服务器“你给我返回多少多少字节以后的数据” [request addValue:[NSString stringWithFormat:@"bytes=%llu-", _downloadFileSize] forHTTPHeaderField:@"Range"]; // 注意: @"bytes=%llu-" 中间不能有空格 self.connection = [NSURLConnection connectionWithRequest:request delegate:self]; }}- (BOOL)prepareDownload { NSLog(@"%@", NSHomeDirectory());// 打印下载的路径,可转为dmg格式的点击同意就可安装 // 1.确保文件存在(根据传过来的urlStr关联文件),如果文件不存在,则创建,并得到文件路径 -> 下载的文件夹/文件名 if (self.urlStr == nil || self.urlStr.length == 0) { return NO; } NSString *fileName = self.urlStr.md5; NSString *filePath = [self.downloadDirPath stringByAppendingPathComponent:fileName]; // Document/ZZFileDownload/fileName 调这个方法会自动的在gf_list.txt前面加一个/ BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath]; // 文件是否存在 判断原来filepath文件是否存在 if (!fileExists) { // 如果不存在,则创建它 /* // 第一个参数:文件路径(在哪个路径下创建文件) // 第二个参数:文件创建时给它写入什么内容(NSData *) // 第三个参数:文件的属性,写为nil用它默认的属性 // 返回值是YES表示创建成功,否则创建失败 // 注意:如果原来文件存在,会把原来文件替换掉,所以一般判断一下原来文件是否已经存在 */ BOOL createSuccess = [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil]; if (!createSuccess) { return NO; } } // 2.根据文件路径获取它的大小 /* // 获取文件属性 // attributesOfItemAtPath:filePath 返回文件filePath的信息,把这些信息存在一个字典中以键值对的形式返回 error = nil; NSDictionary *fileInfo = [fileManager attributesOfItemAtPath:filePath error:&error]; NSLog(@"------%@", fileInfo); NSLog(@"文件大小(以字节为单位):%lld", [fileInfo[NSFileSize] longLongValue]); // [fileInfo objectForKey:NSFileSize]; // NSFileSize -> key -> 字符串 NSLog(@"获取文件大小方法二(以字节为单位):%lld", [fileInfo fileSize]); */ _downloadFileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil] fileSize]; // 3.设置写入此文件的文件句柄(等一下要把从服务器上获取的data写入文件中) /* // NSFileHandle // 文件句柄,使用它对文件进行读取,写入,更改 NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath]; // 以只读方式打开文件,不能往里面写入 fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath]; // 以只写方式打开文件,不能读取 fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath]; // 以读写方式打开文件,即可读又可写 */ self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:filePath]; return YES;}// 停止下载- (void)stop { [self.connection cancel], self.connection = nil; // 断开连接 [self.writeHandle closeFile]; // 关闭文件句柄}#pragma mark - <NSURLConnectionDataDelegate>// 失败- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { if ([self.delegate respondsToSelector:@selector(fileDownload:failWithError:)]) { [self.delegate fileDownload:self failWithError:error]; }}// 接收到服务器的响应(服务器给我们返回的是响应头信息)// 在每次connection连接只会调用一次该方法- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { // bytes=500- 获取下载文件的大小expectedContentLength self.totalFileSize = response.expectedContentLength + self.downloadFileSize; // 说明我们已经下载完了 if (self.totalFileSize == self.downloadFileSize) { [self stop]; if ([self.delegate respondsToSelector:@selector(fileDownloadDidFinish:)]) { [self.delegate fileDownloadDidFinish:self]; } }}// 接收到服务器返回来的数据(这个方法被调很多次)- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { //把数据写入文件 [self.writeHandle seekToEndOfFile]; //在文件末尾追加数据 [self.writeHandle writeData:data]; //写入数据 self.downloadFileSize += data.length; if ([self.delegate respondsToSelector:@selector(fileDownload:downloadSize:totalSize:)]) { [self.delegate fileDownload:self downloadSize:self.downloadFileSize totalSize:self.totalFileSize]; }}@end
图片剪裁ImageTool.h
#import <Foundation/Foundation.h>#import <UIKit/UIKit.h>@interface ImageTool : NSObject// 返回单例的静态方法+ (ImageTool *)shareTool;// 返回特定尺寸的UImage , image参数为原图片,size为要设定的图片大小- (UIImage*)resizeImageToSize:(CGSize)size sizeOfImage:(UIImage*)image;// 在指定的视图内进行截屏操作,返回截屏后的图片- (UIImage *)imageWithScreenContentsInView:(UIView *)view;@end
ImageTool.m
#import "ImageTool.h"#import <QuartzCore/QuartzCore.h>@implementation ImageToolstatic ImageTool *_shareImageTool =nil;// 返回单例的静态方法+ (ImageTool *)shareTool{ //确保线程安全 @synchronized(self){ //确保只返回一个实例 if (_shareImageTool == nil) { _shareImageTool = [[ImageTool alloc] init]; } } return _shareImageTool;}- (id)init{ self = [super init]; if (self) { } return self;}// 在指定的视图内进行截屏操作,返回截屏后的图片- (UIImage *)imageWithScreenContentsInView:(UIView *)view{ //根据屏幕大小,获取上下文 UIGraphicsBeginImageContext([[UIScreen mainScreen] bounds].size); [view.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return viewImage;}- (UIImage*)resizeImageToSize:(CGSize)size sizeOfImage:(UIImage*)image{ UIGraphicsBeginImageContext(size); //获取上下文内容 CGContextRef ctx= UIGraphicsGetCurrentContext(); CGContextTranslateCTM(ctx, 0.0, size.height); CGContextScaleCTM(ctx, 1.0, -1.0); //重绘image CGContextDrawImage(ctx,CGRectMake(0.0f, 0.0f, size.width, size.height), image.CGImage); //根据指定的size大小得到新的image UIImage* scaled= UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return scaled;}@end
歌词解析ZZLrcParser.h
#import <Foundation/Foundation.h>@interface ZZLrcItem : NSObject@property (nonatomic) float time;@property (nonatomic, copy) NSString *lrc;- (BOOL)descByTime:(ZZLrcItem *)item;- (void)show;@end// --------------------------------------------------@interface ZZLrcParser : NSObject- (instancetype)initWithFilePath:(NSString *)filePath;@property (nonatomic, copy) NSString *title; // [ti:歌词(歌曲)标题]@property (nonatomic, copy) NSString *author; // [ar:歌词作者]@property (nonatomic, copy) NSString *albume; // [al:歌曲所在唱片集]@property (nonatomic, copy) NSString *editor; // [by:本LRC文件的创建者]@property (nonatomic, copy) NSString *version; // [ve:歌曲的版本]@property (nonatomic) NSMutableArray *items; // 数组里存放的是一个个ZZLrcItem对象// 给出来时间,我们就知道应该播放哪句歌词- (NSString *)lrcByTime:(float)time;- (void)show;@end
ZZLrcParser.m
#import "ZZLrcParser.h"NSString *const kTitle = @"ti";NSString *const kAuthor = @"ar";NSString *const kAlbume = @"al";NSString *const kEditor = @"by";NSString *const kVersion = @"ve";@implementation ZZLrcItem- (BOOL)descByTime:(ZZLrcItem *)item { return self.time > item.time;}- (void)show { printf("time: %f --> song: %s\n", self.time, [self.lrc UTF8String]);}@end// --------------------------------------------------@implementation ZZLrcParser- (instancetype)initWithFilePath:(NSString *)filePath { if (self = [super init]) { self.items = [NSMutableArray array]; NSString *content = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; NSArray *array = [content componentsSeparatedByString:@"\n"]; for (NSString *str in array) { if (str.length == 0) continue; // @"" unichar c = [str characterAtIndex:1]; if (c <= '9' && c >= '0') { [self parseTimeString:str]; // 时间歌词 } else { [self parseFileInfo:str]; } } } return self;}// [02:11.27][01:50.22][00:21.95]穿过幽暗地岁月// [00:06.53]你对自由地向往- (void)parseTimeString:(NSString *)aStr { NSArray *array = [aStr componentsSeparatedByString:@"]"]; // [02:11.27 // [01:50.22 // [00:21.95 // 穿过幽暗地岁月 NSString *song = [array lastObject]; // 遍历的话不取最后一项(最后一项是歌词项) for (NSInteger i = 0; i < array.count-1; i++) { NSString *timeStr = array[i]; // [02:11.27 timeStr = [timeStr substringFromIndex:1]; // 02:11.27 NSArray *arr = [timeStr componentsSeparatedByString:@":"]; NSString *minute = arr[0]; NSString *second = arr[1]; float time = [minute floatValue]*60 + [second floatValue]; // 131.27 ZZLrcItem *item = [[ZZLrcItem alloc] init]; item.time = time; // 131.27 item.lrc = song; // 穿过幽暗地岁月 [_items addObject:item]; } // 以时间升序排序 [_items sortUsingSelector:@selector(descByTime:)];}- (void)parseFileInfo:(NSString *)aStr { NSString *newStr = aStr; newStr = [newStr substringFromIndex:1]; // [ti:蓝莲花] -> ti:蓝莲花] newStr = [newStr substringToIndex:newStr.length-1]; // ti:蓝莲花 NSArray *arr = [newStr componentsSeparatedByString:@":"]; NSString *type = arr[0]; // ti NSString *content = arr[1]; // 蓝莲花 if ([type isEqualToString:kTitle]) { self.title = content; } else if ([type isEqualToString:kAuthor]) { self.author = content; } else if ([type isEqualToString:kAlbume]) { self.albume = content; } else if ([type isEqualToString:kEditor]) { self.author = content; } else if ([type isEqualToString:kVersion]) { self.version = content; }}- (NSString *)lrcByTime:(float)time { ZZLrcItem *item = [self itemByTime:time]; return item.lrc;}// 找比time稍大的,返回上一条- (ZZLrcItem *)itemByTime:(float)time { NSInteger index = -100; for (NSInteger i = 0; i < self.items.count; i++) { ZZLrcItem *item = self.items[i]; if (item.time > time) { // 找到了比time大一点的项 index = i - 1; break; } } if (index == -1) { // 播放第一行 index = 0; } else if (index == -100) { // 播放最后一行 index = _items.count-1; } return _items[index];}- (void)show { [_items makeObjectsPerformSelector:@selector(show)];}/** * ZZLrcParser { author, title, ... NSMutableArray *_items; // 数组里存放的是一个修正<ZZLrcItem>对象 } */@end
0 0
- 多媒体和断点续传
- 多媒体编程(多线程下载、断点续传、可以暂停、继续)
- Etag和断点续传
- NSURLSession下载和断点续传
- 多线程下载和断点续传
- 多线程下载和断点续传
- 文件断点续传和下载
- wget断点续传和限速
- 浅谈多线程和断点续传
- 断点续传和多线程下载
- 《网络和多媒体》读书笔记
- Applet和多媒体
- 22.1 Windows 和多媒体
- 断点续传和多线程下载(上)
- 断点续传和多线程下载模块
- C# 断点续传原理和实现
- FTP和HTTP断点续传原理
- java多线程下载和断点续传
- JSON写、读文件
- Android旋屏onConfigurationChanged
- 专注度训练
- ffmpeg源码分析与应用示例(二)——代码抽取的意义
- Android开发-安卓插件开发
- 多媒体和断点续传
- 上位机
- ios 关于MD5 加密的32位与16位
- Swift学习: 从Objective-C到Swift
- 从svn路径上下载了代码在Androidstudio中不显示
- OC_构造方法(工厂方法)
- Python XML解析
- 太瞌睡工作!用xshell连接vm(centos6.4)老是提示失败
- vim 代码提示功能,让vim可以媲美IDE