Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: show subtitles for videos in full-screen mode #97

Merged
merged 1 commit into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Course/Course/Presentation/Video/EncodedVideoPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public struct EncodedVideoPlayer: View {
@State private var isLoading: Bool = true
@State private var isAnimating: Bool = false
@State private var isOrientationChanged: Bool = false
@State private var subtitleText: String = ""

@State var showAlert = false
@State var alertMessage: String? {
Expand All @@ -54,7 +55,10 @@ public struct EncodedVideoPlayer: View {
VStack(spacing: 10) {
HStack {
VStack {
PlayerViewController(playerController: viewModel.controller)
PlayerViewController(
playerController: viewModel.controller,
subtitleText: $subtitleText
)
.aspectRatio(16 / 9, contentMode: .fit)
.frame(minWidth: playerWidth(for: reader.size))
.cornerRadius(12)
Expand Down Expand Up @@ -115,6 +119,10 @@ public struct EncodedVideoPlayer: View {
viewModel.controller.player?.allowsExternalPlayback = true
viewModel.controller.setNeedsStatusBarAppearanceUpdate()
}
.onReceive(viewModel.$currentTime) { currentTime in
let subtitle = viewModel.findSubtitle(at: Date(milliseconds: currentTime))
subtitleText = subtitle?.text ?? ""
}
}

private func playerWidth(for size: CGSize) -> CGFloat {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Core
import Combine

public class EncodedVideoPlayerViewModel: VideoPlayerViewModel {
var controller: AVPlayerViewController {
(playerHolder.playerController as? AVPlayerViewController) ?? AVPlayerViewController()
var controller: CustomAVPlayerViewController {
(playerHolder.playerController as? CustomAVPlayerViewController) ?? CustomAVPlayerViewController()
}
}
68 changes: 65 additions & 3 deletions Course/Course/Presentation/Video/PlayerViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import SwiftUI
import _AVKit_SwiftUI

struct PlayerViewController: UIViewControllerRepresentable {
var playerController: AVPlayerViewController
var playerController: CustomAVPlayerViewController
@Binding var subtitleText: String

func makeUIViewController(context: Context) -> AVPlayerViewController {
func makeUIViewController(context: Context) -> CustomAVPlayerViewController {
do {
try AVAudioSession.sharedInstance().setCategory(.playback)
} catch {
Expand All @@ -23,5 +24,66 @@ struct PlayerViewController: UIViewControllerRepresentable {
return playerController
}

func updateUIViewController(_ playerController: AVPlayerViewController, context: Context) {}
func updateUIViewController(_ playerController: CustomAVPlayerViewController, context: Context) {
playerController.subtitleText = subtitleText
}
}

class CustomAVPlayerViewController: AVPlayerViewController {
private let subtitleLabel = UILabel()

var subtitleText: String = "" {
didSet {
subtitleLabel.text = subtitleText
}
}

var hideSubtitle: Bool = false {
didSet {
subtitleLabel.isHidden = hideSubtitle
}
}

override func viewDidLoad() {
super.viewDidLoad()

// Configure the subtitle label
subtitleLabel.textColor = .white
subtitleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
subtitleLabel.backgroundColor = UIColor.black.withAlphaComponent(0.6)
subtitleLabel.textAlignment = .center
subtitleLabel.numberOfLines = 0
subtitleLabel.layer.cornerRadius = 8
subtitleLabel.layer.masksToBounds = true
subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
subtitleLabel.isHidden = true

self.delegate = self

// Add subtitle label to the content overlay view of AVPlayerViewController
contentOverlayView?.addSubview(subtitleLabel)

// Set constraints for the subtitle label
NSLayoutConstraint.activate([
subtitleLabel.centerXAnchor.constraint(equalTo: contentOverlayView!.centerXAnchor),
subtitleLabel.bottomAnchor.constraint(equalTo: contentOverlayView!.bottomAnchor, constant: -20),
subtitleLabel.widthAnchor.constraint(lessThanOrEqualTo: contentOverlayView!.widthAnchor, multiplier: 0.9)
])
}
}

extension CustomAVPlayerViewController: AVPlayerViewControllerDelegate {
func playerViewController(
_ playerViewController: AVPlayerViewController,
willBeginFullScreenPresentationWithAnimationCoordinator coordinator: any UIViewControllerTransitionCoordinator
) {
hideSubtitle = false
}

func playerViewController(
_ playerViewController: AVPlayerViewController,
willEndFullScreenPresentationWithAnimationCoordinator coordinator: any UIViewControllerTransitionCoordinator
) {
hideSubtitle = true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public class PlayerViewControllerHolder: PlayerViewControllerHolderProtocol {
let pipManager: PipManagerProtocol

public lazy var playerController: PlayerControllerProtocol? = {
let playerController = AVPlayerViewController()
let playerController = CustomAVPlayerViewController()
playerController.modalPresentationStyle = .fullScreen
playerController.allowsPictureInPicturePlayback = true
playerController.canStartPictureInPictureAutomaticallyFromInline = true
Expand Down
4 changes: 4 additions & 0 deletions Course/Course/Presentation/Video/VideoPlayerViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,8 @@ public class VideoPlayerViewModel: ObservableObject {
duration: playerHolder.duration
)
}

func findSubtitle(at currentTime: Date) -> Subtitle? {
return subtitles.first { $0.fromTo.contains(currentTime) }
}
}
Loading