|
|
@@ -0,0 +1,139 @@
|
|
|
+//
|
|
|
+// LNVideoPreviewController.swift
|
|
|
+// Gami
|
|
|
+//
|
|
|
+// Created by OneeChan on 2026/3/4.
|
|
|
+//
|
|
|
+
|
|
|
+import Foundation
|
|
|
+import UIKit
|
|
|
+import SnapKit
|
|
|
+
|
|
|
+
|
|
|
+extension UIView {
|
|
|
+ func presentVideoPreview(_ urls: [String], _ targetIndex: Int) {
|
|
|
+ let vc = LNVideoPreviewController()
|
|
|
+ vc.loadVideos(urls: urls, targetIndex: targetIndex)
|
|
|
+ vc.modalPresentationStyle = .overFullScreen
|
|
|
+ viewController?.present(vc, animated: true)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+class LNVideoPreviewController: LNViewController {
|
|
|
+ private var videoURLs: [String] = []
|
|
|
+ private var curIndex: Int = 0 {
|
|
|
+ didSet {
|
|
|
+ titleLabel.text = "\(curIndex + 1)/\(videoURLs.count)"
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private var collectionView: UICollectionView?
|
|
|
+
|
|
|
+ private let fakeBar = LNFakeNaviBar()
|
|
|
+ private let titleLabel = UILabel()
|
|
|
+
|
|
|
+ func loadVideos(urls: [String], targetIndex: Int) {
|
|
|
+ videoURLs = urls
|
|
|
+ curIndex = targetIndex
|
|
|
+
|
|
|
+ collectionView?.reloadData()
|
|
|
+ DispatchQueue.main.async { [weak self] in
|
|
|
+ guard let self else { return }
|
|
|
+ collectionView?.scrollToItem(
|
|
|
+ at: .init(row: curIndex, section: 0),
|
|
|
+ at: .centeredHorizontally,
|
|
|
+ animated: false)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override func viewDidLoad() {
|
|
|
+ super.viewDidLoad()
|
|
|
+
|
|
|
+ showNavigationBar = false
|
|
|
+ setupViews()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension LNVideoPreviewController: LNVideoPreviewCellDelegate {
|
|
|
+ func onVideoPreviewCellDragToDismiss(cell: LNVideoPreviewCell) {
|
|
|
+ dismiss(animated: false)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension LNVideoPreviewController: UICollectionViewDataSource, UICollectionViewDelegate {
|
|
|
+ func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
|
|
+ videoURLs.count
|
|
|
+ }
|
|
|
+
|
|
|
+ func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
|
|
+ let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LNVideoPreviewCell.className, for: indexPath) as! LNVideoPreviewCell
|
|
|
+ cell.update(url: videoURLs[indexPath.row], coverUrl: nil)
|
|
|
+ cell.delegate = self
|
|
|
+ return cell
|
|
|
+ }
|
|
|
+
|
|
|
+ func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
|
|
|
+ let previewCell = cell as! LNVideoPreviewCell
|
|
|
+ previewCell.stop()
|
|
|
+ }
|
|
|
+
|
|
|
+ func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
|
|
+ curIndex = Int(scrollView.contentOffset.x / scrollView.bounds.width)
|
|
|
+ }
|
|
|
+
|
|
|
+ func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
|
|
|
+ curIndex = Int(scrollView.contentOffset.x / scrollView.bounds.width)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension LNVideoPreviewController {
|
|
|
+ private func setupViews() {
|
|
|
+ view.backgroundColor = .clear
|
|
|
+
|
|
|
+ let layout = UICollectionViewFlowLayout()
|
|
|
+ layout.scrollDirection = .horizontal
|
|
|
+ layout.itemSize = view.bounds.size
|
|
|
+ layout.minimumLineSpacing = 0
|
|
|
+ layout.minimumInteritemSpacing = 0
|
|
|
+
|
|
|
+ let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
|
|
+ collectionView.isPagingEnabled = true
|
|
|
+ collectionView.showsHorizontalScrollIndicator = false
|
|
|
+ collectionView.backgroundColor = .black
|
|
|
+ collectionView.register(LNVideoPreviewCell.self, forCellWithReuseIdentifier: LNVideoPreviewCell.className)
|
|
|
+ collectionView.dataSource = self
|
|
|
+ collectionView.delegate = self
|
|
|
+ view.addSubview(collectionView)
|
|
|
+ collectionView.snp.makeConstraints { make in
|
|
|
+ make.edges.equalToSuperview()
|
|
|
+ }
|
|
|
+ self.collectionView = collectionView
|
|
|
+
|
|
|
+ let fakeBar = buildFakeNavBar()
|
|
|
+ view.addSubview(fakeBar)
|
|
|
+ fakeBar.snp.makeConstraints { make in
|
|
|
+ make.horizontalEdges.equalToSuperview()
|
|
|
+ make.top.equalToSuperview()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private func buildFakeNavBar() -> UIView {
|
|
|
+ fakeBar.showBackButton { [weak self] in
|
|
|
+ guard let self else { return }
|
|
|
+ dismiss(animated: true)
|
|
|
+ }
|
|
|
+ fakeBar.backButton?.setImage(.init(systemName: "chevron.backward")?.withRenderingMode(.alwaysTemplate), for: .normal)
|
|
|
+ fakeBar.backButton?.tintColor = .fill
|
|
|
+
|
|
|
+ titleLabel.font = .body_l
|
|
|
+ titleLabel.textColor = .text_1
|
|
|
+ fakeBar.actionView.addSubview(titleLabel)
|
|
|
+ titleLabel.snp.makeConstraints { make in
|
|
|
+ make.center.equalToSuperview()
|
|
|
+ }
|
|
|
+
|
|
|
+ return fakeBar
|
|
|
+ }
|
|
|
+}
|
|
|
+
|