Commit 7a44f2d6 authored by CZ1004's avatar CZ1004

【新增】左右滑动UI

parent 3048c9af
//
// PhotoSlideViewController.swift
// AIClean
//
// Created by 赵前 on 2025/5/10.
//
import UIKit
class PhotoSlideViewController: BaseViewController {
// 按钮竖直方向偏移量
let buttonVerticalOffset : CGFloat = 20
// MARK: - 数据与视图
var dataSource: [AssetModel] = []
private var currentIndex = 0
private var nextIndex = 0
private lazy var topView: AnchorRotatableView = {
let view = AnchorRotatableView()
view.configure(anchorPoint: CGPoint(x: 0.5, y: 0.5))
return view
}()
private lazy var bottomView: AnchorRotatableView = {
let view = AnchorRotatableView()
view.configure(anchorPoint: CGPoint(x: 0.5, y: 0.5))
return view
}()
private let leftButton: UIButton = {
let btn = UIButton()
btn.setTitle("Left", for: .normal)
btn.backgroundColor = .systemBlue.withAlphaComponent(0.8)
btn.alpha = 0
btn.layer.cornerRadius = 8
return btn
}()
private let rightButton: UIButton = {
let btn = UIButton()
btn.setTitle("Right", for: .normal)
btn.backgroundColor = .systemBlue.withAlphaComponent(0.8)
btn.alpha = 0
btn.layer.cornerRadius = 8
return btn
}()
// MARK: - 生命周期
override func viewDidLoad() {
super.viewDidLoad()
setupData()
setupViews()
setupGestures()
}
// MARK: - 初始化方法
private func setupData() {
currentIndex = dataSource.count - 1
updateCardContent()
}
private func setupViews() {
// 卡片视图
view.addSubview(bottomView)
view.addSubview(topView)
topView.snp.makeConstraints { make in
make.top.equalToSuperview().offset(100)
make.bottom.equalToSuperview().offset(-100)
make.left.equalToSuperview().offset(20)
make.right.equalToSuperview().offset(-20)
}
bottomView.snp.makeConstraints { make in
make.top.equalToSuperview().offset(100)
make.bottom.equalToSuperview().offset(-100)
make.left.equalToSuperview().offset(20)
make.right.equalToSuperview().offset(-20)
}
// 操作按钮
view.addSubview(leftButton)
view.addSubview(rightButton)
resetButtonsPosition()
}
private func resetButtonsPosition() {
leftButton.snp.makeConstraints { make in
make.height.width.equalTo(50)
make.centerX.equalTo(100)
make.centerY.equalTo(100)
}
rightButton.snp.makeConstraints { make in
make.height.width.equalTo(50)
make.centerX.equalTo(45)
make.centerY.equalTo(100)
}
}
private func setupGestures() {
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
topView.addGestureRecognizer(pan)
}
// MARK: - 手势处理
@objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
let translation = gesture.translation(in: view)
let velocity = gesture.velocity(in: view)
switch gesture.state {
case .began:
break
case .changed:
let progress = translation.x * 2 / topView.bounds.size.width
updateCardTransform(translation: translation)
updateButtons(progress: progress,translation: translation)
case .ended, .cancelled:
endDragAnimation(velocity: velocity)
default: break
}
}
// MARK: - 新手势绑定
private func setupGesturesForNewTopView() {
// 移除旧手势
topView.gestureRecognizers?.forEach { topView.removeGestureRecognizer($0) }
// 添加新手势
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
pan.delegate = self
topView.addGestureRecognizer(pan)
// 确保视图层级
view.bringSubviewToFront(bottomView)
view.bringSubviewToFront(topView)
view.bringSubviewToFront(leftButton)
view.bringSubviewToFront(rightButton)
}
private func updateCardTransform(translation: CGPoint) {
let progress = translation.x / topView.bounds.size.width
// 角度
let rotationAngle = topView.maxRotationAngle * progress
topView.transform = CGAffineTransform(translationX: translation.x, y: 0)
.rotated(by: rotationAngle)
}
private func updateButtons(progress: CGFloat,translation:CGPoint) {
// 计算X轴坐标 向右的时候progress为正数
if progress > 0 {
leftButton.isHidden = false
rightButton.isHidden = true
}else {
leftButton.isHidden = true
rightButton.isHidden = false
}
let leftX = min(translation.x + 25 , topView.bounds.size.width / 2)
let rightX = max(360 - abs(translation.x) , topView.bounds.size.width / 2)
// 计算Y轴坐标
let verY = min(100 + buttonVerticalOffset,100 + buttonVerticalOffset * abs(progress))
UIView.animate(withDuration: 0.1) {
self.leftButton.center = CGPoint(
x: leftX,
y:verY
)
self.rightButton.center = CGPoint(
x: rightX,
y: verY
)
// 渐变效果
let alpha = abs(progress)
self.leftButton.alpha = alpha
self.rightButton.alpha = alpha
}
}
private func endDragAnimation(velocity: CGPoint) {
let translation = topView.transform.tx
let shouldDisappear = abs(translation) > topView.bounds.size.width/2 || abs(velocity.x) > 800
self.leftButton.alpha = 0
self.rightButton.alpha = 0
shouldDisappear ? animateCardDisappear() : resetCardPosition()
}
private func animateCardDisappear() {
let direction: UISwipeGestureRecognizer.Direction = topView.transform.tx > 0 ? .right : .left
UIView.animate(withDuration: 0.4) {
let targetX = direction == .right ? self.view.bounds.width * 1.5 : -self.view.bounds.width * 1.5
self.topView.transform = CGAffineTransform(translationX: targetX, y: 0)
self.topView.alpha = 0
} completion: { _ in
self.handleCardDisappeared()
}
}
private func resetCardPosition() {
UIView.animate(withDuration: 0.6,
delay: 0,
usingSpringWithDamping: 0.6,
initialSpringVelocity: 0.5) {
self.topView.transform = .identity
self.resetButtonsPosition()
}
}
// MARK: - 数据更新
private func updateCardContent() {
guard dataSource.indices.contains(currentIndex) else { return }
// 获取资源
topView.imageView.image = PhotoAndVideoMananger.mananger.getImageFromAssetID(id: dataSource[currentIndex].localIdentifier)
// 预加载下一张
let next = currentIndex - 1 >= 0 ? currentIndex - 1 : dataSource.count - 1
bottomView.imageView.image = PhotoAndVideoMananger.mananger.getImageFromAssetID(id: dataSource[next].localIdentifier)
}
private func handleCardDisappeared() {
// 更新索引(循环逻辑)
currentIndex = currentIndex - 1 >= 0 ? currentIndex - 1 : dataSource.count - 1
// 交换视图层级
let temp = topView
topView = bottomView
bottomView = temp
// 重置视图状态
topView.transform = .identity
topView.alpha = 1
topView.isUserInteractionEnabled = true
// 重新绑定新手势
setupGesturesForNewTopView()
resetButtonsPosition()
updateCardContent()
// 重置底层视图位置
bottomView.snp.remakeConstraints { make in
make.top.equalToSuperview().offset(100)
make.bottom.equalToSuperview().offset(-100)
make.left.equalToSuperview().offset(20)
make.right.equalToSuperview().offset(-20)
}
bottomView.transform = .identity
bottomView.alpha = 1
}
}
extension PhotoSlideViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
//
// AnchorRotatableView.swift
// AIClean
//
// Created by 赵前 on 2025/5/10.
//
import Foundation
import UIKit
class AnchorRotatableView: UIView {
var maxRotationAngle: CGFloat = .pi/12
lazy var imageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
return iv
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
private func setupView() {
backgroundColor = .gray
layer.cornerRadius = 12
clipsToBounds = true
addSubview(imageView)
imageView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
}
}
extension AnchorRotatableView {
func configure(anchorPoint: CGPoint) {
layer.anchorPoint = anchorPoint
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment