Following video playback with AVPlayer Create a video playback Progress Bar so that still images of the video are displayed side by side every 1 to 2 seconds.
reference: https://github.com/AppsBoulevard/ABVideoRangeSlider
public class VideoThumbnailUtils: NSObject{
public static func createVideoThumbnail(asset:AVAsset, targetTime: Double, scaleRate: Float)->CGImage?{
var videoSize: CGSize?
for track in asset.tracks {
if track.mediaType == AVMediaType.video {
videoSize = track.naturalSize
}
}
let videoHeight = videoSize?.height ?? 0
let videoWidth = videoSize?.width ?? 0
var reSize: CGFloat = 0
if videoHeight >= videoWidth {
reSize = videoHeight * CGFloat(scaleRate)
}else{
reSize = videoWidth * CGFloat(scaleRate)
}
let assetImageGenerator = AVAssetImageGenerator(asset: asset)
assetImageGenerator.appliesPreferredTrackTransform = true
assetImageGenerator.maximumSize = CGSize(width: reSize, height: reSize)
var actualTime: CMTime = CMTime.zero
do{
let imageRef = try assetImageGenerator.copyCGImage(at: CMTimeMakeWithSeconds(targetTime, preferredTimescale: 600), actualTime: &actualTime)
return imageRef
}
catch {
print("error")
return nil
}
}
}
Create [UIImage] from the still image acquired in 2.1
class ThumbnailsUtils: NSObject {
var thumbnailsView = [UIImageView]()
func updateThumbnails(view: UIView, videoURL: URL, duration: Float64) -> [UIImageView] {
var thumbnails = [UIImage]()
var offset: Float64 = 0
let imageCount = thumbnailsCount
for i in 0..<imageCount {
var thumbnail: UIImage?
let asset = AVAsset(url: videoURL)
if let imageData = VideoThumbnailUtils.createVideoThumbnail(asset: asset, targetTime: Double(offset), scaleRate: 0.3){
thumbnail = UIImage(cgImage: imageData)
}
if thumbnail == nil {
thumbnail = UIImage(systemName: "sun")
}
offset = Float64(i)*(duration/Float64(imageCount))
if thumbnail != nil {
thumbnails.append(thumbnail!)
}
}
return self.thumbnailsView
}
}
Put the [UIImageView] obtained in 3.2 into the UIView: addImagesToView
class ThumbnailsUtils: NSObject {
var thumbnailsView = [UIImageView]()
private var thumbnailsCount: Int = 0
func set(thumbnailsCount count: Int)-> ThumbnailsUtils {
self.thumbnailsCount = count
return self
}
///Implemented on the premise that the number of thumbnails is decided for the time being
private func clearThumbnails(){
for tView in self.thumbnailsView {
DispatchQueue.main.async {
tView.removeFromSuperview()
}
}
}
private func perThumbnailWidth(_ inView: UIView)-> CGFloat{
return inView.frame.size.width/CGFloat(thumbnailsCount)
}
private func addImagesToView(images:[UIImage], view: UIView){
thumbnailsView.removeAll()
let perWidth = perThumbnailWidth(view)
var xpos: CGFloat = 0.0
var width: CGFloat = 0.0
for image in images {
DispatchQueue.main.async {
if xpos + perWidth < view.frame.size.width {
width = perWidth
}else{
width = view.frame.size.width - xpos
}
let imageView = UIImageView(image: image)
imageView.alpha = 0
imageView.contentMode = UIView.ContentMode.scaleAspectFill
imageView.clipsToBounds = true
imageView.frame = CGRect(x: xpos, y: 0.0, width: width, height: view.frame.size.height)
self.thumbnailsView.append(imageView)
view.addSubview(imageView)
UIView.animate(withDuration: 0.2, animations: {()->Void in
imageView.alpha = 1.0
})
view.sendSubviewToBack(imageView)
xpos += perWidth
}
}
}
@discardableResult
func updateThumbnails(view: UIView, videoURL: URL, duration: Float64) -> [UIImageView] {
clearThumbnails()
var thumbnails = [UIImage]()
var offset: Float64 = 0
let imageCount = thumbnailsCount
for i in 0..<imageCount {
var thumbnail: UIImage?
let asset = AVAsset(url: videoURL)
if let imageData = VideoThumbnailUtils.createVideoThumbnail(asset: asset, targetTime: Double(offset), scaleRate: 0.3){
thumbnail = UIImage(cgImage: imageData)
}
if thumbnail == nil {
thumbnail = UIImage(systemName: "sun")
}
offset = Float64(i)*(duration/Float64(imageCount))
if thumbnail != nil {
thumbnails.append(thumbnail!)
}
}
addImagesToView(images: thumbnails, view: view)
return self.thumbnailsView
}
}
func loadThubnails(){
let thumbManager = ThumbnailsUtils().set(thumbnailsCount: 8)
let duration = ThumbnailsUtils.videoDuration(url: url!)
thumbManager.updateThumbnails(view: thumbnailsView, videoURL: url!, duration: duration)
}
It is now displayed.
Merge Progress Bar with Thumbnail Bar and make Gesture recognized by Thumbnail Bar It looks like this after the merger.
When displaying a still image thumbnail of a long video, it can be assumed that the width of the screen is too short. I want to be able to scroll left and right still image thumbnails
You can scroll by setting the UIView to ScrollView (fixed height) and remembering to set the width of the child View of ScrollView. However, if you add a GestureRecognizer in the ProgressBar to trim the video, the GestrueRecognizer in the ScrollView and the GestureRecgnizer in the ProgressBar (change the playback position by tapping, Trim, etc.) will conflict and you will not be able to scroll. ..
Here we use UIGestureRecognizerDelegate. Reference: https://qiita.com/ruwatana/items/16997b1b416512c20fb6
UIGestureRecognizerDelegate is mainly used for:
When competing as above, both the other side and the own side inherit the same Delegate, and then You need to write each method as needed. I will make a note again next time.