Skip to content

Instantly share code, notes, and snippets.

@junpluse
Last active May 12, 2021 15:28
Show Gist options
  • Save junpluse/ee6d031654dd07c39fc7d8fbab80d6ed to your computer and use it in GitHub Desktop.
Save junpluse/ee6d031654dd07c39fc7d8fbab80d6ed to your computer and use it in GitHub Desktop.
AVQueuePlayer.repeatMode = .All
//
// AVPlayer+Repeat.swift
//
// Created by Jun Tanaka on 4/1/16.
// Copyright © 2016 eje Inc. All rights reserved.
//
import AVFoundation
private final class RepeatManager: NSObject {
weak var player: AVPlayer?
var mode: AVPlayer.RepeatMode = .None
init(player: AVPlayer) {
self.player = player
super.init()
startObservingCurrentItem(of: player)
if let playerItem = player.currentItem {
startObservingNotifications(of: playerItem)
}
}
deinit {
guard let player = player else {
return
}
stopObservingCurrentItem(of: player)
if let playerItem = player.currentItem {
stopObservingNotifications(of: playerItem)
}
}
func startObservingCurrentItem(of player: AVPlayer) {
player.addObserver(self, forKeyPath: "currentItem", options: [.Old, .New], context: nil)
}
func stopObservingCurrentItem(of player: AVPlayer) {
player.removeObserver(self, forKeyPath: "currentItem")
}
func startObservingNotifications(of playerItem: AVPlayerItem) {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(RepeatManager.observe(AVPlayerItemDidPlayToEndTimeNotification:)), name: AVPlayerItemDidPlayToEndTimeNotification, object: playerItem)
}
func stopObservingNotifications(of playerItem: AVPlayerItem) {
NSNotificationCenter.defaultCenter().removeObserver(self, name: AVPlayerItemDidPlayToEndTimeNotification, object: playerItem)
}
@objc func observe(AVPlayerItemDidPlayToEndTimeNotification notification: NSNotification) {
guard let player = player, let currentItem = player.currentItem where currentItem == (notification.object as? AVPlayerItem) else {
return
}
switch (mode, player) {
case (.All, let queuePlayer as AVQueuePlayer):
queuePlayer.advanceToNextItem()
currentItem.seekToTime(kCMTimeZero)
queuePlayer.insertItem(currentItem, afterItem: nil)
case (.One, _), (.All, _):
currentItem.seekToTime(kCMTimeZero)
default:
break
}
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if (object as? AVPlayer) == player && keyPath == "currentItem" {
if let oldItem = change?[NSKeyValueChangeOldKey] as? AVPlayerItem {
stopObservingNotifications(of: oldItem)
}
if let newItem = change?[NSKeyValueChangeNewKey] as? AVPlayerItem {
startObservingNotifications(of: newItem)
}
}
}
}
public extension AVPlayer {
private struct CustomProperties {
static var repeatManager: RepeatManager? = nil
}
private var repeatManager: RepeatManager? {
get {
return objc_getAssociatedObject(self, &CustomProperties.repeatManager) as? RepeatManager
}
set(value) {
objc_setAssociatedObject(self, &CustomProperties.repeatManager, value, .OBJC_ASSOCIATION_RETAIN)
}
}
enum RepeatMode {
case None
case One
case All // for AVQueuePlayer
}
var repeatMode: RepeatMode {
get {
return repeatManager?.mode ?? .None
}
set(mode) {
if repeatManager == nil {
repeatManager = RepeatManager(player: self)
}
repeatManager?.mode = mode
switch mode {
case .One:
actionAtItemEnd = .None
case _ where isKindOfClass(AVQueuePlayer.self):
actionAtItemEnd = .Advance
case .All:
actionAtItemEnd = .None
default:
actionAtItemEnd = .Pause
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment