From 45bafed1e9e11b8ea0a58f3c23a1f58e10428c34 Mon Sep 17 00:00:00 2001 From: Ethan Pippin Date: Wed, 19 Jan 2022 00:18:24 -0700 Subject: [PATCH] add aspect fill and update packages --- Shared/Extensions/CGSizeExtensions.swift | 15 ++++ .../xcshareddata/swiftpm/Package.resolved | 16 ++-- .../Overlays/VLCPlayerOverlayView.swift | 38 ++++++++++ .../VideoPlayer/PlayerOverlayDelegate.swift | 6 ++ .../VideoPlayer/VLCPlayerViewController.swift | 74 ++++++++++++++++++- 5 files changed, 139 insertions(+), 10 deletions(-) diff --git a/Shared/Extensions/CGSizeExtensions.swift b/Shared/Extensions/CGSizeExtensions.swift index 08026424..664eb5d0 100644 --- a/Shared/Extensions/CGSizeExtensions.swift +++ b/Shared/Extensions/CGSizeExtensions.swift @@ -13,4 +13,19 @@ extension CGSize { static func Circle(radius: CGFloat) -> CGSize { CGSize(width: radius, height: radius) } + + // From https://gist.github.com/jkosoy/c835fea2c03e76720c77 + static func aspectFill(aspectRatio: CGSize, minimumSize: CGSize) -> CGSize { + var minimumSize = minimumSize + let mW = minimumSize.width / aspectRatio.width + let mH = minimumSize.height / aspectRatio.height + + if mH > mW { + minimumSize.width = minimumSize.height / aspectRatio.height * aspectRatio.width + } else if mW > mH { + minimumSize.height = minimumSize.width / aspectRatio.width * aspectRatio.height + } + + return minimumSize + } } diff --git a/Swiftfin.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Swiftfin.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8e52e799..74055c43 100644 --- a/Swiftfin.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Swiftfin.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/CombineCommunity/CombineExt", "state": { "branch": null, - "revision": "8ca006df5e3cc6bb176b70238e2b0014bbc3a235", - "version": "1.0.0" + "revision": "0880829102152185190064fd17847a7c681d2127", + "version": "1.5.1" } }, { @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/sindresorhus/Defaults", "state": { "branch": null, - "revision": "8a6e4a96fd38504a05903d136c85634b65fd7c4d", - "version": "6.0.0" + "revision": "55f3302c3ab30a8760f10042d0ebc0a6907f865a", + "version": "6.1.0" } }, { @@ -96,8 +96,8 @@ "repositoryURL": "https://github.com/sushichop/Puppy", "state": { "branch": null, - "revision": "dc82e65c749cee431ffbb8c0913680b61ccd7e08", - "version": "0.2.0" + "revision": "95ce04b0e778b8d7c351876bc98bbf68328dfc9b", + "version": "0.3.1" } }, { @@ -105,8 +105,8 @@ "repositoryURL": "https://github.com/rundfunk47/stinsen", "state": { "branch": null, - "revision": "5e6c714f6f308877c8a988523915f9eb592d7d82", - "version": "2.0.3" + "revision": "36d97964075dc770046ddef9346a29bfa8982d6d", + "version": "2.0.7" } }, { diff --git a/Swiftfin/Views/VideoPlayer/Overlays/VLCPlayerOverlayView.swift b/Swiftfin/Views/VideoPlayer/Overlays/VLCPlayerOverlayView.swift index ce9979f0..98d08f27 100644 --- a/Swiftfin/Views/VideoPlayer/Overlays/VLCPlayerOverlayView.swift +++ b/Swiftfin/Views/VideoPlayer/Overlays/VLCPlayerOverlayView.swift @@ -87,6 +87,8 @@ struct VLCPlayerOverlayView: View { HStack(spacing: 20) { + // MARK: Previous Item + if viewModel.shouldShowPlayPreviousItem { Button { viewModel.playerOverlayDelegate?.didSelectPlayPreviousItem() @@ -97,6 +99,8 @@ struct VLCPlayerOverlayView: View { .foregroundColor(viewModel.nextItemVideoPlayerViewModel == nil ? .gray : .white) } + // MARK: Next Item + if viewModel.shouldShowPlayNextItem { Button { viewModel.playerOverlayDelegate?.didSelectPlayNextItem() @@ -107,6 +111,8 @@ struct VLCPlayerOverlayView: View { .foregroundColor(viewModel.nextItemVideoPlayerViewModel == nil ? .gray : .white) } + // MARK: Autoplay + if viewModel.shouldShowAutoPlay { Button { viewModel.autoplayEnabled.toggle() @@ -119,6 +125,8 @@ struct VLCPlayerOverlayView: View { } } + // MARK: Subtitle Toggle + if !viewModel.subtitleStreams.isEmpty { Button { viewModel.subtitlesEnabled.toggle() @@ -133,10 +141,32 @@ struct VLCPlayerOverlayView: View { .foregroundColor(viewModel.selectedSubtitleStreamIndex == -1 ? .gray : .white) } + // MARK: Screen Fill + + Button { + viewModel.playerOverlayDelegate?.didSelectScreenFill() + } label: { + if viewModel.playerOverlayDelegate?.getScreenFilled() ?? true { + if viewModel.playerOverlayDelegate?.isVideoAspectRatioGreater() ?? true { + Image(systemName: "rectangle.arrowtriangle.2.inward") + } else { + Image(systemName: "rectangle.portrait.arrowtriangle.2.inward") + } + } else { + if viewModel.playerOverlayDelegate?.isVideoAspectRatioGreater() ?? true { + Image(systemName: "rectangle.arrowtriangle.2.outward") + } else { + Image(systemName: "rectangle.portrait.arrowtriangle.2.outward") + } + } + } + // MARK: Settings Menu Menu { + // MARK: Audio Streams + Menu { ForEach(viewModel.audioStreams, id: \.self) { audioStream in Button { @@ -156,6 +186,8 @@ struct VLCPlayerOverlayView: View { } } + // MARK: Subtitle Streams + Menu { ForEach(viewModel.subtitleStreams, id: \.self) { subtitleStream in Button { @@ -175,6 +207,8 @@ struct VLCPlayerOverlayView: View { } } + // MARK: Playback Speed + Menu { ForEach(PlaybackSpeed.allCases, id: \.self) { speed in Button { @@ -194,6 +228,8 @@ struct VLCPlayerOverlayView: View { } } + // MARK: Chapters + if !viewModel.chapters.isEmpty { Button { viewModel.playerOverlayDelegate?.didSelectChapters() @@ -205,6 +241,8 @@ struct VLCPlayerOverlayView: View { } } + // MARK: Jump Button Lengths + if viewModel.shouldShowJumpButtonsInOverlayMenu { Menu { ForEach(VideoPlayerJumpLength.allCases, id: \.self) { forwardLength in diff --git a/Swiftfin/Views/VideoPlayer/PlayerOverlayDelegate.swift b/Swiftfin/Views/VideoPlayer/PlayerOverlayDelegate.swift index f0e49c37..4977b3a4 100644 --- a/Swiftfin/Views/VideoPlayer/PlayerOverlayDelegate.swift +++ b/Swiftfin/Views/VideoPlayer/PlayerOverlayDelegate.swift @@ -32,4 +32,10 @@ protocol PlayerOverlayDelegate { func didSelectChapters() func didSelectChapter(_ chapter: ChapterInfo) + + func didSelectScreenFill() + func getScreenFilled() -> Bool + // Returns whether the aspect ratio of the video + // is greater than the aspect ratio of the screen + func isVideoAspectRatioGreater() -> Bool } diff --git a/Swiftfin/Views/VideoPlayer/VLCPlayerViewController.swift b/Swiftfin/Views/VideoPlayer/VLCPlayerViewController.swift index a22a5212..135c32ab 100644 --- a/Swiftfin/Views/VideoPlayer/VLCPlayerViewController.swift +++ b/Swiftfin/Views/VideoPlayer/VLCPlayerViewController.swift @@ -28,6 +28,7 @@ class VLCPlayerViewController: UIViewController { private var lastProgressReportTicks: Int64 = 0 private var viewModelListeners = Set() private var overlayDismissTimer: Timer? + private var isScreenFilled: Bool = false private var currentPlayerTicks: Int64 { Int64(vlcMediaPlayer.time.intValue) * 100_000 @@ -42,7 +43,7 @@ class VLCPlayerViewController: UIViewController { } private lazy var videoContentView = makeVideoContentView() - private lazy var mainGestureView = makeTapGestureView() + private lazy var mainGestureView = makeMainGestureView() private var currentOverlayHostingController: UIHostingController? private var currentChapterOverlayHostingController: UIHostingController? private var currentJumpBackwardOverlayView: UIImageView? @@ -152,7 +153,7 @@ class VLCPlayerViewController: UIViewController { return view } - private func makeTapGestureView() -> UIView { + private func makeMainGestureView() -> UIView { let view = UIView() view.translatesAutoresizingMaskIntoConstraints = false @@ -164,7 +165,10 @@ class VLCPlayerViewController: UIViewController { let leftSwipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(didLeftSwipe)) leftSwipeGesture.direction = .left + let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(didPinch(_:))) + view.addGestureRecognizer(singleTapGesture) + view.addGestureRecognizer(pinchGesture) if viewModel.jumpGesturesEnabled { view.addGestureRecognizer(rightSwipeGesture) @@ -189,6 +193,23 @@ class VLCPlayerViewController: UIViewController { self.didSelectBackward() } + private var pinchScale: CGFloat = 1 + + @objc + private func didPinch(_ gestureRecognizer: UIPinchGestureRecognizer) { + if gestureRecognizer.state == .began || gestureRecognizer.state == .changed { + pinchScale = gestureRecognizer.scale + } else { + isScreenFilled.toggle() + + if pinchScale > 1 { + fillScreen() + } else { + shrinkScreen() + } + } + } + // MARK: setupOverlayHostingController private func setupOverlayHostingController(viewModel: VideoPlayerViewModel) { @@ -814,4 +835,53 @@ extension VLCPlayerViewController: PlayerOverlayDelegate { viewModel.sendProgressReport() } + + func didSelectScreenFill() { + + isScreenFilled.toggle() + + if isScreenFilled { + fillScreen() + } else { + shrinkScreen() + } + } + + private func fillScreen() { + let screenSize = UIScreen.main.bounds.size + let videoSize = vlcMediaPlayer.videoSize + let fillSize = CGSize.aspectFill(aspectRatio: videoSize, minimumSize: screenSize) + + let scale: CGFloat + + if fillSize.height > screenSize.height { + scale = fillSize.height / screenSize.height + } else { + scale = fillSize.width / screenSize.width + } + + UIView.animate(withDuration: 0.2) { + self.videoContentView.transform = CGAffineTransform(scaleX: scale, y: scale) + } + } + + private func shrinkScreen() { + UIView.animate(withDuration: 0.2) { + self.videoContentView.transform = .identity + } + } + + func getScreenFilled() -> Bool { + isScreenFilled + } + + func isVideoAspectRatioGreater() -> Bool { + let screenSize = UIScreen.main.bounds.size + let videoSize = vlcMediaPlayer.videoSize + + let screenAspectRatio = screenSize.width / screenSize.height + let videoAspectRatio = videoSize.width / videoSize.height + + return videoAspectRatio > screenAspectRatio + } }