| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- //
- // LNVideoPreviewCell.swift
- // Lanu
- //
- // Created by OneeChan on 2025/12/11.
- //
- import Foundation
- import UIKit
- import SnapKit
- protocol LNVideoPreviewCellDelegate: NSObject {
- func onVideoPreviewCellDragToDismiss(cell: LNVideoPreviewCell)
- }
- class LNVideoPreviewCell: UICollectionViewCell {
- private let videoPlayer = LNVideoPlayerView()
- private let indicator = UIActivityIndicatorView(style: .large)
-
- private var panGesture: UIPanGestureRecognizer?
- private var touchBeginPoint: CGPoint = .zero
- private var lastMove: CGPoint = .zero
- private let dragScaleMin = 0.4
- private let dragScaleOffsetY = UIScreen.main.bounds.height * 0.5
- private let dragAlphaOffsetY = 150.0
-
- private var curUrl: String?
-
- weak var delegate: LNVideoPreviewCellDelegate?
-
- override init(frame: CGRect) {
- super.init(frame: frame)
- setupViews()
- setupGesture()
- }
-
- func update(url: String, coverUrl: String?) {
- indicator.startAnimating()
- videoPlayer.loadVideo(url, coverUrl: coverUrl)
- }
-
- func stop() {
- videoPlayer.stop()
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- }
- extension LNVideoPreviewCell {
- @objc
- private func handlePan(_ gesture: UIPanGestureRecognizer) {
- let position = gesture.translation(in: self)
-
- switch gesture.state {
- case .began:
- touchBeginPoint = position
- break
- case .changed:
- lastMove = gesture.velocity(in: self)
- var frame = contentView.frame
- frame.origin.y = position.y - touchBeginPoint.y
- frame.origin.x = position.x - touchBeginPoint.x
- contentView.frame = frame
-
- let progress = (frame.origin.y / dragAlphaOffsetY).bounded(min: 0, max: 1.0)
- superview?.backgroundColor = .black.withAlphaComponent(1 - progress)
-
- let scale = 1 - (frame.origin.y / dragScaleOffsetY).bounded(min: 0, max: 1.0) * (1 - dragScaleMin)
- videoPlayer.transform = .init(scaleX: scale, y: scale)
- break
- default:
- if lastMove.y > 0 {
- var frame = contentView.frame
- frame.origin.y = bounds.height
-
- UIView.animate(withDuration: 0.25) { [weak self] in
- guard let self else { return }
- contentView.frame = frame
-
- superview?.backgroundColor = .clear
- } completion: { [weak self] _ in
- guard let self else { return }
- delegate?.onVideoPreviewCellDragToDismiss(cell: self)
- }
- } else {
- UIView.animate(withDuration: 0.25) { [weak self] in
- guard let self else { return }
- contentView.frame = bounds
-
- superview?.backgroundColor = .black
- videoPlayer.transform = .identity
- }
- }
- break
- }
- }
- }
- extension LNVideoPreviewCell: LNVideoPlayerViewDelegate {
- func onVideoDidLoad(view: LNVideoPlayerView) {
- indicator.stopAnimating()
- videoPlayer.start()
- }
- }
- extension LNVideoPreviewCell: UIGestureRecognizerDelegate {
- override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
- guard let pan = gestureRecognizer as? UIPanGestureRecognizer else {
- return true
- }
-
- let velocity = pan.velocity(in: self)
- return abs(velocity.y) > abs(velocity.x)
- }
- }
- extension LNVideoPreviewCell {
- private func setupViews() {
- videoPlayer.showCover = false
- videoPlayer.loop = true
- videoPlayer.delegate = self
- videoPlayer.backgroundColor = .clear
- videoPlayer.playButtonSize = 56
- contentView.addSubview(videoPlayer)
- videoPlayer.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.verticalEdges.equalToSuperview()
- }
- }
-
- private func setupGesture() {
- let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
- pan.delegate = self
- pan.isEnabled = true
- pan.maximumNumberOfTouches = 1
- contentView.addGestureRecognizer(pan)
- panGesture = pan
- }
- }
|