#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
@interface ZYAudioManager : NSObject
+ (instancetype)defaultManager;
//播放音乐
- (AVAudioPlayer *)playingMusic:(NSString *)filename;
- (void)pauseMusic:(NSString *)filename;
- (void)stopMusic:(NSString *)filename;
//播放音效
- (void)playSound:(NSString *)filename;
- (void)disposeSound:(NSString *)filename;
@end
#import "ZYAudioManager.h"
@interface ZYAudioManager ()
@property (nonatomic, strong) NSMutableDictionary *musicPlayers;
@property (nonatomic, strong) NSMutableDictionary *soundIDs;
@end
static ZYAudioManager *_instance = nil;
@implementation ZYAudioManager
+ (void)initialize
{
// 音频会话
AVAudioSession *session = [AVAudioSession sharedInstance];
// 设置会话类型(播放类型、播放模式,会自动停止其他音乐的播放)
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
// 激活会话
[session setActive:YES error:nil];
}
+ (instancetype)defaultManager
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
- (instancetype)init
{
__block ZYAudioManager *temp = self;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if ((temp = [super init]) != nil) {
_musicPlayers = [NSMutableDictionary dictionary];
_soundIDs = [NSMutableDictionary dictionary];
}
});
self = temp;
return self;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
//播放音乐
- (AVAudioPlayer *)playingMusic:(NSString *)filename
{
if (filename == nil || filename.length == 0) return nil;
AVAudioPlayer *player = self.musicPlayers[filename]; //先查询对象是否缓存了
if (!player) {
NSURL *url = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
if (!url) return nil;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
if (![player prepareToPlay]) return nil;
self.musicPlayers[filename] = player; //对象是最新创建的,那么对它进行一次缓存
}
if (![player isPlaying]) { //如果没有正在播放,那么开始播放,如果正在播放,那么不需要改变什么
[player play];
}
return player;
}
- (void)pauseMusic:(NSString *)filename
{
if (filename == nil || filename.length == 0) return;
AVAudioPlayer *player = self.musicPlayers[filename];
if ([player isPlaying]) {
[player pause];
}
}
- (void)stopMusic:(NSString *)filename
{
if (filename == nil || filename.length == 0) return;
AVAudioPlayer *player = self.musicPlayers[filename];
[player stop];
[self.musicPlayers removeObjectForKey:filename];
}
//播放音效
- (void)playSound:(NSString *)filename
{
if (!filename) return;
//取出对应的音效ID
SystemSoundID soundID = (int)[self.soundIDs[filename] unsignedLongValue];
if (!soundID) {
NSURL *url = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
if (!url) return;
AudioServicesCreateSystemSoundID((__bridge CFURLRef)(url), &soundID);
self.soundIDs[filename] = @(soundID);
}
// 播放
AudioServicesPlaySystemSound(soundID);
}
//摧毁音效
- (void)disposeSound:(NSString *)filename
{
if (!filename) return;
SystemSoundID soundID = (int)[self.soundIDs[filename] unsignedLongValue];
if (soundID) {
AudioServicesDisposeSystemSoundID(soundID);
[self.soundIDs removeObjectForKey:filename]; //音效被摧毁,那么对应的对象应该从缓存中移除
}
}
@end
#import <XCTest/XCTest.h>
#import "ZYAudioManager.h"
#import <AVFoundation/AVFoundation.h>
@interface ZYAudioManagerTests : XCTestCase
@property (nonatomic, strong) AVAudioPlayer *player;
@end
static NSString *_fileName = @"10405520.mp3";
@implementation ZYAudioManagerTests
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
/**
* 测试是否为单例,要在并发条件下测试
*/
- (void)testAudioManagerSingle
{
NSMutableArray *managers = [NSMutableArray array];
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];
[managers addObject:tempManager];
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];
[managers addObject:tempManager];
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];
[managers addObject:tempManager];
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];
[managers addObject:tempManager];
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
ZYAudioManager *tempManager = [[ZYAudioManager alloc] init];
[managers addObject:tempManager];
});
ZYAudioManager *managerOne = [ZYAudioManager defaultManager];
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[managers enumerateObjectsUsingBlock:^(ZYAudioManager *obj, NSUInteger idx, BOOL * _Nonnull stop) {
XCTAssertEqual(managerOne, obj, @"ZYAudioManager is not single");
}];
});
}
/**
* 测试是否可以正常播放音乐
*/
- (void)testPlayingMusic
{
self.player = [[ZYAudioManager defaultManager] playingMusic:_fileName];
XCTAssertTrue(self.player.playing, @"ZYAudioManager is not PlayingMusic");
}
/**
* 测试是否可以正常停止音乐
*/
- (void)testStopMusic
{
if (self.player == nil) {
self.player = [[ZYAudioManager defaultManager] playingMusic:_fileName];
}
if (self.player.playing == NO) [self.player play];
[[ZYAudioManager defaultManager] stopMusic:_fileName];
XCTAssertFalse(self.player.playing, @"ZYAudioManager is not StopMusic");
}
/**
* 测试是否可以正常暂停音乐
*/
- (void)testPauseMusic
{
if (self.player == nil) {
self.player = [[ZYAudioManager defaultManager] playingMusic:_fileName];
}
if (self.player.playing == NO) [self.player play];
[[ZYAudioManager defaultManager] pauseMusic:_fileName];
XCTAssertFalse(self.player.playing, @"ZYAudioManager is not pauseMusic");
}
@end
#import "ZYMusicViewController.h"
#import "ZYPlayingViewController.h"
#import "ZYMusicTool.h"
#import "ZYMusic.h"
#import "ZYMusicCell.h"
@interface ZYMusicViewController ()
@property (nonatomic, strong) ZYPlayingViewController *playingVc;
@property (nonatomic, assign) int currentIndex;
@end
@implementation ZYMusicViewController
- (ZYPlayingViewController *)playingVc
{
if (_playingVc == nil) {
_playingVc = [[ZYPlayingViewController alloc] initWithNibName:@"ZYPlayingViewController" bundle:nil];
}
return _playingVc;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setupNavigation];
}
- (void)setupNavigation
{
self.navigationItem.title = @"音乐播放器";
}
#pragma mark ----TableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [ZYMusicTool musics].count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
ZYMusicCell *cell = [ZYMusicCell musicCellWithTableView:tableView];
cell.music = [ZYMusicTool musics][indexPath.row];
return cell;
}
#pragma mark ----TableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 70;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[ZYMusicTool setPlayingMusic:[ZYMusicTool musics][indexPath.row]];
ZYMusic *preMusic = [ZYMusicTool musics][self.currentIndex];
preMusic.playing = NO;
ZYMusic *music = [ZYMusicTool musics][indexPath.row];
music.playing = YES;
NSArray *indexPaths = @[
[NSIndexPath indexPathForItem:self.currentIndex inSection:0],
indexPath
];
[self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
self.currentIndex = (int)indexPath.row;
[self.playingVc show];
}
@end
//得到挪动距离 CGPoint point = [sender translationInView:sender.view]; //将translation清空,免得重复叠加 [sender setTranslation:CGPointZero inView:sender.view];
#import <UIKit/UIKit.h>
@interface ZYLrcView : UIImageView
@property (nonatomic, assign) NSTimeInterval currentTime;
@property (nonatomic, copy) NSString *fileName;
@end
#import "ZYLrcView.h"
#import "ZYLrcLine.h"
#import "ZYLrcCell.h"
#import "UIView+AutoLayout.h"
@interface ZYLrcView () <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, weak) UITableView *tableView;
@property (nonatomic, strong) NSMutableArray *lrcLines;
/**
* 记录当前显示歌词在数组里面的index
*/
@property (nonatomic, assign) int currentIndex;
@end
@implementation ZYLrcView
#pragma mark ----setter\geter方法
- (NSMutableArray *)lrcLines
{
if (_lrcLines == nil) {
_lrcLines = [ZYLrcLine lrcLinesWithFileName:self.fileName];
}
return _lrcLines;
}
- (void)setFileName:(NSString *)fileName
{
if ([_fileName isEqualToString:fileName]) {
return;
}
_fileName = [fileName copy];
[_lrcLines removeAllObjects];
_lrcLines = nil;
[self.tableView reloadData];
}
- (void)setCurrentTime:(NSTimeInterval)currentTime
{
if (_currentTime > currentTime) {
self.currentIndex = 0;
}
_currentTime = currentTime;
int minute = currentTime / 60;
int second = (int)currentTime % 60;
int msecond = (currentTime - (int)currentTime) * 100;
NSString *currentTimeStr = [NSString stringWithFormat:@"d:d.d", minute, second, msecond];
for (int i = self.currentIndex; i < self.lrcLines.count; i++) {
ZYLrcLine *currentLine = self.lrcLines[i];
NSString *currentLineTime = currentLine.time;
NSString *nextLineTime = nil;
if (i + 1 < self.lrcLines.count) {
ZYLrcLine *nextLine = self.lrcLines[i + 1];
nextLineTime = nextLine.time;
}
if (([currentTimeStr compare:currentLineTime] != NSOrderedAscending) && ([currentTimeStr compare:nextLineTime] == NSOrderedAscending) && (self.currentIndex != i)) {
NSArray *reloadLines = @[
[NSIndexPath indexPathForItem:self.currentIndex inSection:0],
[NSIndexPath indexPathForItem:i inSection:0]
];
self.currentIndex = i;
[self.tableView reloadRowsAtIndexPaths:reloadLines withRowAnimation:UITableViewRowAnimationNone];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:self.currentIndex inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}
}
#pragma mark ----初始化方法
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self commitInit];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
[self commitInit];
}
return self;
}
- (void)commitInit
{
self.userInteractionEnabled = YES;
self.image = [UIImage imageNamed:@"28131977_1383101943208"];
self.contentMode = UIViewContentModeScaleToFill;
self.clipsToBounds = YES;
UITableView *tableView = [[UITableView alloc] init];
tableView.delegate = self;
tableView.dataSource = self;
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
tableView.backgroundColor = [UIColor clearColor];
self.tableView = tableView;
[self addSubview:tableView];
[self.tableView autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
}
#pragma mark ----UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.lrcLines.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
ZYLrcCell *cell = [ZYLrcCell lrcCellWithTableView:tableView];
cell.lrcLine = self.lrcLines[indexPath.row];
if (indexPath.row == self.currentIndex) {
cell.textLabel.font = [UIFont boldSystemFontOfSize:16];
}
else{
cell.textLabel.font = [UIFont systemFontOfSize:13];
}
return cell;
}
- (void)layoutSubviews
{
[super layoutSubviews];
// NSLog(@"++++++++++%@",NSStringFromCGRect(self.tableView.frame));
self.tableView.contentInset = UIEdgeInsetsMake(self.frame.size.height / 2, 0, self.frame.size.height / 2, 0);
}
@end
机械节能产品生产企业官网模板...
大气智能家居家具装修装饰类企业通用网站模板...
礼品公司网站模板
宽屏简约大气婚纱摄影影楼模板...
蓝白WAP手机综合医院类整站源码(独立后台)...苏ICP备2024110244号-2 苏公网安备32050702011978号 增值电信业务经营许可证编号:苏B2-20251499 | Copyright 2018 - 2025 源码网商城 (www.ymwmall.com) 版权所有