一 框架搭建

  • 添加一个毛玻璃效果
- (void)setupBlurGlass
{// 1.创建UIToolbarUIToolbar *toolbar = [[UIToolbar alloc] init];toolbar.barStyle = UIBarStyleBlack;[self.albumView addSubview:toolbar];// 2.给UIToolbar添加约束[toolbar mas_makeConstraints:^(MASConstraintMaker *make) {/*make.width.equalTo(self.albumView.mas_width);make.height.equalTo(self.albumView.mas_height);make.centerX.equalTo(self.albumView.mas_centerX);make.centerY.equalTo(self.albumView.mas_centerY);*/make.edges.equalTo(self.albumView);}];
}
  • 设置状态栏字体为白色
- (UIStatusBarStyle)preferredStatusBarStyle
{return UIStatusBarStyleLightContent;
}
  • 歌手图片绘制成圆形
// 颜色抽成一个宏
#define GGColor(r,g,b) ([UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1.0])- (void)viewWillLayoutSubviews
{[super viewWillLayoutSubviews];// 设置歌手图片的圆角self.iconView.layer.cornerRadius = self.iconView.bounds.size.width * 0.5;self.iconView.layer.masksToBounds = YES;self.iconView.layer.borderWidth = 8;self.iconView.layer.borderColor = GGColor(40, 40, 40).CGColor;
}

二 播放音乐

  • 抽取工具类MusicTool,管理所有歌曲
static NSArray *_musics;
static GGMusic *_playingMusic;+ (void)initialize
{// 使用MJExtension 框架_musics = [GGMusic objectArrayWithFilename:@"Musics.plist"];_playingMusic = _musics[0];
}+ (NSArray *)musics
{return _musics;
}
// 获取当前正在播放的歌曲
+ (GGMusic *)playingMusic
{return _playingMusic;
}
// 设置正在播放的歌曲
+ (void)setPlayingMusic:(GGMusic *)playingMusic
{_playingMusic = playingMusic;
}
  • 用工具类播放音乐
#pragma mark - 开始播放歌曲
- (void)startPlayingMusic
{// 1.取出当前播放的歌曲GGMusic *playingMusic = [GGMusicTool playingMusic];// 2.设置界面的基本展示self.albumView.image = [UIImage imageNamed:playingMusic.icon];self.iconView.image = [UIImage imageNamed:playingMusic.icon];self.songLabel.text = playingMusic.name;self.singerLabel.text = playingMusic.singer;// 3.开始播放歌曲AVAudioPlayer *player = [GGAudioTool playMusicWithMusicName:playingMusic.filename];self.currentTimeLabel.text = [NSString stringWithTime:player.currentTime]; // 当前时间self.totalTimeLabel.text = [NSString stringWithTime:player.duration];  // 总时间self.player = player;// 4.给iconView添加旋转动画[self addIconViewAnimation];// 5.添加定时器[self startProgressTimer];
}
  • 新建分类NSString+GGTimeToString,时间转换拼接为字符串
+ (NSString *)stringWithTime:(NSTimeInterval)time
{NSInteger min = time / 60;NSInteger second = (NSInteger)time % 60;return [NSString stringWithFormat:@"%02ld:%02ld", min, second];
}

三 旋转动画和进度完善

  • 给iconView添加旋转动画
- (void)addIconViewAnimation
{// 1.创建动画(基本动画)CABasicAnimation *rotationAnim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];// 2.设置动画的属性rotationAnim.fromValue = @(0);rotationAnim.toValue = @(M_PI * 2);rotationAnim.duration = 40.0;rotationAnim.repeatCount = NSIntegerMax;// 3.添加到iconView的layer[self.iconView.layer addAnimation:rotationAnim forKey:nil];
}
  • 更新进度条,搞一个定时器
