2017-06-26技术周报

本期周报内容来自: nololDemoCooky

时光机:周报汇总周报博客iOS puzzles


iOS

1. iOS多线程读写崩溃分析:【推荐】

2. AFNetworking之于https认证

3.AFNetworking 3.0与服务端 自签名证书 https双向认证:对https双向认证讲解比较详细,感兴趣的同学可以看看

组件化

1.微信终端跨平台组件 mars 系列(一) - 高性能日志模块xlog

2.微信终端跨平台组件 mars 系列(二) - 信令传输超时设计

3.微信iOS SQLite源码优化实践

开源

1.33 个 2017 年必须了解的 iOS 开源库 :持续更新,建议收藏哦~

2017-05-22技术周报

本期周报内容来自: nololDemoCooky

时光机:周报汇总周报博客iOS puzzles


Swift

1.JSONNeverDie 使用指南:JSONNeverDie 是一个 Swift 写的 JSON 编码、解码库,利用反射实现 JSON 到 Model 的自动映射,无需任何 parse 代码,强大而简单。【推荐】

2.swift-style-guide : wift编程风格指南

Object-C

1.ThreadSafeClassDesign : 线程安全【推荐】

iOS

1. 关于内存泄漏,还有哪些是你不知道的?:【推荐】

2.Lottie初级教程:打造iOS APP完美动画

3.iOS进阶(一)block与property

4.iOS进阶(二)Objective-C底层原理

开源

1.Github上关于iOS的各种开源项目集合 :建议收藏哦~

2017-05-07技术周报

本期周报内容来自: eggInBlackDemoCooky

时光机:周报汇总周报博客iOS puzzles


iOS–UI性能优化

近期组内黑蛋童鞋在搞UI优化(赞美黑蛋),本期主题以此为主 ^_^

1. iOS9中UIStackView的使用

2.iOS图片加载速度极限优化—FastImageCache解析

3.On the importance of setting shadowPath 关于阴影的绘制

4.小科普:脑洞大开:为啥帧率达到 60 fps 就流畅:【推荐】

5.从 Auto Layout 的布局算法谈性能

6.iOS图形原理与离屏渲染:【推荐】

iOS–数据缓存优化

1. APP 缓存数据线程安全问题探讨:介绍了两种解决APP缓存数据线程安全问题的方案

博客

1.iOS很好的开源库 :一个开源库全家桶~赞美

2017-4-09技术周报

本期周报内容来自: GlareCHDemoCookynolol

时光机:周报汇总周报博客iOS puzzles


好啦~清明过后,也该收收心继续前行了~本期内容同样精彩 速速来围观吧^_^

Swift

1.Swift Talk :推荐一个不错的swift学习网站

iOS

1.舞动的表情包——浅析GIF格式图片的存储和压缩

2.iOS持续集成大比拼:Xcode Server、Jenkins、Travis和fastlane

3.【iOS程序启动与运转】- RunLoop个人小结 [推荐]

4.iOS App 签名的原理

开源库

介绍一个内存泄露检测工具:

1.MLeaksFinder:精准 iOS 内存泄露检测工具

团队原创之AV Foundation-AVAudioPlayer

转自東引瓯越 技术博客

开篇

最近在学习AV Foundation 试图把学习内容记录下来 并参考一些博客文章
本期的内容是AVAudioPlayer

音频知识基础

音频文件的生成过程是将声音信息采样量化编码产生的数字信号的过程,人耳所能听到的声音,最低的频率是从20Hz起一直到最高频率20KHZ,因此音频文件格式的最大带宽是20KHZ。根据奈奎斯特的理论,只有采样频率高于声音信号最高频率的两倍时,才能把数字信号表示的声音还原成为原来的声音,所以音频文件的采样率一般在40~50KHZ,比如最常见的CD音质采样率44.1KHZ。 (所以一般大家都觉得CD音质是最好的.) 对声音进行采样、量化过程被称为脉冲编码调制(Pulse Code Modulation),简称PCM。PCM数据是最原始的音频数据完全无损,所以PCM数据虽然音质优秀但体积庞大,为了解决这个问题先后诞生了一系列的音频格式,这些音频格式运用不同的方法对音频数据进行压缩,其中有无损压缩(ALAC、APE、FLAC)和有损压缩(MP3、AAC、OGG、WMA)两种 来源:iOS音频播放 (一):概述 by 码农人生

