iOS_36声词同步

最终效果图:




模型

//
//  Sentence.h
//  36_声词同步
//
//  Created by beyond on 14-9-12.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
//  模型,句子

#import <Foundation/Foundation.h>

@interface Sentence : NSObject

// 文字
@property (nonatomic, copy) NSString *text;
// 在音频文件中,朗诵开始的时间
@property (nonatomic, assign) double startTime;
@end

控制器

//
//  BeyondController.m
//  36_声词同步
//
//  Created by beyond on 14-9-12.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
//

#import "BeyondController.h"

#import "Sentence.h"

#import "SongTool.h"


@interface BeyondController ()
// 句子对象数组
@property (nonatomic, strong) NSArray *sentenceArr;
// 开始播放就开启时钟,监听 音乐的播放进度
@property (nonatomic, strong) CADisplayLink *link;
// 音乐播放器 可以获得当前播放的时间 【currentTime】
@property (nonatomic, strong) AVAudioPlayer *sentencePlayer;
@end

@implementation BeyondController

// 懒加载,需要时才创建 时钟
- (CADisplayLink *)link
{
    if (!_link) {
        // 绑定时钟方法
        self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
    }
    return _link;
}

// 懒加载,需要时才创建 句子对象数组,从Plist中的字典数组,转成对象数组
- (NSArray *)sentenceArr
{
    if (!_sentenceArr) {
         self.sentenceArr = [Sentence objectArrayWithFilename:@"redStory.plist"];
    }
    return _sentenceArr;
}


- (void)viewDidLoad
{
    [super viewDidLoad];
    // 工具类播放音乐,并用成员变量记住 创建的播放器对象【可拿到播放的currentTime】
    self.sentencePlayer = [SongTool playMusic:@"一东.mp3"];
    // 播放背景音乐
    [SongTool playMusic:@"Background.caf"];
    // 同时,开启时钟
    [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0);
}
#pragma mark - 时钟方法
// 重点~~~~时钟绑定的方法
- (void)update
{
    // 获取当前播放的位置(第多少秒,比如第10秒)
    double currentTime = self.sentencePlayer.currentTime;
    // 遍历,找出对应的一句
    int count = self.sentenceArr.count;
    for (int i = 0; i<count; i++) {
        // 1.当前词句
        Sentence *s = self.sentenceArr[i];
        
        // 2.获得下一条句子对象
        int next = i + 1;
        Sentence *nextS = nil;
        // 需防止越界
        if (next < count) {
            nextS = self.sentenceArr[next];
        }
        // 3.关键判断,如果当前播放的时间,大于i对应的时间,并且小于i+1对应的时间
        if (currentTime >= s.startTime && currentTime < nextS.startTime) {
            // 选中并高亮对应行的文字
            NSIndexPath *path = [NSIndexPath indexPathForRow:i inSection:0];
            [self.tableView selectRowAtIndexPath:path animated:YES scrollPosition:UITableViewScrollPositionTop];
            break;
        }
    }
}
#pragma mark - tableView数据源方法
// 共多少行,即多少句
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.sentenceArr.count;
}
// 每一行的独一无二的文字
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 1.创建cell
    static NSString *cellID = @"Sentence";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID];
    }
    
    // 2.设置cell  独一无二的数据
    Sentence *s = self.sentenceArr[indexPath.row];
    cell.textLabel.text = s.text;
    
    return cell;
}


@end


音乐播放工具类

//
//  SongTool.h
//  36_声词同步
//
//  Created by beyond on 14-9-12.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
//

#import <Foundation/Foundation.h>
// 音乐工具类,必须导入AVFoundation的主头文件
#import <AVFoundation/AVFoundation.h>

@interface SongTool : NSObject



// 类方法, 播放音乐,  参数:音乐文件名 如@"a.mp3",同时为了能够给播放器AVAudioPlayer对象设置代理,在创建好播放器对象后,将其返回给调用者
+ (AVAudioPlayer *)playMusic:(NSString *)filename;

// 类方法, 暂停音乐,  参数:音乐文件名 如@"a.mp3"
+ (void)pauseMusic:(NSString *)filename;

// 类方法, 停止音乐,  参数:音乐文件名 如@"a.mp3",记得从字典中移除
+ (void)stopMusic:(NSString *)filename;