#pragma mark - 对定时器的操作
- (void)startProgressTimer
{[self updateProgress]; // 主动调用,防止1s间隔self.progressTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];[[NSRunLoop mainRunLoop] addTimer:self.progressTimer forMode:NSRunLoopCommonModes];
}- (void)stopProgressTimer
{[self.progressTimer invalidate];self.progressTimer = nil;
}- (void)updateProgress
{// 改变滑块的位置self.slider.value = self.player.currentTime / self.player.duration;// 设置当前播放时间的Label的文字self.currentTimeLabel.text = [NSString stringWithTime:self.player.currentTime];
}

四 进度条事件处理

  • 滑动进度条需要以下几个操作步骤:

    • 1.移除定时器
    • 2.滑动过程中改变当前播放时间
    • 3.用户松手时播放当前时间对应音乐
  • 定时器移除代码

- (void)stopProgressTimer
{[self.progressTimer invalidate];self.progressTimer = nil;
}
  • 根据滑动位置比例计算出当前时间
#pragma mark 滑块的值改变
- (IBAction)sliderValueChange:(UISlider *)sender {// 1.进度条当前进度的比例CGFloat ratio = sender.value;// 2.根据当前的比例,计算当前的时间NSTimeInterval currentTime =  self.player.duration * ratio;// 3.改变currentTimeLabel的显示的文字self.currentTimeLabel.text = [NSString stringWithTime:currentTime];
}
  • 用户松手时播放当前位置对应时间的音乐

    • 注意:进度条想要监听点击需要添加一个手势
#pragma mark 用户结束点击
- (IBAction)sliderTouchUpInside:(UISlider *)sender {// 1.改变歌曲播放的进度// 1.1.进度条当前进度的比例CGFloat ratio = sender.value;// 1.2.根据当前的比例,计算当前的时间NSTimeInterval currentTime =  self.player.duration * ratio;// 1.3.改变歌曲播放的时间self.player.currentTime = currentTime;// 2.添加定时器[self startProgressTimer];
}
  • 进度条的点击
#pragma mark 进度条的点击
- (IBAction)sliderClick:(UITapGestureRecognizer *)sender {// 1.获取进度条的比例// 1.1.获取用户点击的位置CGPoint point = [sender locationInView:sender.view];// 1.2.计算比例CGFloat ratio = point.x / self.slider.bounds.size.width;// 2.计算当前应该播放的时间NSTimeInterval currentTime = ratio * self.player.duration;// 3.改变歌曲播放的进度self.player.currentTime = currentTime;// 4.更新进度(定时器会有1s 间隔)[self updateProgress];
}

五 对歌曲控制的事件

  • 上一首

    • 抽取工具类方法
+ (GGMusic *)previousMusic
{// 1.取出当前歌曲的下标值NSUInteger currentIndex = [_musics indexOfObject:_playingMusic];// 2.计算上一个歌曲的下表NSInteger previousIndex = currentIndex - 1;if (previousIndex < 0) {previousIndex = _musics.count - 1;}// 3.取出上一个歌曲return [_musics objectAtIndex:previousIndex];
}
- (IBAction)previousMusic {// 0.取出当前歌曲GGMusic *playingMusic = [GGGMusicTool playingMusic];// 1.停止当前歌曲[GGAudioTool stopMusicWithMusicName:playingMusic.filename];// 2.取出下一首歌曲,并且播放GGMusic *previousMusic = [GGMusicTool previousMusic];[GGAudioTool playMusicWithMusicName:previousMusic.filename];// 3.设置上一首歌曲成为当前播放的歌曲[GGMusicTool setPlayingMusic:previousMusic];// 4.改变界面信息成为上一首歌曲的信息[self startPlayingMusic];
}
  • 下一首
+ (GGMusic *)nextMusic
{// 1.取出当前歌曲的下标值NSUInteger currentIndex = [_musics indexOfObject:_playingMusic];// 2.计算下一个歌曲的下表NSInteger nextIndex = currentIndex + 1;if (nextIndex > _musics.count - 1) {nextIndex = 0;}// 3.取出上一个歌曲return [_musics objectAtIndex:nextIndex];
}
  • 上一首和下一首代码类似,抽成1个方法