我觉得程寅大牛的处理音频说的很明白
大神列出一个经典的音频播放流程(以MP3为例)

  1. 读取MP3文件
  2. 解析采样率、码率、时长等信息,分离MP3中的音频帧
  3. 对分离出来的音频帧解码得到PCM数据
  4. 对PCM数据进行音效处理(均衡器、混响器等,非必须)
  5. 把PCM数据解码成音频信号
  6. 把音频信号交给硬件播放
  7. 重复1-6步直到播放完成

在iOS系统中apple对上述的流程进行了封装并提供了不同层次的接口

这是CoreAudio的接口层次

下面对其中的中高层接口进行功能说明:

  • Audio File Services:读写音频数据,可以完成播放流程中的第2步;
  • Audio File Stream Services:对音频进行解码,可以完成播放流程中的第2步;
  • Audio Converter services:音频数据转换,可以完成播放流程中的第3步;
  • Audio Processing Graph Services:音效处理模块,可以完成播放流程中的第4步;
  • Audio Unit Services:播放音频数据:可以完成播放流程中的第5步、第6步;
  • Extended Audio File Services:Audio File Services和Audio
  • Converter services的结合体;
  • AVAudioPlayer/AVPlayer(AVFoundation):高级接口,可以完成整个音频播放的过程(包括本地文件和网络流播放,第4步除外);
  • Audio Queue Services:高级接口,可以进行录音和播放,可以完成播放流程中的第3、5、6步;
  • OpenAL:用于游戏音频播放,暂不讨论

可以看到apple提供的接口类型非常丰富,可以满足各种类别类需求:

  • 如果你只是想实现音频的播放,没有其他需求AVFoundation会很好的满足你的需求。它的接口使用简单、不用关心其中的细节;

  • 如果你的app需要对音频进行流播放并且同时存储,那么AudioFileStreamer加AudioQueue能够帮到你,你可以先把音频数据下载到本地,一边下载一边用NSFileHandler等接口读取本地音频文件并交给AudioFileStreamer或者AudioFile解析分离音频帧,分离出来的音频帧可以送给AudioQueue进行解码和播放。如果是本地文件直接读取文件解析即可。(这两个都是比较直接的做法,这类需求也可以用AVFoundation+本地server的方式实现,AVAudioPlayer会把请求发送给本地server,由本地server转发出去,获取数据后在本地server中存储并转送给AVAudioPlayer。另一个比较trick的做法是先把音频下载到文件中,在下载到一定量的数据后把文件路径给AVAudioPlayer播放,当然这种做法在音频seek后就回有问题了。)

  • 如果你正在开发一个专业的音乐播放软件,需要对音频施加音效(均衡器、混响器),那么除了数据的读取和解析以外还需要用到AudioConverter来把音频数据转换成PCM数据,再由AudioUnit+AUGraph来进行音效处理和播放(但目前多数带音效的app都是自己开发音效模块来坐PCM数据的处理,这部分功能自行开发在自定义性和扩展性上会比较强一些。PCM数据通过音效器处理完成后就可以使用AudioUnit播放了,当然AudioQueue也支持直接使对PCM数据进行播放。)。下图描述的就是使用AudioFile + AudioConverter + AudioUnit进行音频播放的流程

以上内容均转自码农人生 希望大神不要介意 如果有问题 我可立即清除

使用AVAudioPlayer之前对AudioSession简介

AVAudioSession负责管理音频会话 它是个单例 在应用程序和操作系统之间负责中间人的角色 AudioSession参考

AVAudioSession主要功能包括以下几点:

  • app是如何使用的音频服务 播放 还是录制 之类的
  • 控制协调app输入输出设备(比如 麦克风,耳机、手机外放比如蓝牙连接一个外置音响 或airplay)
  • 协调你的app的音频播放和系统以及其他app行为(例如有电话时需要打断,电话结束时需要恢复,按下静音按钮时是否歌曲也要静音等)

注:AVAudioSession iOS6以后使用 以前叫AudioSession__

如何使用AVAudioPlayer

在我的博客里面我尽量使用code胜过千言万语
使用AVAudioPlayer之前需要在AppDelegate里面导入#import <AVFoundation/AVFoundation.h>
并且启动音频会话

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error;
if (![session setCategory:AVAudioSessionCategoryPlayback error:&error]) {
NSLog(@"Category Error: %@", [error localizedDescription]);
}
if (![session setActive:YES error:&error]) {
NSLog(@"Activation Error: %@", [error localizedDescription]);
}
return YES;
}

