123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- /*****************************************************************************
- * MediaPlayerActionSheet.swift
- *
- * Copyright © 2019 VLC authors and VideoLAN
- *
- * Authors: Robert Gordon <robwaynegordon@gmail.com>
- *
- *
- * Refer to the COPYING file of the official project for license.
- *****************************************************************************/
- enum MediaPlayerActionSheetCellIdentifier: String, CustomStringConvertible, CaseIterable {
- case filter
- case playback
- case equalizer
- case sleepTimer
- case interfaceLock
- var description: String {
- switch self {
- case .filter:
- return NSLocalizedString("VIDEO_FILTER", comment: "")
- case .playback:
- return NSLocalizedString("PLAYBACK_SPEED", comment: "")
- case .equalizer:
- return NSLocalizedString("EQUALIZER_CELL_TITLE", comment: "")
- case .sleepTimer:
- return NSLocalizedString("BUTTON_SLEEP_TIMER", comment: "")
- case .interfaceLock:
- return NSLocalizedString("INTERFACE_LOCK_BUTTON", comment: "")
- }
- }
- }
- @objc (VLCMediaPlayerActionSheetDataSource)
- protocol MediaPlayerActionSheetDataSource {
- var configurableCellModels: [ActionSheetCellModel] { get }
- }
- @objc (VLCMediaPlayerActionSheetDelegate)
- protocol MediaPlayerActionSheetDelegate {
- func mediaPlayerActionSheetHeaderTitle() -> String?
- @objc optional func mediaPlayerDidToggleSwitch(for cell: ActionSheetCell, state: Bool)
- }
- @objc (VLCMediaPlayerActionSheet)
- class MediaPlayerActionSheet: ActionSheet {
-
- // MARK: Private Instance Properties
- private weak var currentChildView: UIView?
- @objc weak var mediaPlayerActionSheetDelegate: MediaPlayerActionSheetDelegate?
- @objc weak var mediaPlayerActionSheetDataSource: MediaPlayerActionSheetDataSource?
-
- var offScreenFrame: CGRect {
- let y = collectionView.frame.origin.y + headerView.cellHeight
- let w = collectionView.frame.size.width
- let h = collectionView.frame.size.height
- return CGRect(x: w, y: y, width: w, height: h)
- }
-
- private var leftToRightGesture: UIPanGestureRecognizer {
- let leftToRight = UIPanGestureRecognizer(target: self, action: #selector(draggedRight(panGesture:)))
- return leftToRight
- }
- // MARK: Private Methods
- private func add(childView child: UIView) {
- UIView.animate(withDuration: 0.3, animations: {
- child.frame = self.collectionView.frame
- self.addChildToStackView(child)
- }) {
- (completed) in
- child.addGestureRecognizer(self.leftToRightGesture)
- self.currentChildView = child
- }
- }
- private func remove(childView child: UIView) {
- UIView.animate(withDuration: 0.3, animations: {
- child.frame = self.offScreenFrame
- }) { (completed) in
- child.removeFromSuperview()
- child.removeGestureRecognizer(self.leftToRightGesture)
- }
- }
- @objc func removeCurrentChild() {
- if let current = currentChildView {
- remove(childView: current)
- }
- }
- func setTheme() {
- let darkColors = PresentationTheme.darkTheme.colors
- collectionView.backgroundColor = darkColors.background
- headerView.backgroundColor = darkColors.background
- headerView.title.textColor = darkColors.cellTextColor
- for cell in collectionView.visibleCells {
- if let cell = cell as? ActionSheetCell {
- cell.backgroundColor = darkColors.background
- cell.name.textColor = darkColors.cellTextColor
- cell.icon.tintColor = .orange
- // toggleSwitch's tintColor should not be changed
- if cell.accessoryType == .disclosureChevron {
- cell.accessoryView.tintColor = darkColors.cellDetailTextColor
- } else if cell.accessoryType == .checkmark {
- cell.accessoryView.tintColor = .orange
- }
- }
- }
- collectionView.layoutIfNeeded()
- }
- /// Animates the removal of the `currentChildViewController` when it is dragged from its left edge to the right
- @objc private func draggedRight(panGesture: UIPanGestureRecognizer) {
- if let current = currentChildView {
- let translation = panGesture.translation(in: view)
- let x = translation.x + current.center.x
- let halfWidth = current.frame.size.width / 2
- panGesture.setTranslation(.zero, in: view)
- if panGesture.state == .began || panGesture.state == .changed {
- // only enable left-to-right drags
- if current.frame.minX + translation.x >= 0 {
- current.center = CGPoint(x: x, y: current.center.y)
- }
- } else if panGesture.state == .ended {
- if current.frame.minX > halfWidth {
- removeCurrentChild()
- } else {
- UIView.animate(withDuration: 0.3) {
- current.frame = self.collectionView.frame
- }
- }
- }
- }
- }
-
- // MARK: Overridden superclass methods
- // Removed the automatic dismissal of the view when a cell is selected
- override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
- if let delegate = delegate {
- if let item = delegate.itemAtIndexPath(indexPath) {
- delegate.actionSheet?(collectionView: collectionView, didSelectItem: item, At: indexPath)
- action?(item)
- }
- if let cell = collectionView.cellForItem(at: indexPath) as? ActionSheetCell, cell.accessoryType == .checkmark {
- removeActionSheet()
- }
- }
- }
-
- override func viewDidLoad() {
- super.viewDidLoad()
- // Remove the themeDidChangeNotification set in the superclass
- // MovieViewController Video Options should be dark at all times
- NotificationCenter.default.removeObserver(self, name: .VLCThemeDidChangeNotification, object: nil)
- }
- override func viewWillAppear(_ animated: Bool) {
- super.viewWillAppear(animated)
- setTheme()
- }
- // MARK: Initializers
- override init() {
- super.init()
- delegate = self
- dataSource = self
- modalPresentationStyle = .custom
- setAction { (item) in
- if let item = item as? UIView {
- self.add(childView: item)
- } else {
- preconditionFailure("MediaMoreOptionsActionSheet: Action:: Item's could not be cased as UIView")
- }
- }
- setTheme()
- }
-
- required init?(coder aDecoder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- }
- extension MediaPlayerActionSheet: ActionSheetDataSource {
- func numberOfRows() -> Int {
- return mediaPlayerActionSheetDataSource?.configurableCellModels.count ?? 0
- }
-
- func actionSheet(collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
- guard let source = mediaPlayerActionSheetDataSource,
- indexPath.row < source.configurableCellModels.count else {
- preconditionFailure("MediaPlayerActionSheet: mediaPlayerActionSheetDataSource or invalid indexPath")
- }
- var sheetCell: ActionSheetCell
- let cellModel = source.configurableCellModels[indexPath.row]
- if let cell = collectionView.dequeueReusableCell(
- withReuseIdentifier: ActionSheetCell.identifier,
- for: indexPath) as? ActionSheetCell {
- sheetCell = cell
- sheetCell.configure(withModel: cellModel)
- } else {
- assertionFailure("MediaMoreOptionsActionSheet: Could not dequeue reusable cell")
- sheetCell = ActionSheetCell(withCellModel: cellModel)
- }
- sheetCell.accessoryView.tintColor = PresentationTheme.darkTheme.colors.cellDetailTextColor
- sheetCell.delegate = self
- return sheetCell
- }
- }
- extension MediaPlayerActionSheet: ActionSheetDelegate {
- func itemAtIndexPath(_ indexPath: IndexPath) -> Any? {
- guard let source = mediaPlayerActionSheetDataSource,
- indexPath.row < source.configurableCellModels.count else {
- preconditionFailure("MediaPlayerActionSheet: mediaPlayerActionSheetDataSource not set")
- }
- let cellModel = source.configurableCellModels[indexPath.row]
- return cellModel.viewToPresent
- }
-
- func headerViewTitle() -> String? {
- return mediaPlayerActionSheetDelegate?.mediaPlayerActionSheetHeaderTitle()
- }
- }
- extension MediaPlayerActionSheet: ActionSheetCellDelegate {
- func actionSheetCellShouldUpdateColors() -> Bool {
- return false
- }
- func actionSheetCellDidToggleSwitch(for cell: ActionSheetCell, state: Bool) {
- guard let mediaDelegate = mediaPlayerActionSheetDelegate else {
- preconditionFailure("MediaPlayerActionSheet: mediaPlayerActionSheetDelegate not set")
- }
- mediaDelegate.mediaPlayerDidToggleSwitch?(for: cell, state: state)
- }
- }
|