- (void)switchMusicWithNextMusic:(BOOL)isNextMusic
{// 0.取出当前歌曲GGMusic *playingMusic = [GGMusicTool playingMusic];// 1.停止当前歌曲[GGAudioTool stopMusicWithMusicName:playingMusic.filename];// 2.取出下一首歌曲,并且播放GGMusic *changeMusic = nil;if (isNextMusic) {changeMusic = [GGMusicTool nextMusic];} else {changeMusic = [GGMusicTool previousMusic];}[GGAudioTool playMusicWithMusicName:changeMusic.filename];// 3.设置上一首歌曲成为当前播放的歌曲[GGMusicTool setPlayingMusic:changeMusic];// 4.改变界面信息成为上一首歌曲的信息[self startPlayingMusic];
}
  • 暂停/播放

    • 默认图片是选中状态
- (IBAction)playOrPauseMusic:(UIButton *)sender {// 1.改变按钮的状态sender.selected = !sender.isSelected;// 2.根据歌曲是否在播放,来决定暂停还是播放if (self.player.isPlaying) {[self.player pause];// 暂停动画[self.iconView.layer pauseAnimate];// 停止进度定时器[self stopProgressTimer];} else {[self.player play];// 继续动画[self.iconView.layer resumeAnimate];// 添加进度定时器[self startProgressTimer];}
}
  • 暂停/继续核心动画根控制器没什么关系,抽取分类CALayer+GGAnimate
- (void)pauseAnimate
{CFTimeInterval pausedTime = [self convertTime:CACurrentMediaTime() fromLayer:nil];self.speed = 0.0;self.timeOffset = pausedTime;
}
- (void)resumeAnimate
{CFTimeInterval pausedTime = [self timeOffset];self.speed = 1.0;self.timeOffset = 0.0;self.beginTime = 0.0;CFTimeInterval timeSincePause = [self convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;self.beginTime = timeSincePause;
}

六 添加歌词的 View

  • 歌词的 View 为一个UIScrollView

    • 根据偏移量不同,变化 view 的透明度

      • 遵守UIScrollViewDelegate
 #pragma mark - 实现LrcView的代理方法
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{// 1.计算scrollView偏移量CGFloat offsetRatio = scrollView.contentOffset.x / scrollView.bounds.size.width;// 2.设置歌词的Label和iconView的透明度self.iconView.alpha = 1 - offsetRatio;self.lrcLabel.alpha = 1 - offsetRatio;
}   
  • 歌词展示用 tableview 实现GGLrcView

    • 初始化出来就是一个tableview
    - (instancetype)initWithCoder:(NSCoder *)aDecoder
    {
    if (self = [super initWithCoder:aDecoder]) {[self setupTableView];
    }
    return self;
    }
    • 添加 tableview
    - (void)setupTableView
    {
    // 1.创建tableView
    UITableView *tableView = [[UITableView alloc] init];
    self.tableView = tableView;// 2.添加到歌词的View中
    [self addSubview:tableView];// 3.设置tableView的属性
    self.tableView.dataSource = self;
    self.tableView.rowHeight = 35;
    }
  • UIScrollView中使用 Autolayout

    • 注意点:添加约束,必须要多添加2个约束(距离右边和下边)
- (void)layoutSubviews
{[super layoutSubviews];// 给tableView添加约束[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {make.top.equalTo(self.mas_top);make.left.equalTo(self.mas_left).offset(self.bounds.size.width);make.height.equalTo(self.mas_height);make.width.equalTo(self.mas_width);make.bottom.equalTo(self.mas_bottom);make.right.equalTo(self.mas_right);}];// 清除tableView的背景颜色和边线self.tableView.backgroundColor = [UIColor clearColor];self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;// 设置tableView的上下的内边距self.tableView.contentInset = UIEdgeInsetsMake(self.bounds.size.height * 0.5, 0, self.bounds.size.height * 0.5, 0);
}
  • 实现tableview 的数据源方法
#pragma mark - 实现tableView的数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{return 20;
}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{static NSString *ID = @"LrcCell";UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];if (cell == nil) {cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];// 设置cell的背景cell.backgroundColor = [UIColor clearColor];cell.selectionStyle = UITableViewCellSelectionStyleNone;// 设置label的属性cell.textLabel.textColor = [UIColor whiteColor];cell.textLabel.font = [UIFont systemFontOfSize:14.0];cell.textLabel.textAlignment = NSTextAlignmentCenter;}cell.textLabel.text = @"测试数据123";return cell;
}