上边已经介绍了AVAudioSession

这里面说一下[session setCategory:AVAudioSessionCategoryPlayback error:&error] 里面的AVAudioSessionCategoryPlayback

音频会话分类

这是这几种分类的列表大家可以看下

记得开启后台播放

或者在plist里面修改

下面就是创建音频播放器代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#import "ViewController.h"
#import <Masonry/Masonry.h>
#import "THControlKnob.h"
#import "THPlayButton.h"
#import <AVFoundation/AVFoundation.h>
@interface ViewController ()
//三个控制推子
@property (weak, nonatomic) IBOutlet THOrangeControlKnob *panKnob;
@property (weak, nonatomic) IBOutlet THOrangeControlKnob *volumnKnob;
@property (weak, nonatomic) IBOutlet THGreenControlKnob *rateKnob;
@property (weak, nonatomic) IBOutlet THPlayButton *playButton;
//音乐播放器
@property (nonatomic, strong) AVAudioPlayer *musicPlayer;
@property (nonatomic, getter = isPlaying) BOOL playing; //播放状态
//无关代码
@property (weak, nonatomic) IBOutlet UILabel *LeftRightRoundDec;
@property (weak, nonatomic) IBOutlet UILabel *voiceDec;
@property (weak, nonatomic) IBOutlet UILabel *rateDec;
@property (weak, nonatomic) IBOutlet UILabel *trackDescrption;
@end

导入几个第三方控件的类用于音乐播放

这上边的三个旋钮就是导入的开源库

下面创建播放器AVAudioPlayer
创建时需要一个NSURL代表要播放的文件路径 这里简单从bundle中拖了一首歌进去了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#pragma mark -
#pragma mark - 创建AVAudioPlayer与播放状态控制
/**
创建音乐播放器
@param fileName 文件名
@param fileExtension 文件扩展名
@return 播放器实例
*/
- (AVAudioPlayer *)createPlayForFile:(NSString *)fileName
withExtension:(NSString *)fileExtension{
NSURL *url = [[NSBundle mainBundle] URLForResource:fileName withExtension:fileExtension];
NSError *error = nil;
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
if (audioPlayer) {
audioPlayer.numberOfLoops = -1; //-1无限循环
audioPlayer.enableRate = YES; //启动倍速控制
[audioPlayer prepareToPlay];
} else {
NSLog(@"Error creating player: %@",[error localizedDescription]);
}
return audioPlayer;
}

numberOfLoops = -1; 代表本首歌 无限循环 其它常数代表循环次数
enableRate 代表是否启用倍速调节 0.5x 1.0x 2.0x 等倍速 1.0代表正常速度

这里说一下[audioPlayer prepareToPlay]
调用这个函数是为了取得需要的音频硬件并预加载Audio Queue的缓冲区. 当然也可以不调用这个方法直接调用 [audioPlayer play],但当 调用play方法时也会隐性激活,调用prepareToPlay是为了减少 创建播放器时预设加载和听到声音输出之间的延时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@implementation ViewController
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
if (self.musicPlayer == nil) {
self.musicPlayer = [self createPlayForFile:@"384551_1438267683" withExtension:@"mp3"];
}
[self setupNotifications];
}
return self;
}
- (void)awakeFromNib{
[super awakeFromNib];
if (self.musicPlayer == nil) {
self.musicPlayer = [self createPlayForFile:@"384551_1438267683" withExtension:@"mp3"];
}
[self setupNotifications];
}

initWithNibNameawakeFromNib时候调用一下创建播放器的代码
这个[self setupNotifications];后面说

先添加一些常见的方法封装 比如 播放、暂停、停止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
- (void)play {
if (self.musicPlayer == nil) { return; }
if (!self.playing) {
NSTimeInterval delayTime = [self.musicPlayer deviceCurrentTime] + 0.01;
[self.musicPlayer playAtTime:delayTime];
self.playing = YES;
}
self.trackDescrption.text = [self.musicPlayer.url absoluteString];
[self configNowPlayingInfoCenter]; //配置后台播放的页面信息
}
- (void)stop {
if (self.musicPlayer == nil) { return; }
if (self.playing) {
[self.musicPlayer stop];
self.musicPlayer.currentTime = 0.0f;
self.playing = NO;
}
}
- (void)pause {
if (self.musicPlayer == nil) { return; }
if (self.playing) {
[self.musicPlayer pause];
self.playing = NO;
}
}

