Learn How to Create a Video Card using SwiftUI

Image for post
Image for post
A look at the video card we’ll be making

Getting Started

Image for post
Image for post
Image for post
Image for post

Creating The Custom PlayerView

import UIKit

class PlayerView: UIView {

}
import UIKit

class PlayerView: UIView {

private let playerLayer = AVPlayerLayer()
private var previewTimer:Timer?
var previewLength:Double


}
import UIKit

class PlayerView: UIView {

private let playerLayer = AVPlayerLayer()
private var previewTimer:Timer?
var previewLength:Double

init(frame: CGRect, url: URL, previewLength:Double) {
self.previewLength = previewLength
super.init(frame: frame)
}

required init?(coder: NSCoder) {
self.previewLength = 15
super.init(coder: coder)
}
}
init(frame: CGRect, url: URL, previewLength:Double) {
self.previewLength = previewLength
super.init(frame: frame)

// Create the video player using the URL passed in.
let player = AVPlayer(url: url)
player.volume = 0 // Will play audio if you don't set to zero
player.play() // Set to play once created.

// Add the player to our Player Layer
playerLayer.player = player
playerLayer.videoGravity = .resizeAspectFill // Resizes content to fill whole video layer.
playerLayer.backgroundColor = UIColor.black.cgColor

previewTimer = Timer.scheduledTimer(withTimeInterval: previewLength, repeats: true, block: { (timer) in
player.seek(to: CMTime(seconds: 0, preferredTimescale: CMTimeScale(1)))
})

}
import UIKit
import AVFoundation
import AVKit

class PlayerView: UIView {
private let playerLayer = AVPlayerLayer()
private var previewTimer:Timer?
var previewLength:Double

init(frame: CGRect, url: URL, previewLength:Double) {
self.previewLength = previewLength
super.init(frame: frame)

// Create the video player using the URL passed in.
let player = AVPlayer(url: url)
player.volume = 0 // Will play audio if you don't set to zero
player.play() // Set to play once created

// Add the player to our Player Layer
playerLayer.player = player
playerLayer.videoGravity = .resizeAspectFill // Resizes content to fill whole video layer.
playerLayer.backgroundColor = UIColor.black.cgColor

previewTimer = Timer.scheduledTimer(withTimeInterval: previewLength, repeats: true, block: { (timer) in
player.seek(to: CMTime(seconds: 0, preferredTimescale: CMTimeScale(1)))
})


layer.addSublayer(playerLayer)
}

required init?(coder: NSCoder) {
self.previewLength = 15
super.init(coder: coder)
}

override func layoutSubviews() {
super.layoutSubviews()
playerLayer.frame = bounds
}
}

Setting Up The UIViewRepresentable

import SwiftUI
import AVFoundation
import AVKit

struct VideoView: UIViewRepresentable {

var videoURL:URL
var previewLength:Double?

func makeUIView(context: Context) -> UIView {
return PlayerView(frame: .zero, url: videoURL, previewLength: previewLength ?? 15)
}

func updateUIView(_ uiView: UIView, context: Context) {

}
}
import SwiftUI

struct VideoCardTestView: View {

@State var maxHeight:CGFloat = 200

var body: some View {
VStack{
VideoView(videoURL: URL(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4")!, previewLength: 60)
.cornerRadius(15)
.frame(width: nil, height: maxHeight, alignment: .center)
.shadow(color: Color.black.opacity(0.7), radius: 30, x: 0, y: 2)
.padding(.horizontal, 20)
.padding(.top, 20)

Spacer()
}
}
}
Image for post
Image for post

Extra Credit!

@State var videoURL:URL
@State var showPlayIcon:Bool
var previewLength:Double
Image(systemName: "play.circle.fill")
import SwiftUI

struct VideoCard: View {

@State var videoURL:URL
@State var showPlayIcon:Bool
var previewLength:Double

var body: some View {
ZStack {
VideoView(videoURL: videoURL, previewLength: previewLength)
if showPlayIcon {
Image(systemName: "play.circle.fill")
.resizable()
.scaledToFit()
.frame(minWidth: 20, idealWidth: 40, maxWidth: 40, minHeight: 20, idealHeight: 40, maxHeight: 40, alignment: .center)
.foregroundColor(Color.white)
}

}
}
}
.onTapGesture {
// You Tapped the Video Card!
}

Extra Credit Complete!

Here’s an example mockup of how I used my VideoCard in testing!

Image for post
Image for post
Image for post
Image for post

Creator of TrailingClosure.com, and an avid learner!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store