diff --git a/Course/Course/Presentation/Video/EncodedVideoPlayer.swift b/Course/Course/Presentation/Video/EncodedVideoPlayer.swift index 380f7fcc1..1f7be7649 100644 --- a/Course/Course/Presentation/Video/EncodedVideoPlayer.swift +++ b/Course/Course/Presentation/Video/EncodedVideoPlayer.swift @@ -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? { @@ -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) @@ -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 { diff --git a/Course/Course/Presentation/Video/EncodedVideoPlayerViewModel.swift b/Course/Course/Presentation/Video/EncodedVideoPlayerViewModel.swift index b7bdaed9f..fa8125d7b 100644 --- a/Course/Course/Presentation/Video/EncodedVideoPlayerViewModel.swift +++ b/Course/Course/Presentation/Video/EncodedVideoPlayerViewModel.swift @@ -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() } } diff --git a/Course/Course/Presentation/Video/PlayerViewController.swift b/Course/Course/Presentation/Video/PlayerViewController.swift index 573a1195e..f90fd240c 100644 --- a/Course/Course/Presentation/Video/PlayerViewController.swift +++ b/Course/Course/Presentation/Video/PlayerViewController.swift @@ -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 { @@ -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 + } } diff --git a/Course/Course/Presentation/Video/PlayerViewControllerHolder.swift b/Course/Course/Presentation/Video/PlayerViewControllerHolder.swift index 62a11bef6..b958107f9 100644 --- a/Course/Course/Presentation/Video/PlayerViewControllerHolder.swift +++ b/Course/Course/Presentation/Video/PlayerViewControllerHolder.swift @@ -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 diff --git a/Course/Course/Presentation/Video/VideoPlayerViewModel.swift b/Course/Course/Presentation/Video/VideoPlayerViewModel.swift index 51b8d3353..eb837d9b8 100644 --- a/Course/Course/Presentation/Video/VideoPlayerViewModel.swift +++ b/Course/Course/Presentation/Video/VideoPlayerViewModel.swift @@ -266,4 +266,8 @@ public class VideoPlayerViewModel: ObservableObject { duration: playerHolder.duration ) } + + func findSubtitle(at currentTime: Date) -> Subtitle? { + return subtitles.first { $0.fromTo.contains(currentTime) } + } }