这里看到[self.musicPlayer deviceCurrentTime] + 0.01 加了 -0.01的延时, 是为了以后大家做播放器的时候 有可能暂停或者歌曲切换时 有可能 向前向后做片段衔接, 也是为了使用 playAtTime去播放 指定位置的音乐用于 意外暂停或者播放上次播放的配置信息使用 这里看到我写了一个
[self configNowPlayingInfoCenter];配置后台播放的页面信息
这个主要用于播放音乐在后台时 锁屏显示的屏幕信息 请看下面代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//设置锁屏状态,显示的歌曲信息
-(void)configNowPlayingInfoCenter{
if (NSClassFromString(@"MPNowPlayingInfoCenter")) {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
//歌曲名称
[dict setObject:@"歌曲名称" forKey:MPMediaItemPropertyTitle];
//演唱者
[dict setObject:@"演唱者" forKey:MPMediaItemPropertyArtist];
//专辑名
[dict setObject:@"专辑名" forKey:MPMediaItemPropertyAlbumTitle];
//专辑缩略图
UIImage *image = [UIImage imageNamed:@"sunyazhou"];
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:image];
[dict setObject:artwork forKey:MPMediaItemPropertyArtwork];
//音乐剩余时长
[dict setObject:@20 forKey:MPMediaItemPropertyPlaybackDuration];
//音乐当前播放时间 在计时器中修改
// [dict setObject:[NSNumber numberWithDouble:100.0] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
//设置锁屏状态下屏幕显示播放音乐信息
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];
}
}

如果需要在计时器中不断刷新锁屏状态下的播放进度条请写如下代码

1
2
3
4
5
6
7
8
9
10
//计时器修改进度
- (void)changeProgress:(NSTimer *)sender{
if(self.player){
//当前播放时间
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:[[MPNowPlayingInfoCenter defaultCenter] nowPlayingInfo]];
[dict setObject:[NSNumber numberWithDouble:self.player.currentTime] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime]; //音乐当前已经过时间
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];
}
}

参考IOS后台运行 之 后台播放音乐

下面我们来介绍一下
[self setupNotifications];注册监听 音频意外中断和耳机拔出时要暂停音乐播放
实现代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
播放的通知处理
*/
- (void)setupNotifications {
NSNotificationCenter *nsnc = [NSNotificationCenter defaultCenter];
//添加意外中断音频播放的通知
[nsnc addObserver:self
selector:@selector(handleInterruption:)
name:AVAudioSessionInterruptionNotification
object:[AVAudioSession sharedInstance]];
//添加线路变化通知
[nsnc addObserver:self
selector:@selector(hanldeRouteChange:)
name:AVAudioSessionRouteChangeNotification
object:[AVAudioSession sharedInstance]];
}

注:记得在delloc里面[[NSNotificationCenter defaultCenter] removeObserver:self]

意外中断音频发生的场景 例如 听歌过程中来电话或者 按住home键使用siri

下面是具体方法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
音频意外打断处理
@param notification 通知信息
*/
- (void)handleInterruption:(NSNotification *)notification {
NSDictionary *info = notification.userInfo;
AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
if (type == AVAudioSessionInterruptionTypeBegan) {
//Handle AVAudioSessionInterruptionTypeBegan
[self pause];
} else {
//Handle AVAudioSessionInterruptionTypeEnded
AVAudioSessionInterruptionOptions options = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
NSError *error = nil;
//激活音频会话 允许外接音响
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
[[AVAudioSession sharedInstance] setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];
if (options == AVAudioSessionInterruptionOptionShouldResume) {
[self play];
} else {
[self play];
}
self.playButton.selected = YES;
if (error) {
NSLog(@"AVAudioSessionInterruptionOptionShouldResume失败:%@",[error localizedDescription]);
}
}
}

先说handleInterruption意外情况下中断比如我按住home键使用siri
我会收到意外打断的通知当 type == AVAudioSessionInterruptionTypeBegan时 我们停止音乐播放或者暂停.
当type != AVAudioSessionInterruptionTypeBegan的时候一定是AVAudioSessionInterruptionTypeEnded这个时候notification.userInfo里面包含一个AVAudioSessionInterruptionOptions值来表明音频会话是否已经重新激活以及是否可以再次播放