七 歌词的解析

  • 字典转模型(一句歌词转成模型)GGLrcLine
/** 显示的歌词文字 */
@property (nonatomic, copy) NSString *text;
/** 时间 */
@property (nonatomic, assign) NSTimeInterval time;
  • 模型对外提供下面方法
- (instancetype)initWithLrcString:(NSString *)lrcString
{if (self = [super init]) {// [01:20.74]想这样没担忧 唱着歌 一直走NSArray *lrclineArray = [lrcString componentsSeparatedByString:@"]"];self.text = lrclineArray[1];self.time = [self timeWithString:[lrclineArray[0] substringFromIndex:1]];}return self;
}
  • 时间需要解析成秒,抽取为方法
- (NSTimeInterval)timeWithString:(NSString *)timeString
{// 01:20.74NSArray *timeArray = [timeString componentsSeparatedByString:@":"];NSInteger min = [timeArray[0] integerValue];NSInteger second = [[timeArray[1] componentsSeparatedByString:@"."][0] integerValue];NSInteger haomiao = [[timeArray[1] componentsSeparatedByString:@"."][1] integerValue];return min * 60 + second + haomiao * 0.01;
}
+ (instancetype)lrcLineWithLrcString:(NSString *)lrcString
{return [[self alloc] initWithLrcString:lrcString];
}
  • 解析歌词抽取工具类
+ (NSArray *)lrcToolWithLrcName:(NSString *)lrcName
{// 1.获取歌词的路径NSString *filePath = [[NSBundle mainBundle] pathForResource:lrcName ofType:nil];// 2.读取该文件中的歌词NSString *lrcString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];// 3.获取歌词的数组NSArray *lrcArray = [lrcString componentsSeparatedByString:@"\n"];// 4.将一句歌词转成模型对象,放入到一个数组中NSMutableArray *tempArray = [NSMutableArray array];for (NSString *lrclineString in lrcArray) {// 4.1.过滤不需要的歌词的行if ([lrclineString hasPrefix:@"[ti:"] || [lrclineString hasPrefix:@"[ar:"] || [lrclineString hasPrefix:@"[al:"] || ![lrclineString hasPrefix:@"["]) {continue;}// 4.2.解析每一句歌词转成模型对象GGLrcLine *lrcLine = [GGLrcLine lrcLineWithLrcString:lrclineString];[tempArray addObject:lrcLine];}return tempArray;
}
  • 重写 set 方法
#pragma mark - 重写setLrcName的方法
- (void)setLrcName:(NSString *)lrcName
{_lrcName = lrcName;// 解析歌词self.lrcLines = [GGLrcTool lrcToolWithLrcName:lrcName];// 刷新列表[self.tableView reloadData];// 让tableView滚动到最上面[self.tableView setContentOffset:CGPointMake(0, - self.tableView.bounds.size.height * 0.5) animated:YES];
}

八 将当前播放时间实时传递给LrcView

  • 建一个歌词定时器
/** 歌词的定时器 */
@property (nonatomic, strong) CADisplayLink *lrcTimer;
  • 对歌词定时器的操作
#pragma mark 歌词的定时器
- (void)startLrcTimer
{self.lrcTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateLrcInfo)];[self.lrcTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}- (void)stopLrcTimer
{[self.lrcTimer invalidate];self.lrcTimer = nil;
}- (void)updateLrcInfo
{self.lrcView.currentTime = self.player.currentTime;
}
  • 拿到实时时间

    • 根据当前播放时间, 让歌词滚到正确的位置
