周末抽时间写了个视频播放器,学习使用 Sketch,画了几个图标。实现功能如下:

  • 手势调节视频进度
  • 手势调节视频亮度
  • 手势调节音量

实现效果图:

/images/VideoPlayer

废话少说,直接上代码:

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private lazy var player: AVPlayer = {
let asset = AVURLAsset(URL: self.URL)
let playerItem = AVPlayerItem(asset: asset)
var player = AVPlayer(playerItem: playerItem)
let vol = AVAudioSession.sharedInstance().outputVolume
player.volume = 20.0 * log10f(vol + FLT_MIN)
self.startVolume = player.volume;
return player
}()
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.bounds
self.layer.addSublayer(playerLayer)

添加监听属性

监听的属性有:

  • 播放进度
  • 音量
  • 亮度
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
dynamic private var brightness: Float = 1 // 亮度
dynamic private var volume: Float = 1 // 音量
dynamic private var progress: Float = 1 // 播放进度
func addObserver() {
// 添加视频播放进度监听
player.addPeriodicTimeObserverForInterval(CMTimeMakeWithSeconds(1.0,Int32(NSEC_PER_SEC)), queue: nil, usingBlock: { (time) in
let doneTime = CMTimeGetSeconds(time)
let undoneTime = self.totalDuration - doneTime
self.configView.progressView.beginTime = doneTime
self.configView.progressView.endTime = undoneTime
self.configView.progressView.progress = doneTime/self.totalDuration
})
// 亮度
self.addObserver(self, forKeyPath: "brightness", options: .New, context: nil)
// 音量
self.addObserver(self, forKeyPath: "volume", options: .New, context: nil)
// 播放进度
self.addObserver(self, forKeyPath: "progress", options: .New, context: nil)
}
// 移除监听
func removeObserver() {
self.removeObserver(self, forKeyPath: "brightness")
self.removeObserver(self, forKeyPath: "volume")
self.removeObserver(self, forKeyPath: "progress")
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if keyPath == "brightness" { // 亮度
let brightnessValue = change!["new"] as! CGFloat
self.configView.lightView.progress = brightnessValue
} else if keyPath == "volume" { // 音量
let volumeValue = change!["new"] as! Float
player.volume = volumeValue
configView.volumeView.progress = CGFloat(volumeValue)
} else if keyPath == "progress" { // 播放进度
let progressValue = change!["new"] as! Float
let dragedSeconds = floorf(Float(totalDuration) * progressValue);
let newCMTime = CMTimeMake(Int64(dragedSeconds),1);
player.seekToTime(newCMTime)
player.play()
}
}

手势实现调节播放进度、亮度、音量

建一个配置类型枚举:

1
2
3
4
5
6
public enum VideoPlayerConfigType {
case None
case Volume // 调节音量
case Brightness // 调节亮度
case Progress // 调节进度
}

设置 touchesBegan 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard event?.allTouches()?.count == 1 else { // 一个手指
return
}
let touch = touches.first
let location = touch?.locationInView(self)
configType = .None // 配置类型
startLocation = location! // 开始位置
// 起始进度
startProgress = CMTimeGetSeconds(player.currentItem!.currentTime()) / totalDuration
// 起始亮度
startBrightness = Float(UIScreen.mainScreen().brightness)
// 起始音量 (后续会改为系统音量设置)
startVolume = player.volume
}