注:这个地方遇到个坑 当意外中断时候有时音频会话会很不灵敏 后来发现这种情况下需要重新激活会话 如下代码:

1
[[AVAudioSession sharedInstance] setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];

这里AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation是为了通知其它应用会话被我激活了 很多播放器开发者很不讲究 每次从来不用这个方法导致每次别人播放完音频 自己都收不到音频重新播放的信息 建议大家以和为贵, 写良心代码.

因为我外接的小米蓝牙音响发现还是不好使 最后又补上了AVAudioSessionCategoryOptionAllowBluetooth这个

激活音频会话 允许外接音响

1
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];

就好使了

下面说一下耳机插拔或者USB麦克风断开 Apple有个什么Human Interface Guidelines(HIG)相关定义 意思是说当硬件耳机拔出时建议 暂停播放音乐或者麦克风断开时。就是处于静音状态。是为了保密播放内容不被外界听到,不管苹果啥规定 我们都得照办 否则就得被拒。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)hanldeRouteChange:(NSNotification *)notification {
NSDictionary *info = notification.userInfo;
AVAudioSessionRouteChangeReason reason = [info[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
//老设备不可用
if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
AVAudioSessionRouteDescription *previousRoute = info[AVAudioSessionRouteChangePreviousRouteKey];
AVAudioSessionPortDescription *previousOutput = previousRoute.outputs[0];
NSString *portType = previousOutput.portType;
if ([portType isEqualToString:AVAudioSessionPortHeadphones]) {
[self stop];
self.playButton.selected = NO;
}
}
}

这需要用AVAudioSessionRouteChangeReasonKey取出线路切换的原因AVAudioSessionRouteChangeReason 原因有这么多

1
2
3
4
5
6
7
8
9
10
11
typedef NS_ENUM(NSUInteger, AVAudioSessionRouteChangeReason)
{
AVAudioSessionRouteChangeReasonUnknown = 0,
AVAudioSessionRouteChangeReasonNewDeviceAvailable = 1,
AVAudioSessionRouteChangeReasonOldDeviceUnavailable = 2,
AVAudioSessionRouteChangeReasonCategoryChange = 3,
AVAudioSessionRouteChangeReasonOverride = 4,
AVAudioSessionRouteChangeReasonWakeFromSleep = 6,
AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory = 7,
AVAudioSessionRouteChangeReasonRouteConfigurationChange NS_ENUM_AVAILABLE_IOS(7_0) = 8
} NS_AVAILABLE_IOS(6_0);

我们需要这个AVAudioSessionRouteChangeReasonOldDeviceUnavailable 判断是否是旧设备
通过AVAudioSessionRouteChangePreviousRouteKey拿出

AVAudioSessionRouteDescription描述信息
previousRoute 在通过
previousRoute.outputs[0]拿出AVAudioSessionPortDescription

拿出NSString *portType = previousOutput.portType

如果[portType isEqualToString:AVAudioSessionPortHeadphones]

如果是耳机AVAudioSessionPortHeadphones则暂停播放

以上就是中断和线路切换的一些代码逻辑

下面我介绍一些好玩的

前面说的一些后台设置信息显示的内容就是上图所示 在锁屏的时候显示

但是大家一定很奇怪的是怎么实现接收 锁屏状态下 点击 上一曲 暂停/播放 下一曲等操作

需要在AppDelegate里面写上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error;
if (![session setCategory:AVAudioSessionCategoryPlayback error:&error]) {
NSLog(@"Category Error: %@", [error localizedDescription]);
}
if (![session setActive:YES error:&error]) {
NSLog(@"Activation Error: %@", [error localizedDescription]);
}
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
return YES;
}

[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
行代码 以及调用自己为 [self becomeFirstResponder];第一响应者 这样写是为了应用响应音频播放 后台切换或者中断的时候更灵敏.

1
2
3
- (BOOL)canBecomeFirstResponder {
return YES;
}

然后 写上如下代码 处理锁屏状态下 点击 上一曲 暂停/播放 下一曲等操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
if (event.type == UIEventTypeRemoteControl) {
switch (event.subtype) {
case UIEventSubtypeRemoteControlPlay:
NSLog(@"暂停播放");
break;
case UIEventSubtypeRemoteControlPause:
NSLog(@"继续播放");
break;
case UIEventSubtypeRemoteControlNextTrack:
NSLog(@"下一曲");
break;
case UIEventSubtypeRemoteControlPreviousTrack:
NSLog(@"上一曲");
break;
default:
break;
}
}
}