#pragma mark - 重写setCurrentTime方法
- (void)setCurrentTime:(NSTimeInterval)currentTime
{_currentTime = currentTime;// 找出需要显示的歌词NSInteger count = self.lrcLines.count;for (int i = 0; i < count; i++) {// 1.拿到i位置的歌词GGLrcLine *currentLrcLine = self.lrcLines[i];// 2.拿出i+1位置的歌词NSInteger nextIndex = i + 1;if (nextIndex >= count) return;GGLrcLine *nextLrcLine = self.lrcLines[nextIndex];// 3.当前时间大于i位置歌词的时间并且小于i+1位置的歌词的时间if (currentTime >= currentLrcLine.time && currentTime < nextLrcLine.time && self.currentIndex != i) {/*[01:03.45]你是我的小呀小苹果儿 63.45 63.46  64.43 i 12[01:07.06]就像天边最美的云朵 67.06 13*/// 计算i位置的IndexPathNSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];NSIndexPath *previousPath = [NSIndexPath indexPathForRow:self.currentIndex inSection:0];// 记录i位置的下标self.currentIndex = i;// 刷新i位置的cell(播放时字体变大)[self.tableView reloadRowsAtIndexPaths:@[indexPath, previousPath] withRowAnimation:UITableViewRowAnimationNone];// 让tableView的i位置的cell,滚动到中间位置[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];// 设置外面的歌词self.lrcLabel.text = currentLrcLine.text;}// 4.当正在播放某一个歌词(self.currentIndex == i)if (self.currentIndex == i) {// 4.1.计算当前已经播放的比例CGFloat progress = (currentTime - currentLrcLine.time) / (nextLrcLine.time - currentLrcLine.time);// 4.2.告知当前的歌词的Label进度NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];GGLrcCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];cell.lrcLabel.progress = progress;// 4.3.改变外面的歌词Label的进度self.lrcLabel.progress = progress;}}
}
  • 刷新数据,播放时字体变大
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{// 1.创建cellGGLrcCell *cell = [GGLrcCell lrcCellWithTableView:tableView];if (indexPath.row == self.currentIndex) {cell.lrcLabel.font = [UIFont systemFontOfSize:18.0];} else {cell.lrcLabel.font = [UIFont systemFontOfSize:14.0];cell.lrcLabel.progress = 0;}// 2.给cell设置数据GGLrcLine *lrcline = self.lrcLines[indexPath.row];cell.lrcLabel.text = lrcline.text;return cell;
}
  • 自定义 Label,实现 Label 按播放进度渐变

    • 重绘一下 Label
    - (void)drawRect:(CGRect)rect
    {
    [super drawRect:rect];CGRect drawRect = CGRectMake(0, 0, rect.size.width * self.progress, rect.size.height);[[UIColor greenColor] set];// UIRectFill(drawRect);
    // R = S * Da   S是现在正在画的内容透明为1,Da 之前 label 内容的透明度
    UIRectFillUsingBlendMode(drawRect, kCGBlendModeSourceIn); // 文字渐变
    }
    • 实时调用
    - (void)setProgress:(CGFloat)progress
    {
    _progress = progress;[self setNeedsDisplay];
    }
  • 将Label 添加到 cell 中GGLrcCell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {// 1.创建自定义的LabelGGLrcLabel *lrcLabel = [[GGLrcLabel alloc] init];// 2.添加cell中[self.contentView addSubview:lrcLabel];// 3.给自定义的label添加约束[lrcLabel mas_makeConstraints:^(MASConstraintMaker *make) {make.center.equalTo(self.contentView);}];// 4.设置label的属性lrcLabel.textColor = [UIColor whiteColor];lrcLabel.font = [UIFont systemFontOfSize:14.0];// 5.设置cell的属性self.backgroundColor = [UIColor clearColor];self.selectionStyle = UITableViewCellSelectionStyleNone;// 6.让成员属性的lrcLabel指向对象self.lrcLabel = lrcLabel;}return self;
}
  • 播放页面显示歌词
