753 lines
29 KiB
Swift
753 lines
29 KiB
Swift
//
|
||
// MeetDragCardContainer.swift
|
||
// Crush
|
||
//
|
||
// Created by AI Assistant on 2024/12/19.
|
||
// Copyright © 2024年 Crush. All rights reserved.
|
||
//
|
||
|
||
import UIKit
|
||
import SnapKit
|
||
import Lottie
|
||
|
||
// MARK: - 数据源协议
|
||
protocol MeetDragCardContainerDataSource: AnyObject {
|
||
/// 数据源个数
|
||
func numberOfRowsInYFLDragCardContainer(_ container: MeetDragCardContainer) -> Int
|
||
|
||
/// 显示数据源
|
||
func container(_ container: MeetDragCardContainer, viewForRowsAt index: Int) -> MeetDragCardView
|
||
}
|
||
|
||
// MARK: - 代理协议
|
||
protocol MeetDragCardContainerDelegate: AnyObject {
|
||
/// 点击卡片回调(⚠️废弃)
|
||
func container(_ container: MeetDragCardContainer, didSelectRowAt index: Int)
|
||
|
||
/// 拖到最后一张卡片 YES,空,可继续调用reloadData分页数据
|
||
func container(_ container: MeetDragCardContainer, dataSourceIsEmpty isEmpty: Bool)
|
||
|
||
/// 当前cardview 是否可以拖拽,默认YES
|
||
func container(_ container: MeetDragCardContainer, canDragForCardView cardView: MeetDragCardView) -> Bool
|
||
|
||
/// 卡片处于拖拽中回调
|
||
func container(_ container: MeetDragCardContainer, dargingForCardView cardView: MeetDragCardView, direction: ContainerDragDirection, widthRate: CGFloat, heightRate: CGFloat)
|
||
|
||
/// 询问这次滑动的方向是否生效,控制 v2.6 ld add
|
||
func container(_ container: MeetDragCardContainer, canDragFinishForDirection direction: ContainerDragDirection, forCardView cardView: MeetDragCardView) -> Bool
|
||
|
||
/// 卡片拖拽结束回调(卡片消失)
|
||
func container(_ container: MeetDragCardContainer, dragDidFinshForDirection direction: ContainerDragDirection, forCardView cardView: MeetDragCardView)
|
||
|
||
/// 卡片回看 v2.6 添加
|
||
func container(_ container: MeetDragCardContainer, lookingBack direction: ContainerDragDirection, forCardView cardView: MeetDragCardView)
|
||
|
||
func container(_ container: MeetDragCardContainer, enterSmallCardMode smallCardMode: Bool, forCardView cardView: MeetDragCardView)
|
||
}
|
||
|
||
// MARK: - 主容器类
|
||
class MeetDragCardContainer: UIView {
|
||
|
||
// MARK: - 公开属性
|
||
private(set) var tmpStoreNopeCardView: MeetDragCardView?
|
||
private(set) var smallCardMode: Bool = false
|
||
private(set) var isMoveIng: Bool = false
|
||
|
||
weak var dataSource: MeetDragCardContainerDataSource?
|
||
weak var delegate: MeetDragCardContainerDelegate?
|
||
|
||
// MARK: - 私有属性
|
||
private var cards: [MeetDragCardView] = []
|
||
private var direction: ContainerDragDirection = .default
|
||
private var loadedIndex: Int = 0
|
||
private var firstCardFrame: CGRect = .zero
|
||
private var lastCardFrame: CGRect = .zero
|
||
private var cardCenter: CGPoint = .zero
|
||
private var lastCardTransform: CGAffineTransform = .identity
|
||
private var configure: MeetDragConfigure!
|
||
|
||
private var likeOrDislikeShowLogoView: UIView!
|
||
private var likeShowImageView: UIImageView!
|
||
private var dislikeShowImageView: UIImageView!
|
||
// private var likeLottieView: LottieAnimationView!
|
||
// private var dislikeLottieView: LottieAnimationView!
|
||
|
||
|
||
// v2.8
|
||
private var currentIntialGestureDirection: ContainerDragDirection = .default
|
||
private var canMoveView: Bool = true
|
||
|
||
// MARK: - 初始化方法
|
||
convenience override init(frame: CGRect) {
|
||
self.init(frame: frame, configure: MeetDragCardContainer.setDefaultsConfigures())
|
||
}
|
||
|
||
init(frame: CGRect, configure: MeetDragConfigure) {
|
||
super.init(frame: frame)
|
||
initDataConfigure(configure)
|
||
}
|
||
|
||
required init?(coder: NSCoder) {
|
||
super.init(coder: coder)
|
||
initDataConfigure(MeetDragCardContainer.setDefaultsConfigures())
|
||
}
|
||
|
||
// MARK: - 私有方法
|
||
private static func setDefaultsConfigures() -> MeetDragConfigure {
|
||
let configure = MeetDragConfigure()
|
||
configure.visableCount = 3
|
||
configure.containerEdge = 0//16.0
|
||
configure.cardEdge = 0.01
|
||
configure.cardCornerRadius = 48.0
|
||
configure.cardCornerBorderWidth = 0.0
|
||
configure.cardBordColor = UIColor.clear
|
||
configure.cardVTopEdage = 0
|
||
configure.cardVBottomEdage = 0//12
|
||
return configure
|
||
}
|
||
|
||
private func initDataConfigure(_ configure: MeetDragConfigure) {
|
||
resetInitData()
|
||
initialLikeDislikesShowViews()
|
||
cards = []
|
||
self.configure = configure
|
||
}
|
||
|
||
private func initialLikeDislikesShowViews() {
|
||
likeOrDislikeShowLogoView = UIView()
|
||
likeOrDislikeShowLogoView.backgroundColor = UIColor.clear
|
||
likeOrDislikeShowLogoView.isUserInteractionEnabled = false
|
||
likeOrDislikeShowLogoView.alpha = 0
|
||
likeOrDislikeShowLogoView.layer.zPosition = 10
|
||
insertSubview(likeOrDislikeShowLogoView, at: 0)
|
||
likeOrDislikeShowLogoView.snp.makeConstraints { make in
|
||
make.edges.equalToSuperview()
|
||
}
|
||
|
||
likeShowImageView = {
|
||
let v = UIImageView()
|
||
v.image = UIImage(named: "meet_big_like")
|
||
likeOrDislikeShowLogoView.addSubview(v)
|
||
v.snp.makeConstraints { make in
|
||
make.centerX.equalToSuperview()
|
||
make.bottom.equalToSuperview().offset(-143)
|
||
}
|
||
v.isHidden = true
|
||
return v
|
||
}()
|
||
|
||
dislikeShowImageView = {
|
||
let v = UIImageView()
|
||
v.image = UIImage(named: "meet_big_dislike")
|
||
likeOrDislikeShowLogoView.addSubview(v)
|
||
v.snp.makeConstraints { make in
|
||
make.centerX.equalToSuperview()
|
||
make.bottom.equalToSuperview().offset(-143)
|
||
}
|
||
v.isHidden = true
|
||
return v
|
||
}()
|
||
|
||
// likeLottieView = {
|
||
// let animation = LottieAnimation.named("meet_right_swipe_like")
|
||
// let v = LottieAnimationView(animation: animation)
|
||
//
|
||
// v.contentMode = .scaleAspectFit
|
||
// v.loopMode = .playOnce
|
||
// v.backgroundBehavior = .pauseAndRestore
|
||
// v.backgroundColor = .clear
|
||
// likeOrDislikeShowLogoView.addSubview(v)
|
||
// let size = CGSize(width: 200, height: 200)
|
||
// v.size = size
|
||
// v.snp.makeConstraints { make in
|
||
// make.centerX.equalToSuperview()
|
||
// make.bottom.equalToSuperview().offset(-143)
|
||
// make.size.equalTo(size)
|
||
// }
|
||
// v.isHidden = true
|
||
// return v
|
||
// }()
|
||
//
|
||
// dislikeLottieView = {
|
||
// let animation = LottieAnimation.named("meet_left_swipe_dislike")
|
||
// let v = LottieAnimationView(animation: animation)
|
||
//
|
||
// v.contentMode = .scaleAspectFit
|
||
// v.loopMode = .playOnce
|
||
// v.backgroundBehavior = .pauseAndRestore
|
||
// v.backgroundColor = .clear
|
||
// likeOrDislikeShowLogoView.addSubview(v)
|
||
// let size = CGSize(width: 200, height: 200)
|
||
// v.size = size
|
||
// v.snp.makeConstraints { make in
|
||
// make.centerX.equalToSuperview()
|
||
// make.bottom.equalToSuperview().offset(-143)
|
||
// make.size.equalTo(size)
|
||
// }
|
||
// v.isHidden = true
|
||
// return v
|
||
// }()
|
||
|
||
}
|
||
|
||
/// 重置数据(为了二次调用reload)
|
||
private func resetInitData() {
|
||
loadedIndex = 0
|
||
resetCards()
|
||
direction = .default
|
||
isMoveIng = false
|
||
}
|
||
|
||
/// 清掉所有card view
|
||
private func resetCards() {
|
||
for view in cards {
|
||
view.removeFromSuperview()
|
||
}
|
||
cards.removeAll()
|
||
}
|
||
|
||
/// 添加子视图
|
||
private func addSubViews() {
|
||
guard let dataSource = dataSource else { return }
|
||
|
||
let sum = dataSource.numberOfRowsInYFLDragCardContainer(self)
|
||
let preLoadViewCount = min(sum, configure.visableCount)
|
||
|
||
// 预防越界
|
||
if loadedIndex < sum {
|
||
// 当手势滑动,加载第四个,最多创建4个。不存在内存warning。(手势停止,滑动的view没消失,需要干掉多创建的+1)
|
||
let targetCount = isMoveIng ? preLoadViewCount + 1 : preLoadViewCount
|
||
for _ in cards.count..<targetCount {
|
||
let cardView = dataSource.container(self, viewForRowsAt: loadedIndex)
|
||
addAdaptForDragCardView(cardView)
|
||
cards.append(cardView)
|
||
loadedIndex += 1
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 布局子视图
|
||
private func resetLayoutSubviews() {
|
||
// 动画时允许用户交流,比如触摸 | 时间曲线函数,缓入缓出,中间快
|
||
UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.6, options: [.allowUserInteraction, .curveEaseInOut]) {
|
||
|
||
if let delegate = self.delegate, let firstCard = self.cards.first {
|
||
delegate.container(self, dargingForCardView: firstCard, direction: self.direction, widthRate: 0, heightRate: 0)
|
||
}
|
||
|
||
for (i, cardView) in self.cards.enumerated() {
|
||
cardView.transform = .identity
|
||
let frame = self.firstCardFrame
|
||
|
||
switch i {
|
||
case 0:
|
||
cardView.frame = frame
|
||
case 1:
|
||
cardView.transform = CGAffineTransform(scaleX: secondCardScale, y: 1)
|
||
case 2:
|
||
cardView.transform = CGAffineTransform(scaleX: thirdCardScale, y: 1)
|
||
if self.lastCardFrame.isEmpty {
|
||
self.lastCardFrame = frame
|
||
self.lastCardTransform = cardView.transform
|
||
}
|
||
default:
|
||
break
|
||
}
|
||
|
||
cardView.originTransForm = cardView.transform
|
||
}
|
||
} completion: { finished in
|
||
let isEmpty = self.cards.isEmpty
|
||
self.delegate?.container(self, dataSourceIsEmpty: isEmpty)
|
||
}
|
||
}
|
||
|
||
private func recordFrame(_ cardView: MeetDragCardView) {
|
||
if loadedIndex >= 3 {
|
||
cardView.frame = lastCardFrame
|
||
} else {
|
||
let frame = cardView.frame
|
||
if firstCardFrame.isEmpty {
|
||
firstCardFrame = frame
|
||
cardCenter = cardView.center
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 移动卡片
|
||
private func moveIngStatusChange(_ scale: CGFloat) {
|
||
// 如果正在移动,添加第四个
|
||
if !isMoveIng {
|
||
isMoveIng = true
|
||
addSubViews()
|
||
} else {
|
||
// 第四个加载完,立马改变没作用在手势上其他cardview的scale
|
||
let absScale = min(abs(scale), boundaryRation)
|
||
let transFormtxPoor = (secondCardScale - thirdCardScale) / (boundaryRation / absScale)
|
||
let frameYPoor: CGFloat = 0
|
||
|
||
for (index, cardView) in cards.enumerated() {
|
||
guard index > 0 else { continue }
|
||
|
||
switch index {
|
||
case 1:
|
||
let scaleTransform = CGAffineTransform(scaleX: transFormtxPoor + secondCardScale, y: 1)
|
||
let translateTransform = CGAffineTransform(translationX: 0, y: -frameYPoor)
|
||
cardView.transform = scaleTransform.concatenating(translateTransform)
|
||
case 2:
|
||
let scaleTransform = CGAffineTransform(scaleX: transFormtxPoor + thirdCardScale, y: 1)
|
||
let translateTransform = CGAffineTransform(translationX: 0, y: -frameYPoor)
|
||
cardView.transform = scaleTransform.concatenating(translateTransform)
|
||
case 3:
|
||
cardView.transform = lastCardTransform
|
||
default:
|
||
break
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 手势结束
|
||
private func panGesturemMoveFinishOrCancle(_ cardView: MeetDragCardView, direction: ContainerDragDirection, scale: CGFloat, isDisappear: Bool) {
|
||
if !isDisappear { // 没有消失,恢复原样
|
||
if currentIntialGestureDirection == .left || currentIntialGestureDirection == .right || currentIntialGestureDirection == .default {
|
||
UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.6, options: [.allowUserInteraction, .curveEaseInOut]) {
|
||
self.subRestoreCardState()
|
||
} completion: { finished in
|
||
}
|
||
} else { // 其他情况下没有动画
|
||
subRestoreCardState()
|
||
}
|
||
} else {
|
||
// v2.6
|
||
if let delegate = delegate {
|
||
if !delegate.container(self, canDragFinishForDirection: direction, forCardView: cardView) {
|
||
subRestoreCardState()
|
||
return
|
||
}
|
||
}
|
||
|
||
delegate?.container(self, dragDidFinshForDirection: self.direction, forCardView: cardView)
|
||
|
||
if direction == .left || direction == .right {
|
||
tmpStoreNopeCardView = cardView
|
||
}
|
||
|
||
let flag = direction == .left ? -1 : 2
|
||
let screenWidth = UIScreen.main.bounds.width
|
||
|
||
UIView.animate(withDuration: 0.5, delay: 0.0, options: [.curveLinear, .allowUserInteraction]) {
|
||
self.isMoveIng = true
|
||
cardView.center = CGPoint(x: CGFloat(flag) * screenWidth, y: CGFloat(flag) * screenWidth / CGFloat(scale) + self.cardCenter.y)
|
||
} completion: { finished in
|
||
self.isMoveIng = false
|
||
cardView.removeFromSuperview()
|
||
}
|
||
|
||
if let index = cards.firstIndex(of: cardView) {
|
||
cards.remove(at: index)
|
||
}
|
||
isMoveIng = false
|
||
resetLayoutSubviews()
|
||
}
|
||
}
|
||
|
||
private func subRestoreCardState() {
|
||
// 干掉多创建的第四个.重置标量
|
||
if isMoveIng && cards.count > configure.visableCount {
|
||
if let lastView = cards.last {
|
||
lastView.removeFromSuperview()
|
||
cards.removeLast()
|
||
loadedIndex = lastView.tag
|
||
}
|
||
}
|
||
isMoveIng = false
|
||
resetLayoutSubviews()
|
||
}
|
||
|
||
// MARK: - 公开方法
|
||
func reloadData() {
|
||
guard dataSource != nil else {
|
||
assertionFailure("check dataSource and dataSource Methods!")
|
||
return
|
||
}
|
||
|
||
resetInitData()
|
||
addSubViews()
|
||
resetLayoutSubviews()
|
||
}
|
||
|
||
func getCurrentShowCardView() -> MeetDragCardView? {
|
||
return cards.first
|
||
}
|
||
|
||
func getCurrentShowCardViewIndex() -> Int {
|
||
return cards.first?.tag ?? 0
|
||
}
|
||
|
||
/// 手动控制滑动,且不回调代理方法
|
||
func removeCardViewForDirection(_ direction: ContainerDragDirection) {
|
||
guard !isMoveIng else { return }
|
||
|
||
let screenWidth = UIScreen.main.bounds.width
|
||
var cardCenter = CGPoint.zero
|
||
var flag = 0
|
||
|
||
guard let currentShowCardView = cards.first else { return }
|
||
|
||
switch direction {
|
||
case .left:
|
||
cardCenter = CGPoint(x: -screenWidth / 2.0, y: self.cardCenter.y)
|
||
flag = -1
|
||
tmpStoreNopeCardView = currentShowCardView
|
||
case .right:
|
||
cardCenter = CGPoint(x: screenWidth * 1.5, y: self.cardCenter.y)
|
||
flag = 1
|
||
tmpStoreNopeCardView = currentShowCardView
|
||
default:
|
||
break
|
||
}
|
||
|
||
UIView.animate(withDuration: 0.35) {
|
||
self.isMoveIng = true
|
||
let translate = CGAffineTransform(translationX: CGFloat(flag) * 20, y: 0)
|
||
currentShowCardView.transform = translate.rotated(by: CGFloat(flag) * .pi / 4 / 4)
|
||
currentShowCardView.center = cardCenter
|
||
} completion: { finished in
|
||
self.isMoveIng = false
|
||
currentShowCardView.removeFromSuperview()
|
||
if let index = self.cards.firstIndex(of: currentShowCardView) {
|
||
self.cards.remove(at: index)
|
||
}
|
||
self.addSubViews()
|
||
self.resetLayoutSubviews()
|
||
}
|
||
}
|
||
|
||
/// app控制滑动,且回调滑动Finish代理
|
||
func removeCardViewWithCallDelegateForDirection(_ direction: ContainerDragDirection) {
|
||
guard !isMoveIng else { return }
|
||
|
||
let screenWidth = UIScreen.main.bounds.width
|
||
var cardCenter = CGPoint.zero
|
||
var flag = 0
|
||
|
||
guard let currentShowCardView = cards.first else { return }
|
||
|
||
switch direction {
|
||
case .left:
|
||
cardCenter = CGPoint(x: -screenWidth / 2.0, y: self.cardCenter.y)
|
||
flag = -1
|
||
//tmpStoreNopeCardView = currentShowCardView
|
||
case .right:
|
||
cardCenter = CGPoint(x: screenWidth * 1.5, y: self.cardCenter.y)
|
||
flag = 1
|
||
default:
|
||
break
|
||
}
|
||
tmpStoreNopeCardView = currentShowCardView
|
||
|
||
UIView.animate(withDuration: 0.35) {
|
||
let translate = CGAffineTransform(translationX: CGFloat(flag) * 20, y: 0)
|
||
currentShowCardView.transform = translate.rotated(by: CGFloat(flag) * .pi / 4 / 4)
|
||
currentShowCardView.center = cardCenter
|
||
} completion: { finished in
|
||
currentShowCardView.removeFromSuperview()
|
||
if let index = self.cards.firstIndex(of: currentShowCardView) {
|
||
self.cards.remove(at: index)
|
||
}
|
||
self.addSubViews()
|
||
self.resetLayoutSubviews()
|
||
}
|
||
|
||
delegate?.container(self, dragDidFinshForDirection: direction, forCardView: currentShowCardView)
|
||
}
|
||
|
||
/// 业务需要: 手动回看 Meet 卡片,将之前存的临时CardView 回看回来
|
||
func addCardView(_ cardView: MeetDragCardView?, fromDirection direction: ContainerDragDirection) {
|
||
guard !isMoveIng else { return }
|
||
|
||
let targetCardView = cardView ?? tmpStoreNopeCardView
|
||
guard let cardView = targetCardView else { return }
|
||
|
||
let screenWidth = UIScreen.main.bounds.width
|
||
var cardCenter = CGPoint.zero
|
||
var flag = 0
|
||
|
||
switch direction {
|
||
case .left:
|
||
cardCenter = CGPoint(x: -screenWidth / 2.0, y: self.cardCenter.y)
|
||
flag = -1
|
||
//tmpStoreNopeCardView = cardView
|
||
case .right:
|
||
cardCenter = CGPoint(x: screenWidth * 1.5, y: self.cardCenter.y)
|
||
flag = 1
|
||
default:
|
||
cardCenter = CGPoint(x: self.cardCenter.x, y: self.cardCenter.y)
|
||
flag = 0
|
||
break
|
||
}
|
||
tmpStoreNopeCardView = cardView
|
||
|
||
cards.insert(cardView, at: 0)
|
||
addSubview(cardView)
|
||
|
||
cardView.center = cardCenter
|
||
let translate = CGAffineTransform(translationX: CGFloat(flag) * 20, y: 0)
|
||
cardView.transform = translate.rotated(by: CGFloat(flag) * .pi / 4 / 4)
|
||
|
||
cardCenter = CGPoint(x: self.cardCenter.x, y: self.cardCenter.y)
|
||
|
||
UIView.animate(withDuration: 0.35) {
|
||
cardView.transform = .identity
|
||
cardView.center = cardCenter
|
||
self.isMoveIng = true
|
||
} completion: { finished in
|
||
self.isMoveIng = false
|
||
self.tmpStoreNopeCardView = nil
|
||
}
|
||
|
||
delegate?.container(self, lookingBack: direction, forCardView: cardView)
|
||
}
|
||
|
||
func showNopeLogo(_ show: Bool, widthRate: CGFloat) {
|
||
if widthRate == 0 {
|
||
// if !isLottieAnimationPlaying(dislikeLottieView) {
|
||
// UIView.animate(withDuration: 0.25) {
|
||
// self.likeOrDislikeShowLogoView.alpha = 0
|
||
// }
|
||
// }
|
||
UIView.animate(withDuration: 0.5) {
|
||
self.likeOrDislikeShowLogoView.alpha = 0
|
||
}
|
||
return
|
||
}
|
||
|
||
if show {
|
||
// likeLottieView.isHidden = true
|
||
// dislikeLottieView.isHidden = false
|
||
likeShowImageView.isHidden = true
|
||
dislikeShowImageView.isHidden = false
|
||
likeOrDislikeShowLogoView.isHidden = false
|
||
likeOrDislikeShowLogoView.alpha = widthRate > 0.2 ? 1 : (widthRate / 0.2)
|
||
} else {
|
||
UIView.animate(withDuration: 0.45) {
|
||
self.likeOrDislikeShowLogoView.alpha = 0
|
||
}
|
||
}
|
||
}
|
||
|
||
func showLikeLogo(_ show: Bool, widthRate: CGFloat) {
|
||
if widthRate == 0 {
|
||
// if !isLottieAnimationPlaying(likeLottieView) {
|
||
// UIView.animate(withDuration: 0.25) {
|
||
// self.likeOrDislikeShowLogoView.alpha = 0
|
||
// }
|
||
// }
|
||
UIView.animate(withDuration: 0.5) {
|
||
self.likeOrDislikeShowLogoView.alpha = 0
|
||
}
|
||
return
|
||
}
|
||
|
||
if show {
|
||
// likeLottieView.isHidden = false
|
||
// dislikeLottieView.isHidden = true
|
||
likeShowImageView.isHidden = false
|
||
dislikeShowImageView.isHidden = true
|
||
likeOrDislikeShowLogoView.isHidden = false
|
||
likeOrDislikeShowLogoView.alpha = widthRate > 0.2 ? 1 : (widthRate / 0.2)
|
||
} else {
|
||
UIView.animate(withDuration: 0.45) {
|
||
self.likeOrDislikeShowLogoView.alpha = 0
|
||
}
|
||
}
|
||
}
|
||
|
||
// func showNopeLottie() {
|
||
// likeLottieView.isHidden = true
|
||
// dislikeLottieView.isHidden = false
|
||
// stopLottieAnimation(dislikeLottieView)
|
||
// likeOrDislikeShowLogoView.alpha = 1
|
||
//
|
||
// playLottieAnimation(dislikeLottieView) { [weak self] finish in
|
||
// if finish {
|
||
// self?.likeOrDislikeShowLogoView.alpha = 0
|
||
// }
|
||
// }
|
||
// }
|
||
|
||
// func showLikeLottie() {
|
||
// dislikeLottieView.isHidden = true
|
||
//
|
||
// likeLottieView.isHidden = false
|
||
// stopLottieAnimation(likeLottieView) // 🔥 推测方法
|
||
// likeOrDislikeShowLogoView.alpha = 1
|
||
//
|
||
// playLottieAnimation(likeLottieView) { [weak self] finish in
|
||
// if finish {
|
||
// self?.likeOrDislikeShowLogoView.alpha = 0
|
||
// }
|
||
// }
|
||
// }
|
||
|
||
func showNopeLogo(){
|
||
likeShowImageView.isHidden = true
|
||
dislikeShowImageView.isHidden = false
|
||
likeOrDislikeShowLogoView.alpha = 1
|
||
UIView.animate(withDuration: 0.5) { [self] in
|
||
likeOrDislikeShowLogoView.alpha = 0
|
||
}
|
||
}
|
||
|
||
func showLikeLogo(){
|
||
likeShowImageView.isHidden = false
|
||
dislikeShowImageView.isHidden = true
|
||
likeOrDislikeShowLogoView.alpha = 1
|
||
UIView.animate(withDuration: 0.5) { [self] in
|
||
likeOrDislikeShowLogoView.alpha = 0
|
||
}
|
||
}
|
||
|
||
func addAdaptForDragCardView(_ cardView: MeetDragCardView) {
|
||
let y = configure.containerEdge + configure.cardVTopEdage
|
||
let width = frame.size.width - 2 * configure.containerEdge
|
||
let height = frame.size.height - 2 * configure.containerEdge - configure.cardVTopEdage - configure.cardVBottomEdage
|
||
|
||
cardView.frame = CGRect(x: configure.containerEdge, y: y, width: width, height: height)
|
||
cardView.setConfigure(configure)
|
||
cardView.YFLDragCardViewLayoutSubviews()
|
||
|
||
recordFrame(cardView)
|
||
cardView.tag = loadedIndex
|
||
|
||
// 添加拖拽手势
|
||
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
|
||
cardView.addGestureRecognizer(panGesture)
|
||
|
||
//
|
||
// let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:)))
|
||
// tapGesture.numberOfTapsRequired = 1
|
||
// tapGesture.numberOfTouchesRequired = 1
|
||
// cardView.addGestureRecognizer(tapGesture)
|
||
|
||
addSubview(cardView)
|
||
sendSubviewToBack(cardView)
|
||
}
|
||
|
||
func clearTmpStoreNopeCardView() {
|
||
tmpStoreNopeCardView?.removeFromSuperview()
|
||
tmpStoreNopeCardView = nil
|
||
}
|
||
|
||
func switchAllCardsToSmallCardMode(_ smallMode: Bool) {
|
||
guard smallCardMode != smallMode else { return }
|
||
|
||
smallCardMode = smallMode
|
||
delegate?.container(self, enterSmallCardMode: smallCardMode, forCardView: getCurrentShowCardView() ?? MeetDragCardView())
|
||
}
|
||
|
||
// MARK: - 手势处理
|
||
// @objc private func handleTapGesture(_ tap: UITapGestureRecognizer) {
|
||
// delegate?.container(self, didSelectRowAt: tap.view?.tag ?? 0)
|
||
// }
|
||
|
||
@objc private func handlePanGesture(_ pan: UIPanGestureRecognizer) {
|
||
guard let cardView = pan.view as? MeetDragCardView else { return }
|
||
|
||
let canEdit = delegate?.container(self, canDragForCardView: cardView) ?? true
|
||
|
||
if canEdit {
|
||
switch pan.state {
|
||
case .began:
|
||
let point = pan.translation(in: self)
|
||
doVDirectionLogicByPoint(point, forCardView: cardView)
|
||
|
||
case .changed:
|
||
guard currentIntialGestureDirection != .up && currentIntialGestureDirection != .down else { return }
|
||
|
||
if let delegate = delegate {
|
||
// 计算横向滑动比例 >0 向右 <0 向左
|
||
let horizionSliderRate = CGFloat((pan.view?.center.x ?? 0) - cardCenter.x) / CGFloat(cardCenter.x)
|
||
let verticalSliderRate = CGFloat((pan.view?.center.y ?? 0) - cardCenter.y) / CGFloat(cardCenter.y)
|
||
|
||
// 正在滑动,需要创建第四个。
|
||
moveIngStatusChange(horizionSliderRate)
|
||
|
||
if currentIntialGestureDirection == .default {
|
||
// 再次决定方向
|
||
let point = pan.translation(in: self)
|
||
doVDirectionLogicByPoint(point, forCardView: cardView)
|
||
} else { // 已经确定了方向,且走到这儿,肯定是左右滑
|
||
// 以自身的左上角为原点;每次移动后,原点都置0;计算的是相对于上一个位置的偏移;
|
||
let point = pan.translation(in: self)
|
||
cardView.center = CGPoint(x: (pan.view?.center.x ?? 0) + point.x * 0.5, y: pan.view?.center.y ?? 0)
|
||
|
||
// 当angle为正值时,逆时针旋转坐标系统,反之顺时针旋转坐标系统
|
||
let rotationAngle = ((pan.view?.center.x ?? 0) - cardCenter.x) / cardCenter.x * (.pi / 4 / 12)
|
||
cardView.transform = cardView.originTransForm.rotated(by: rotationAngle)
|
||
pan.setTranslation(.zero, in: self) // 设置坐标原点位上次的坐标
|
||
}
|
||
|
||
if horizionSliderRate > 0 {
|
||
direction = .right
|
||
} else if horizionSliderRate < 0 {
|
||
direction = .left
|
||
} else {
|
||
direction = .default
|
||
}
|
||
|
||
if currentIntialGestureDirection == .default {
|
||
currentIntialGestureDirection = direction
|
||
}
|
||
|
||
delegate.container(self, dargingForCardView: cardView, direction: direction, widthRate: horizionSliderRate, heightRate: verticalSliderRate)
|
||
}
|
||
|
||
case .ended, .cancelled:
|
||
// 还原card位置,或者消失card
|
||
let horizionSliderRate = CGFloat((pan.view?.center.x ?? 0) - cardCenter.x) / CGFloat(cardCenter.x)
|
||
let moveY = (pan.view?.center.y ?? 0) - cardCenter.y
|
||
let moveX = (pan.view?.center.x ?? 0) - cardCenter.x
|
||
|
||
panGesturemMoveFinishOrCancle(cardView, direction: direction, scale: CGFloat(moveX / moveY), isDisappear: abs(horizionSliderRate) > boundaryRation)
|
||
|
||
for per in cards { // 上下滑完后,不隐藏后面的cardView
|
||
per.isHidden = false
|
||
}
|
||
|
||
currentIntialGestureDirection = .default
|
||
|
||
default:
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
// MARK: - 辅助方法
|
||
private func doVDirectionLogicByPoint(_ point: CGPoint, forCardView cardView: MeetDragCardView) {
|
||
// option2: 不单独处理竖直方向手势
|
||
if point.x == 0 {
|
||
currentIntialGestureDirection = .default
|
||
} else if point.x < 0 {
|
||
currentIntialGestureDirection = .left
|
||
} else {
|
||
currentIntialGestureDirection = .right
|
||
}
|
||
}
|
||
|
||
private func isLottieAnimationPlaying(_ view: LottieAnimationView) -> Bool {
|
||
return view.isAnimationPlaying
|
||
}
|
||
|
||
private func stopLottieAnimation(_ view: LottieAnimationView) {
|
||
view.stop()
|
||
}
|
||
|
||
private func playLottieAnimation(_ view: LottieAnimationView, completion: @escaping (Bool) -> Void) {
|
||
view.play { completed in
|
||
completion(completed)
|
||
}
|
||
}
|
||
}
|