剩余逻辑大家自己填充吧我就不介绍了.

好了AVAudioPlayer就到这吧!有啥疑问大家可以评论留言都能看到或者指正我的错误。我会及时改正.

全文完

文章的最终demo

2017-03-26技术周报

本期周报内容来自: 孙亚洲GlareCHDemoCookynolol

时光机:周报汇总周报博客iOS puzzles


Swift

1.CallKit 之旅 :swift CallKit 之旅

2.简单点,理解iOS与函数式编程

iOS

本期iOS相关主要集中介绍一下AV Foundation框架,并附上团队同学孙亚洲的一篇原创博客(感谢~~),没错,就是上一篇~ 希望对你有所帮助!

开源库

介绍两个Swfit中管理资源的开源库:

1.R.swift : 在Swift中更安全、方便的使用各类资源

2.SwiftGen: SwiftGen是一个为你的项目资源自动生成代码的工具

3.SwiftTask: Promise + progress + pause + cancel + retry for Swift.

2017-03-18技术周报

本期周报内容来自: GlareCHDemoCookynolol

时光机:周报汇总周报博客iOS puzzles


Swift

1.GodEye:基于Swift的日志监控系统 :GodEye是一款基于Swift的功能强大的监控开源库,笔者在文末也推荐了自己写的一本书《iOS监控编程》,想深入了解监控方向的童鞋可以尝试。

2.Swift 3使用GCD和DispatchQueues —>翻译版戳我

iOS

1. iOS中如何避免图像解压缩的时间开销 —>翻译版戳我

2.正确使用@synchronized()

扩展

本期扩展内容主要涉足算法领域,看完后智商+2[手动滑稽]

1.simhash算法,计算文本相似度

2.深度学习在美团点评的应用

3.浅谈指数退避算法:退避算法就是网络上的节点在发送数据冲突后,等待一定时间后再发,等待时间是随指数增长,从而避免频繁的触发冲突.

2017-03-11技术周报

本期周报内容来自: GlareCHDemoCookyhappyappernololjenglev

时光机:周报汇总周报博客iOS puzzles


关于Swift

1.无论你是否主力 Swift, 这些事情你都可以了解一下 :聊一聊关于Swift 能不能取代 Objective-C

2.The Official raywenderlich.com Swift Style Guide : 更具有风格的Swift3 指南【推荐】

iOS–实战

1.[Swift] 导航栏的平滑显示和隐藏 【推荐】

2.[Object-C] 今日头条iOS客户端启动速度优化

单测

1.如何进行 HTTP Mock:HTTP Mock 在测试中非常好用,我们可以在不需要后端 API 的情况下,在本地对 HTTP 请求进行拦截,返回想要的 json 数据,而 OHHTTPStubs 就为我们提供了这样一种解决方案。【推荐】

源码

1.Swifty Time : 一个收录了大量swift优秀源码的网站 【推荐】

2.iOS开发之Alamofire源码解析前奏–NSURLSession全家桶

扩展

1.Google QUIC 协议:从 TCP 到 UDP 的 Web 平台 :QUIC(Quick UDP Internet Connections)协议是一种全新的基于UDP的web开发协议,开创性的使用了UDP协议作为底层传输协议,通过各种方式减少了网络延迟。

2017-03-03技术周报

本期周报内容来自: eggInBlackGlareCHDemoCooky

时光机:周报汇总周报博客iOS puzzles


Swift–基础

1.所有权宣言 - Swift 官方文章 Ownership Manifesto 译文评注版

2.《USING SWIFT WITH COCOA AND OBJECTIVE-C》学习笔记

Swift–实战

1.在Swift项目中使用JavaScript [推荐]

2.Server Side Swift with Vapor: Authentication with Turnstile :视频讲解了如何通过Swift的Vapor来实现一个基于Turnstile库的登录和授权功能[推荐]

3.Swift 后台开发 :对于swift后台开发不太了解的同学,可以先参考这篇文章

编译原理

1.大白话聊聊编译那点事儿

源码

1.关于iOS开源框架源代码解析的文章:详解了一些优秀的开源库,作者会不断更新,建议收藏[推荐]