// 歌词的Label
@property (weak, nonatomic) IBOutlet GGLrcLabel *lrcLabel;// 5.将外面显示歌词的Label的对象,赋值给lrcView的引用self.lrcView.lrcLabel = self.lrcLabel;

注意:切换歌曲,刷新 i 位置的 cell 时会报错(上一首歌词量与下一首歌词量不同)

     // 计算i位置的IndexPathNSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];NSArray *indexPaths = nil;if (self.currentIndex >= count) {indexPaths = @[indexPath];} else {NSIndexPath *previousPath = [NSIndexPath indexPathForRow:self.currentIndex inSection:0];indexPaths = @[indexPath, previousPath];}

九 音乐后台播放

  • 1.开启后台模式

  • 2.设置音频会话模式

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {// 1.获取音频会话AVAudioSession *session = [AVAudioSession sharedInstance];// 2.设置音频会话的类型[session setCategory:AVAudioSessionCategoryPlayback error:nil];// 3.激活音频会话(静音状态依然可以播放)[session setActive:YES error:nil];return YES;
}
  • 设置锁屏界面
#pragma mark - 设置锁屏界面的信息
- (void)setupLockScreenInfo
{// 1.拿到当前播放的歌曲GGMusic *playingMusic = [GGMusicTool playingMusic];// 2.设置锁屏界面的内容// 2.1.获取锁屏界面中心MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];// 2.2.设置显示的信息NSMutableDictionary *dict = [NSMutableDictionary dictionary];// 2.2.1.设置歌曲名称[dict setValue:playingMusic.name forKey:MPMediaItemPropertyAlbumTitle];// 2.2.2.设置歌手名称[dict setValue:playingMusic.singer forKey:MPMediaItemPropertyArtist];// 2.2.3.设置专辑封面UIImage *image = [UIImage imageNamed:playingMusic.icon];MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:image];[dict setValue:artwork forKey:MPMediaItemPropertyArtwork];// 2.2.4.设置歌曲的总时长[dict setValue:@(self.player.duration) forKey:MPMediaItemPropertyPlaybackDuration];infoCenter.nowPlayingInfo = dict;// 2.3.让应用程序可以接受远程事件[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
  • 处理远程事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
{switch (event.subtype) {case UIEventSubtypeRemoteControlPlay:case UIEventSubtypeRemoteControlPause:[self playOrPauseMusic:self.playOrPauseBtn];break;case UIEventSubtypeRemoteControlNextTrack:[self nextMusic];break;case UIEventSubtypeRemoteControlPreviousTrack:[self previousMusic];break;default:break;}
}

iOS 开发项目之 QQ 音乐相关推荐

  1. iOS开发微信、QQ应用名称修改

    我们在做iOS开发时,经常会用到一些第三方平台,如QQ.微信.微博分享,微信.支付宝支付等:各大平台也都很友好的提供了开放平台,个人开发者可以免费的注册并进行应用创建集成到自己项目中,无非就是填写一些 ...

  2. 简单搭建iOS开发项目框架

    今天我们来谈谈如何搭建框架,框架需要做一些什么. 第一步:找到我们的目标 我们的目标是让其他开发人员拿到手后即可写页面,不再需要考虑其他的问题. 第二步:我们需要做哪些东西 各位跟着我一步一步来进行. ...

  3. ios上传音频文件到服务器,IOS开发:iPod的音乐库中的音频如何上传到服务器中...

    最近在做的项目里有一个功能,就是拿到手机媒体库中的音频文件,并实现APP中的播放,已经转成MP3格式上传到服务器上. 首先是要能获取到ipod library中的音频.这里我用的是MPMediaQue ...

  4. 前端项目——实现qq音乐网页版本(样式+获取数据+播放)

    这是一个完整的项目实现,内容包括网页排版,利用js css实现一些动态特效,特在此记录项目实现过程中的核心思想,以及难点的解决方法和在此项目中学到的新知识. 首先看一下该项目的官网效果 可以看到该网页 ...

  5. iOS开发之模仿qq通讯录

    这篇文章主要整理一下项目中用到的类似qq通讯录的收缩功能. 我实现的思路是在tableview的header放置button,然后根据button是否选中来判断是否需要显示那一个section. 首先 ...

  6. iOS开发之模仿qq通讯录源代码!

    这篇文章主要整理一下项目中用到的类似qq通讯录的收缩功能. 图片描述 我实现的思路是在tableview的header放置button,然后根据button是否选中来判断是否需要显示那一个sectio ...

  7. iOS开发——项目篇—高仿百思不得姐

    01 一.包装为导航控制器 UINavigationController *nav = [[UINavigationController alloc] initWithRootViewControll ...

  8. vue 项目获取QQ音乐歌单数据

    1.  前端请求(recommend.js): import axios from 'axios'export function getDiscList() {const url = '/api/ge ...

  9. [iOS开发项目-8] 喜马拉雅电台

    本项目是取自传智播客的教学项目,加入笔者的修改和润饰. 1. 项目名称:喜马拉雅电台 2. 项目截图展示 3. 项目功能 单纯的显示和滚动功能 4. 项目代码 #import "ViewCo ...

最新文章

  1. jupyter notebook 进阶使用:nbextensions
  2. linux 系统tar文件压缩打包命令
  3. 一步步学习微软InfoPath2010和SP2010--第三章节--表单设计基础:处理InfoPath布局、控件和视图(4)--控件属性功能区...
  4. ArcGIS中的投影和坐标转换
  5. HDU 4609 3-idiots(FFT)
  6. PAT 1017 Queueing at Bank[一般]
  7. python 类继承和组合_python类与对象的组合与继承
  8. Android 应用开发(39)---GridLayout(网格布局)
  9. Docker 镜像优化:减小镜像尺寸
  10. 【转帖】LoadRunner监控Linux与Windows方法
  11. 海康威视rtsp转rtmp(java稳定版)
  12. QQ截图出现闪退,无法截图(亲测有效)
  13. 神秘诡异的量子世界是如何毁掉科学家三观的?
  14. Mapper 与 Reducer 解析
  15. js对象的三种继承方式
  16. 1-10000的素数 java_java实验题(1-10000之间的素数和)
  17. html标签指定式权重,alternate和Canonical标签防止重复收录分散权重
  18. 我对光学相干层析成像的理解
  19. Go语言如何自定义linter(静态检查工具)
  20. src中的 “/”、“./”与“../”

热门文章

  1. python 游戏大作_使用requests和beautifulsoup爬取3DM单机大作排行榜
  2. OCAD应用:光楔初始设计
  3. mp2格式怎么转换mp3?
  4. java匿名内部类,什么是匿名内部类,如何定义匿名内部类,如何使用匿名内部类?
  5. YV12和I420的区别 yuv420和yuv420p的区别
  6. 【数字电路基础】时序电路和组合电路的区别、为什么要有触发器
  7. U74LVC1G07G-SOT353R-TG
  8. 动态规划多段图和货郎担问题
  9. 剧场版复活的f 剧情
  10. 帕金森病的功能性脑连接障碍:一项5年的纵向研究