// 返回当前正在播放的音乐播放器,方便外界控制其快进,后退或其他方法
+ (AVAudioPlayer *)currentPlayingAudioPlayer;

@end

//
//  SongTool.m
//  36_声词同步
//
//  Created by beyond on 14-9-12.
//  Copyright (c) 2014年 com.beyond. All rights reserved.
//

#import "SongTool.h"

@implementation SongTool
// 字典,存放所有的音乐播放器,键是:音乐名,值是对应的音乐播放器对象audioPlayer
// 一首歌对应一个音乐播放器
static NSMutableDictionary *_audioPlayerDict;

#pragma mark - Life Cycle
+ (void)initialize
{
    // 字典,键是:音乐名,值是对应的音乐播放器对象
    _audioPlayerDict = [NSMutableDictionary dictionary];
    
    // 设置后台播放
    [self sutupForBackgroundPlay];
}

// 设置后台播放
+ (void)sutupForBackgroundPlay
{
    // 后台播放三步曲之第三步,设置 音频会话类型
    AVAudioSession *session = [AVAudioSession sharedInstance];
    // 类型是:播放和录音
    [session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
    // 而且要激活 音频会话
    [session setActive:YES error:nil];
}

#pragma mark - 供外界调用
// 类方法, 播放音乐,  参数:音乐文件名 如@"a.mp3"
// 同时为了能够给播放器AVAudioPlayer对象设置代理,在创建好播放器对象后,将其返回给调用者
+ (AVAudioPlayer *)playMusic:(NSString *)filename
{
    // 健壮性判断
    if (!filename) return nil;
    
    // 1.先从字典中,根据音乐文件名,取出对应的audioPlayer
    AVAudioPlayer *audioPlayer = _audioPlayerDict[filename];
    if (!audioPlayer) {
        // 如果没有,才需创建对应的音乐播放器,并且存入字典
        // 1.1加载音乐文件
        NSURL *url = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
        // 健壮性判断
        if (!url) return nil;
        
        // 1.2根据音乐的URL,创建对应的audioPlayer
        audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
        
        // 1.3开始缓冲
        [audioPlayer prepareToPlay];
        // 如果要实现变速播放,必须同时设置下面两个参数
        //        audioPlayer.enableRate = YES;
        //        audioPlayer.rate = 10.0;
        
        // 1.4最后,放入字典
        _audioPlayerDict[filename] = audioPlayer;
    }
    
    // 2.如果是暂停或停止时,才需要开始播放
    if (!audioPlayer.isPlaying) {
        [audioPlayer play];
    }
    // 3.返回创建好的播放器,方便调用者设置代理,监听播放器的进度currentTime
    return audioPlayer;
}

// 类方法, 暂停音乐,  参数:音乐文件名 如@"a.mp3"
+ (void)pauseMusic:(NSString *)filename
{
    // 健壮性判断
    if (!filename) return;
    
    // 1.先从字典中,根据key【文件名】,取出audioPlayer【肯定 有 值】
    AVAudioPlayer *audioPlayer = _audioPlayerDict[filename];
    
    // 2.如果是正在播放,才需要暂停
    if (audioPlayer.isPlaying) {
        [audioPlayer pause];
    }
}

// 类方法, 停止音乐,  参数:音乐文件名 如@"a.mp3",记得从字典中移除
+ (void)stopMusic:(NSString *)filename
{
    // 健壮性判断
    if (!filename) return;
    
    // 1.先从字典中,根据key【文件名】,取出audioPlayer【肯定 有 值】
    AVAudioPlayer *audioPlayer = _audioPlayerDict[filename];
    
    // 2.如果是正在播放,才需要停止
    if (audioPlayer.isPlaying) {
        // 2.1停止
        [audioPlayer stop];
        
        // 2.2最后,记得从字典中移除
        [_audioPlayerDict removeObjectForKey:filename];
    }
}

// 返回当前正在播放的音乐播放器,方便外界控制其快进,后退或其他方法
+ (AVAudioPlayer *)currentPlayingAudioPlayer
{
    // 遍历字典的键,再根据键取出值,如果它是正在播放,则返回该播放器
    for (NSString *filename in _audioPlayerDict) {
        AVAudioPlayer *audioPlayer = _audioPlayerDict[filename];
        
        if (audioPlayer.isPlaying) {
            return audioPlayer;
        }
    }
    
    return nil;
}


@end












  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值