设置 touchesMoved 方法

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard event?.allTouches()?.count == 1 else {
return
}
let touch = touches.first
let location = touch?.locationInView(self)
let changeY = location!.y - startLocation.y;
let changeX = location!.x - startLocation.x;
if configType == .None {
if fabs(changeX) > fabs(changeY) { // 左右滑动
configType = .Progress
} else if fabs(changeY) > fabs(changeX) { // 上下滑动
let screenWidth = CGRectGetHeight(UIScreen.mainScreen().bounds)
if location!.x >= screenWidth/2.0 { // 屏幕右侧滑动
configType = .Volume
startVolume = player.volume
} else { // 屏幕左侧滑动
configType = .Brightness
}
}
}
if configType == .Volume { // 音量
let yOffset = (Float(startLocation.y - (location?.y)!))/100;
if (startVolume + yOffset) > 1 { // 音量 > 1 设置为 1
startLocation = location!
startVolume = 1
volume = 1
} else if (startVolume + yOffset) < 0 { // 音量 < 0 设置为 0
startLocation = location!
startVolume = 0
volume = 0
} else { // 正常范围
volume = startVolume + yOffset
}
print("音量: \(volume)");
} else if configType == .Brightness { // 亮度
let yOffset = (Float(startLocation.y - (location?.y)!))/100;
if (startBrightness + yOffset) > 1 { // 亮度 > 1 设置为 1
startLocation = location!
brightness = 1;
startBrightness = 1
} else if (startBrightness + yOffset) < 0 { // 亮度 < 0 设置为 0
startLocation = location!
brightness = 0;
startBrightness = 0
} else { // 正常范围
brightness = startBrightness + yOffset
}
print("亮度: \(brightness)");
} else if configType == .Progress { // 进度
let screenHeight = CGRectGetHeight(UIScreen.mainScreen().bounds)
let xOffset = (Double((location?.x)! - startLocation.x))/Double(screenHeight);
if (startProgress + xOffset) > 1 { // 播放进度 > 1 设置为 1
startLocation = location!
startProgress = 1
progress = 1
} else if (startProgress + xOffset) < 0 { // 播放进度 < 0 设置为 0
startLocation = location!
startProgress = 0
progress = 0
} else { // 正常进度
progress = Float(startProgress + xOffset)
}
print("进度 \(startProgress + xOffset)");
}
}

亮度视图(音量视图、进度视图类似)

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class LightView: UIView {
dynamic var progress: CGFloat = 0.0 // 进度
override init(frame: CGRect) {
super.init(frame: frame)
self.addObserver()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
deinit {
self.removeObserver()
}
func addObserver() {
self.addObserver(self, forKeyPath: "progress", options: .New, context: nil)
}
func removeObserver() {
self.removeObserver(self, forKeyPath: "progress")
}
// 配置界面 使用 frame 实现
func configUI() {
let lightIncreaseImage = UIImage.init(named: "Light_Increase")!
let lightIncreaseImageSize = lightIncreaseImage.size
let lightIncreaseImageView = UIImageView.init(image: lightIncreaseImage)
lightIncreaseImageView.frame = CGRect(x: (self.frame.width - lightIncreaseImageSize.width)/2.0, y: 0, width: lightIncreaseImageSize.width, height: lightIncreaseImageSize.height)
self.addSubview(lightIncreaseImageView)
self.layer.addSublayer(undoneLayer)
self.layer.addSublayer(doneLayer)
let lightDecreaseImage = UIImage.init(named: "Light_Decrease")!
let lightDecreaseImageSize = lightDecreaseImage.size
let lightDecreaseImageView = UIImageView.init(image: lightDecreaseImage)
lightDecreaseImageView.frame = CGRect(x: (self.frame.width - lightDecreaseImageSize.width)/2.0, y: self.frame.height - lightDecreaseImageSize.height, width: lightDecreaseImageSize.width, height: lightDecreaseImageSize.height)
self.addSubview(lightDecreaseImageView)
}
// 未设置的亮度 Layer
private lazy var undoneLayer: CALayer = {
var undoneLayer = CALayer()
undoneLayer.frame = CGRectMake((self.frame.width - 4)/2.0, (self.frame.height - 140)/2.0, 4, 140)
undoneLayer.backgroundColor = UIColor.init(white: 1, alpha: 0.2).CGColor
return undoneLayer
}()
// 已经设置的亮度 Layer 比 未设置的亮度 Layer 白一点
private lazy var doneLayer: CALayer = {
var doneLayer = CALayer()
doneLayer.frame = CGRectMake((self.frame.width - 4)/2.0, (self.frame.height - 140)/2.0 + 100, 4, 40)
doneLayer.backgroundColor = UIColor.init(white: 1, alpha: 0.6).CGColor
return doneLayer
}()
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if keyPath == "progress" {
let doneValue = change!["new"] as! CGFloat
let progress = doneValue * 140
UIScreen.mainScreen().brightness = doneValue // 设置屏幕亮度
doneLayer.frame = CGRectMake((self.frame.width - 4)/2.0, (self.frame.height - 140)/2.0 + (140 - progress), 4, progress)
}
}
}

功能还有待完善,后续会优化及添加一些其他功能:

  • 使用 Auto Layout 进行布局
  • 双击暂停等更多手势添加
  • 视频导入( WiFi、iTunes )