Add seek slide gesture

This commit is contained in:
PangMo5 2022-05-04 02:35:07 +09:00
parent 92e3c47214
commit 7a26e69685
8 changed files with 157 additions and 50 deletions

View File

@ -310,6 +310,8 @@ internal enum L10n {
internal static var seasons: String { return L10n.tr("Localizable", "seasons") }
/// See All
internal static var seeAll: String { return L10n.tr("Localizable", "seeAll") }
/// Seek Slide Gesture Enabled
internal static var seekSlideGestureEnabled: String { return L10n.tr("Localizable", "seekSlideGestureEnabled") }
/// See More
internal static var seeMore: String { return L10n.tr("Localizable", "seeMore") }
/// Select Cast Destination

View File

@ -10,21 +10,14 @@ import Defaults
import Foundation
extension SwiftfinStore {
enum Defaults {
static let generalSuite: UserDefaults = .init(suiteName: "swiftfinstore-general-defaults")!
static let generalSuite: UserDefaults = {
UserDefaults(suiteName: "swiftfinstore-general-defaults")!
}()
static let universalSuite: UserDefaults = {
UserDefaults(suiteName: "swiftfinstore-universal-defaults")!
}()
static let universalSuite: UserDefaults = .init(suiteName: "swiftfinstore-universal-defaults")!
}
}
extension Defaults.Keys {
// Universal settings
static let defaultHTTPScheme = Key<HTTPScheme>("defaultHTTPScheme", default: .http, suite: SwiftfinStore.Defaults.universalSuite)
static let appAppearance = Key<AppAppearance>("appAppearance", default: .system, suite: SwiftfinStore.Defaults.universalSuite)
@ -34,7 +27,8 @@ extension Defaults.Keys {
static let inNetworkBandwidth = Key<Int>("InNetworkBandwidth", default: 40_000_000, suite: SwiftfinStore.Defaults.generalSuite)
static let outOfNetworkBandwidth = Key<Int>("OutOfNetworkBandwidth", default: 40_000_000, suite: SwiftfinStore.Defaults.generalSuite)
static let isAutoSelectSubtitles = Key<Bool>("isAutoSelectSubtitles", default: false, suite: SwiftfinStore.Defaults.generalSuite)
static let autoSelectSubtitlesLangCode = Key<String>("AutoSelectSubtitlesLangCode", default: "Auto",
static let autoSelectSubtitlesLangCode = Key<String>("AutoSelectSubtitlesLangCode",
default: "Auto",
suite: SwiftfinStore.Defaults.generalSuite)
static let autoSelectAudioLangCode = Key<String>("AutoSelectAudioLangCode", default: "Auto", suite: SwiftfinStore.Defaults.generalSuite)
@ -46,13 +40,20 @@ extension Defaults.Keys {
// Video player / overlay settings
static let overlayType = Key<OverlayType>("overlayType", default: .normal, suite: SwiftfinStore.Defaults.generalSuite)
static let jumpGesturesEnabled = Key<Bool>("gesturesEnabled", default: true, suite: SwiftfinStore.Defaults.generalSuite)
static let systemControlGesturesEnabled = Key<Bool>("systemControlGesturesEnabled", default: true,
static let systemControlGesturesEnabled = Key<Bool>("systemControlGesturesEnabled",
default: true,
suite: SwiftfinStore.Defaults.generalSuite)
static let playerGesturesLockGestureEnabled = Key<Bool>("playerGesturesLockGestureEnabled", default: true,
static let playerGesturesLockGestureEnabled = Key<Bool>("playerGesturesLockGestureEnabled",
default: true,
suite: SwiftfinStore.Defaults.generalSuite)
static let videoPlayerJumpForward = Key<VideoPlayerJumpLength>("videoPlayerJumpForward", default: .fifteen,
static let seekSlideGestureEnabled = Key<Bool>("seekSlideGestureEnabled",
default: true,
suite: SwiftfinStore.Defaults.generalSuite)
static let videoPlayerJumpForward = Key<VideoPlayerJumpLength>("videoPlayerJumpForward",
default: .fifteen,
suite: SwiftfinStore.Defaults.generalSuite)
static let videoPlayerJumpBackward = Key<VideoPlayerJumpLength>("videoPlayerJumpBackward", default: .fifteen,
static let videoPlayerJumpBackward = Key<VideoPlayerJumpLength>("videoPlayerJumpBackward",
default: .fifteen,
suite: SwiftfinStore.Defaults.generalSuite)
static let autoplayEnabled = Key<Bool>("autoPlayNextItem", default: true, suite: SwiftfinStore.Defaults.generalSuite)
static let resumeOffset = Key<Bool>("resumeOffset", default: false, suite: SwiftfinStore.Defaults.generalSuite)
@ -68,12 +69,14 @@ extension Defaults.Keys {
static let shouldShowMissingEpisodes = Key<Bool>("shouldShowMissingEpisodes", default: true, suite: SwiftfinStore.Defaults.generalSuite)
// Should show video player items in overlay menu
static let shouldShowJumpButtonsInOverlayMenu = Key<Bool>("shouldShowJumpButtonsInMenu", default: true,
static let shouldShowJumpButtonsInOverlayMenu = Key<Bool>("shouldShowJumpButtonsInMenu",
default: true,
suite: SwiftfinStore.Defaults.generalSuite)
// Experimental settings
enum Experimental {
static let syncSubtitleStateWithAdjacent = Key<Bool>("experimental.syncSubtitleState", default: false,
static let syncSubtitleStateWithAdjacent = Key<Bool>("experimental.syncSubtitleState",
default: false,
suite: SwiftfinStore.Defaults.generalSuite)
static let forceDirectPlay = Key<Bool>("forceDirectPlay", default: false, suite: SwiftfinStore.Defaults.generalSuite)
static let nativePlayer = Key<Bool>("nativePlayer", default: false, suite: SwiftfinStore.Defaults.generalSuite)

View File

@ -0,0 +1,39 @@
//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
//
import UIKit.UIGestureRecognizerSubclass
enum PanDirection {
case vertical
case horizontal
}
class PanDirectionGestureRecognizer: UIPanGestureRecognizer {
let direction: PanDirection
init(direction: PanDirection, target: AnyObject, action: Selector) {
self.direction = direction
super.init(target: target, action: action)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
if state == .began {
let vel = velocity(in: view)
switch direction {
case .horizontal where abs(vel.y) > abs(vel.x):
state = .cancelled
case .vertical where abs(vel.x) > abs(vel.y):
state = .cancelled
default:
break
}
}
}
}

View File

@ -20,7 +20,6 @@ import UIKit
#endif
final class VideoPlayerViewModel: ViewModel {
// MARK: Published
// Manually kept state because VLCKit doesn't properly set "played"
@ -32,6 +31,8 @@ final class VideoPlayerViewModel: ViewModel {
@Published
var rightLabelText: String = "--:--"
@Published
var scrubbingTimeLabelText: String = "--:--"
@Published
var playbackSpeed: PlaybackSpeed = .one
@Published
var subtitlesEnabled: Bool {
@ -74,7 +75,12 @@ final class VideoPlayerViewModel: ViewModel {
}
@Published
var sliderIsScrubbing: Bool = false
var sliderIsScrubbing: Bool = false {
didSet {
beganScrubbingCurrentSeconds = currentSeconds
}
}
@Published
var sliderPercentage: Double = 0 {
willSet {
@ -119,6 +125,7 @@ final class VideoPlayerViewModel: ViewModel {
let overlayType: OverlayType
let jumpGesturesEnabled: Bool
let systemControlGesturesEnabled: Bool
let seekSlideGestureEnabled: Bool
let playerGesturesLockGestureEnabled: Bool
let resumeOffset: Bool
let streamType: ServerStreamType
@ -144,6 +151,8 @@ final class VideoPlayerViewModel: ViewModel {
// MARK: Current Time
private var beganScrubbingCurrentSeconds: Double = 0
var currentSeconds: Double {
let runTimeTicks = item.runTimeTicks ?? 0
let videoDuration = Double(runTimeTicks / 10_000_000)
@ -173,13 +182,12 @@ final class VideoPlayerViewModel: ViewModel {
}
var currentChapter: ChapterInfo? {
let chapterPairs = chapters.adjacentPairs().map { ($0, $1) }
let chapterRanges = chapterPairs.map { ($0.startPositionTicks ?? 0, ($1.startPositionTicks ?? 1) - 1) }
for chapterRangeIndex in 0 ..< chapterRanges.count {
if chapterRanges[chapterRangeIndex].0 <= currentSecondTicks &&
currentSecondTicks < chapterRanges[chapterRangeIndex].1
if chapterRanges[chapterRangeIndex].0 <= currentSecondTicks,
currentSecondTicks < chapterRanges[chapterRangeIndex].1
{
return chapterPairs[chapterRangeIndex].0
}
@ -249,6 +257,7 @@ final class VideoPlayerViewModel: ViewModel {
self.jumpGesturesEnabled = Defaults[.jumpGesturesEnabled]
self.systemControlGesturesEnabled = Defaults[.systemControlGesturesEnabled]
self.playerGesturesLockGestureEnabled = Defaults[.playerGesturesLockGestureEnabled]
self.seekSlideGestureEnabled = Defaults[.seekSlideGestureEnabled]
self.shouldShowJumpButtonsInOverlayMenu = Defaults[.shouldShowJumpButtonsInOverlayMenu]
self.resumeOffset = Defaults[.resumeOffset]
@ -271,9 +280,12 @@ final class VideoPlayerViewModel: ViewModel {
leftLabelText = calculateTimeText(from: currentSeconds)
rightLabelText = calculateTimeText(from: secondsScrubbedRemaining)
scrubbingTimeLabelText = calculateTimeText(from: currentSeconds - beganScrubbingCurrentSeconds)
}
private func calculateTimeText(from duration: Double) -> String {
let isNegative = duration < 0
let duration = abs(duration)
let hours = floor(duration / 3600)
let minutes = duration.truncatingRemainder(dividingBy: 3600) / 60
let seconds = duration.truncatingRemainder(dividingBy: 3600).truncatingRemainder(dividingBy: 60)
@ -288,17 +300,15 @@ final class VideoPlayerViewModel: ViewModel {
"\(String(Int(floor(minutes))).leftPad(toWidth: 2, withString: "0")):\(String(Int(floor(seconds))).leftPad(toWidth: 2, withString: "0"))"
}
return timeText
return "\(isNegative ? "-" : "") \(timeText)"
}
}
// MARK: Injected Values
extension VideoPlayerViewModel {
// Injects custom values that override certain settings
func injectCustomValues(startFromBeginning: Bool = false) {
if startFromBeginning {
item.userData?.playbackPositionTicks = 0
item.userData?.playedPercentage = 0
@ -311,7 +321,6 @@ extension VideoPlayerViewModel {
// MARK: Adjacent Items
extension VideoPlayerViewModel {
func getAdjacentEpisodes() {
guard let seriesID = item.seriesId, item.itemType == .episode else { return }
@ -412,21 +421,21 @@ extension VideoPlayerViewModel {
guard let masterSubtitleStream = masterViewModel.subtitleStreams
.first(where: { $0.index == masterViewModel.selectedSubtitleStreamIndex }),
let matchingSubtitleStream = self.subtitleStreams.first(where: { mediaStreamAboutEqual($0, masterSubtitleStream) }),
let matchingSubtitleStream = subtitleStreams.first(where: { mediaStreamAboutEqual($0, masterSubtitleStream) }),
let matchingSubtitleStreamIndex = matchingSubtitleStream.index else { return }
self.selectedSubtitleStreamIndex = matchingSubtitleStreamIndex
selectedSubtitleStreamIndex = matchingSubtitleStreamIndex
}
private func matchAudioStream(with masterViewModel: VideoPlayerViewModel) {
guard let currentAudioStream = masterViewModel.audioStreams.first(where: { $0.index == masterViewModel.selectedAudioStreamIndex }),
let matchingAudioStream = self.audioStreams.first(where: { mediaStreamAboutEqual($0, currentAudioStream) }) else { return }
let matchingAudioStream = audioStreams.first(where: { mediaStreamAboutEqual($0, currentAudioStream) }) else { return }
self.selectedAudioStreamIndex = matchingAudioStream.index ?? -1
selectedAudioStreamIndex = matchingAudioStream.index ?? -1
}
private func matchSubtitlesEnabled(with masterViewModel: VideoPlayerViewModel) {
self.subtitlesEnabled = masterViewModel.subtitlesEnabled
subtitlesEnabled = masterViewModel.subtitlesEnabled
}
private func mediaStreamAboutEqual(_ lhs: MediaStream, _ rhs: MediaStream) -> Bool {
@ -437,23 +446,23 @@ extension VideoPlayerViewModel {
// MARK: Progress Report Timer
extension VideoPlayerViewModel {
private func sendNewProgressReportWithTimer() {
self.progressReportTimer?.invalidate()
self.progressReportTimer = Timer.scheduledTimer(timeInterval: 0.7, target: self, selector: #selector(_sendProgressReport),
userInfo: nil, repeats: false)
progressReportTimer?.invalidate()
progressReportTimer = Timer.scheduledTimer(timeInterval: 0.7,
target: self,
selector: #selector(_sendProgressReport),
userInfo: nil,
repeats: false)
}
}
// MARK: Updates
extension VideoPlayerViewModel {
// MARK: sendPlayReport
func sendPlayReport() {
self.startTimeTicks = Int64(Date().timeIntervalSince1970) * 10_000_000
startTimeTicks = Int64(Date().timeIntervalSince1970) * 10_000_000
let subtitleStreamIndex = subtitlesEnabled ? selectedSubtitleStreamIndex : nil
@ -490,7 +499,6 @@ extension VideoPlayerViewModel {
// MARK: sendPauseReport
func sendPauseReport(paused: Bool) {
let subtitleStreamIndex = subtitlesEnabled ? selectedSubtitleStreamIndex : nil
let pauseInfo = PlaybackStartInfo(canSeek: true,
@ -526,7 +534,6 @@ extension VideoPlayerViewModel {
// MARK: sendProgressReport
func sendProgressReport() {
let subtitleStreamIndex = subtitlesEnabled ? selectedSubtitleStreamIndex : nil
let progressInfo = PlaybackProgressInfo(canSeek: true,
@ -550,9 +557,9 @@ extension VideoPlayerViewModel {
nowPlayingQueue: nil,
playlistItemId: "playlistItem0")
self.lastProgressReport = progressInfo
lastProgressReport = progressInfo
self.sendNewProgressReportWithTimer()
sendNewProgressReportWithTimer()
}
@objc
@ -573,7 +580,6 @@ extension VideoPlayerViewModel {
// MARK: sendStopReport
func sendStopReport() {
let stopInfo = PlaybackStopInfo(item: item,
itemId: item.id,
sessionId: response.playSessionId,
@ -600,9 +606,7 @@ extension VideoPlayerViewModel {
// MARK: Embedded/Normal Subtitle Streams
extension VideoPlayerViewModel {
func createEmbeddedSubtitleStream(with subtitleStream: MediaStream) -> URL {
guard let baseURL = URLComponents(url: directStreamURL, resolvingAgainstBaseURL: false) else { fatalError() }
guard let queryItems = baseURL.queryItems else { fatalError() }
@ -622,7 +626,6 @@ extension VideoPlayerViewModel {
// MARK: Equatable
extension VideoPlayerViewModel: Equatable {
static func == (lhs: VideoPlayerViewModel, rhs: VideoPlayerViewModel) -> Bool {
lhs.item.id == rhs.item.id &&
lhs.item.userData?.playbackPositionTicks == rhs.item.userData?.playbackPositionTicks
@ -632,7 +635,6 @@ extension VideoPlayerViewModel: Equatable {
// MARK: Hashable
extension VideoPlayerViewModel: Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(item)
hasher.combine(directStreamURL)

View File

@ -155,6 +155,7 @@
6220D0CC26D640C400B8E046 /* AppURLHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0CB26D640C400B8E046 /* AppURLHandler.swift */; };
6225FCCB2663841E00E067F6 /* ParallaxHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */; };
624C21752685CF60007F1390 /* SearchablePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 624C21742685CF60007F1390 /* SearchablePickerView.swift */; };
62553429282190A00087FE20 /* PanDirectionGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62553428282190A00087FE20 /* PanDirectionGestureRecognizer.swift */; };
625CB56F2678C23300530A6E /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB56E2678C23300530A6E /* HomeView.swift */; };
625CB5732678C32A00530A6E /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5722678C32A00530A6E /* HomeViewModel.swift */; };
625CB5752678C33500530A6E /* LibraryListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5742678C33500530A6E /* LibraryListViewModel.swift */; };
@ -667,6 +668,7 @@
6220D0CB26D640C400B8E046 /* AppURLHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppURLHandler.swift; sourceTree = "<group>"; };
6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParallaxHeader.swift; sourceTree = "<group>"; };
624C21742685CF60007F1390 /* SearchablePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchablePickerView.swift; sourceTree = "<group>"; };
62553428282190A00087FE20 /* PanDirectionGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PanDirectionGestureRecognizer.swift; sourceTree = "<group>"; };
625CB56E2678C23300530A6E /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
625CB5722678C32A00530A6E /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = "<group>"; };
625CB5742678C33500530A6E /* LibraryListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListViewModel.swift; sourceTree = "<group>"; };
@ -1107,6 +1109,7 @@
535870752669D60C00D05A09 /* Shared */ = {
isa = PBXGroup;
children = (
625534272821908D0087FE20 /* UIKit */,
6286F09F271C0AA500C40ED5 /* Generated */,
62C29E9D26D0FE5900C1D2E7 /* Coordinators */,
E1FCD08E26C466F3007C8DCF /* Errors */,
@ -1430,6 +1433,14 @@
path = Extensions;
sourceTree = "<group>";
};
625534272821908D0087FE20 /* UIKit */ = {
isa = PBXGroup;
children = (
62553428282190A00087FE20 /* PanDirectionGestureRecognizer.swift */,
);
path = UIKit;
sourceTree = "<group>";
};
6286F09F271C0AA500C40ED5 /* Generated */ = {
isa = PBXGroup;
children = (
@ -2498,6 +2509,7 @@
09389CC726819B4600AE350E /* VideoPlayerModel.swift in Sources */,
E1D4BF872719D27100A11E64 /* Bitrates.swift in Sources */,
6220D0B726D5EE1100B8E046 /* SearchCoordinator.swift in Sources */,
62553429282190A00087FE20 /* PanDirectionGestureRecognizer.swift in Sources */,
E13DD3EF27178F87009D4DAF /* SwiftfinNotificationCenter.swift in Sources */,
5377CBF5263B596A003A4E83 /* JellyfinPlayerApp.swift in Sources */,
E10D87DE278510E400BD264C /* PosterSize.swift in Sources */,

View File

@ -42,6 +42,8 @@ struct SettingsView: View {
var systemControlGesturesEnabled
@Default(.playerGesturesLockGestureEnabled)
var playerGesturesLockGestureEnabled
@Default(.seekSlideGestureEnabled)
var seekSlideGestureEnabled
@Default(.resumeOffset)
var resumeOffset
@Default(.subtitleSize)
@ -113,6 +115,8 @@ struct SettingsView: View {
Toggle(L10n.systemControlGesturesEnabled, isOn: $systemControlGesturesEnabled)
Toggle(L10n.seekSlideGestureEnabled, isOn: $seekSlideGestureEnabled)
Toggle(L10n.playerGesturesLockGestureEnabled, isOn: $playerGesturesLockGestureEnabled)
Toggle(L10n.resume5SecondOffset, isOn: $resumeOffset)

View File

@ -45,6 +45,7 @@ class VLCPlayerViewController: UIViewController {
private var panBeganBrightness = CGFloat.zero
private var panBeganVolumeValue = Float.zero
private var panBeganSliderPercentage: Double = 0
private var panBeganPoint = CGPoint.zero
private var tapLocationStack = [CGPoint]()
private var isJumping = false
@ -236,7 +237,8 @@ class VLCPlayerViewController: UIViewController {
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(didPinch(_:)))
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(didPan(_:)))
let verticalGesture = PanDirectionGestureRecognizer(direction: .vertical, target: self, action: #selector(didVerticalPan(_:)))
let horizontalGesture = PanDirectionGestureRecognizer(direction: .horizontal, target: self, action: #selector(didHorizontalPan(_:)))
let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(didLongPress))
@ -248,7 +250,11 @@ class VLCPlayerViewController: UIViewController {
}
if viewModel.systemControlGesturesEnabled {
view.addGestureRecognizer(panGesture)
view.addGestureRecognizer(verticalGesture)
}
if viewModel.seekSlideGestureEnabled {
view.addGestureRecognizer(horizontalGesture)
}
return view
@ -322,7 +328,7 @@ class VLCPlayerViewController: UIViewController {
}
@objc
private func didPan(_ gestureRecognizer: UIPanGestureRecognizer) {
private func didVerticalPan(_ gestureRecognizer: UIPanGestureRecognizer) {
switch gestureRecognizer.state {
case .began:
panBeganBrightness = UIScreen.main.brightness
@ -350,6 +356,26 @@ class VLCPlayerViewController: UIViewController {
}
}
@objc
private func didHorizontalPan(_ gestureRecognizer: UIPanGestureRecognizer) {
switch gestureRecognizer.state {
case .began:
panBeganPoint = gestureRecognizer.location(in: mainGestureView)
panBeganSliderPercentage = viewModel.sliderPercentage
viewModel.sliderIsScrubbing = true
case .changed:
let pos = gestureRecognizer.location(in: mainGestureView)
let moveDelta = panBeganPoint.x - pos.x
let changedValue = (moveDelta / mainGestureView.frame.width)
viewModel.sliderPercentage = min(max(0, panBeganSliderPercentage - changedValue), 1)
showSliderOverlay()
default:
viewModel.sliderIsScrubbing = false
hideSystemControlOverlay()
}
}
// MARK: setupOverlayHostingController
private func setupOverlayHostingController(viewModel: VideoPlayerViewModel) {
@ -746,6 +772,25 @@ extension VLCPlayerViewController {
}
}
private func showSliderOverlay() {
guard !displayingOverlay else { return }
let imageAttachment = NSTextAttachment()
imageAttachment.image = UIImage(systemName: "clock.arrow.circlepath",
withConfiguration: UIImage.SymbolConfiguration(pointSize: 48))?
.withTintColor(.white)
let attributedString = NSMutableAttributedString()
attributedString.append(.init(attachment: imageAttachment))
attributedString.append(.init(string: " \(viewModel.scrubbingTimeLabelText) (\(viewModel.leftLabelText))"))
systemControlOverlayLabel.attributedText = attributedString
systemControlOverlayLabel.layer.removeAllAnimations()
UIView.animate(withDuration: 0.1) {
self.systemControlOverlayLabel.alpha = 1
}
}
private func hideSystemControlOverlay() {
UIView.animate(withDuration: 0.75) {
self.systemControlOverlayLabel.alpha = 0
@ -1030,7 +1075,7 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
}
return
} else {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) { [weak self] in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.33) { [weak self] in
guard let self = self else { return }
guard !self.tapLocationStack.isEmpty else { return }
self.tapLocationStack.removeFirst()