jsdw_ios/QuickLocation/Section/Schedule/CreateSchedule/CreateScheduleView.swift

274 lines
8.5 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// CreateScheduleView.swift
// QuickLocation
//
// Created by on 2026/6/23.
//
import UIKit
import RxSwift
import RxCocoa
#if !targetEnvironment(simulator)
import AMapNaviKit
#endif
class CreateScheduleView: UIView {
var disposeBag = DisposeBag()
let createSchedulePopView = CreateSchedulePopView()
// MARK: - PopView
private var popTopConstraint: NSLayoutConstraint?
private var popUpLimit: CGFloat = 0
private var popDownLimit: CGFloat = 0
private var isLimitsSet = false
private var panStartTop: CGFloat = 0
private var isSubCanScroll = false
private func setupRx() {
backBtn.rx.tap.subscribe(onNext: { _ in
AppRouter.shared.popOrDismiss()
}).disposed(by: disposeBag)
// GroupView PanScrollView
createSchedulePopView.scrollView.delegate = self
}
private func setupUI() {
#if !targetEnvironment(simulator)
addSubview(mapView)
#endif
addSubview(navBgView)
addSubview(navBarView)
navBarView.addSubview(backBtn)
navBarView.addSubview(navTitleLabel)
navBarView.addSubview(deleteBtn)
addSubview(createSchedulePopView)
navBgView.layoutChain
.edges(excludingEdge: .bottom)
.heightToWidth(160/375)
navBarView.layoutChain
.edges(excludingEdge: .bottom)
.height(kNaviHeight)
backBtn.layoutChain
.centerY(navTitleLabel)
.left(15)
.width(24).height(24)
navTitleLabel.layoutChain
.top(kStatusBarHeight + 12)
.centerX()
deleteBtn.layoutChain
.centerY(navTitleLabel)
.right(15)
.width(16)
.height(16)
#if !targetEnvironment(simulator)
mapView.layoutChain
.top()
.edgesHorzontal()
.bottom()
#endif
// PopView: 1/3 navBar
createSchedulePopView.layoutChain
.edgesHorzontal()
.bottom()
.top(kScreenHeight / 3 * 2)
popTopConstraint = createSchedulePopView.jh_constraint(
.top, toAttribute: .top, otherView: createSchedulePopView.superview, relation: .equal
)
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePopPan(_:)))
pan.delegate = self
createSchedulePopView.addGestureRecognizer(pan)
}
// MARK: - Pan Gesture
@objc private func handlePopPan(_ pan: UIPanGestureRecognizer) {
guard isLimitsSet, let topConstraint = popTopConstraint else { return }
switch pan.state {
case .began:
layoutIfNeeded()
panStartTop = createSchedulePopView.frame.minY
case .changed:
let newTop = panStartTop + pan.translation(in: self).y
let scrollOffset = createSchedulePopView.scrollView.contentOffset.y
if isSubCanScroll {
// PopView
if scrollOffset > 0 { return }
// Pan velocity > 0
if pan.velocity(in: self).y > 0 || createSchedulePopView.frame.minY > popUpLimit + 1 {
isSubCanScroll = false
panStartTop = createSchedulePopView.frame.minY
}
} else {
// PopView
if createSchedulePopView.frame.minY <= popUpLimit && newTop <= popUpLimit {
isSubCanScroll = true
panStartTop = createSchedulePopView.frame.minY
topConstraint.constant = popUpLimit
return
}
}
let clamped = max(popUpLimit, min(popDownLimit, newTop))
topConstraint.constant = clamped
case .ended, .cancelled:
let velocity = pan.velocity(in: self)
let isNearUp = abs(createSchedulePopView.frame.minY - popUpLimit) < abs(createSchedulePopView.frame.minY - popDownLimit)
let target: CGFloat
if createSchedulePopView.frame.minY <= popUpLimit + 5 {
// scrollView
target = isNearUp ? popUpLimit : popDownLimit
} else if abs(velocity.y) > 200 {
target = velocity.y < 0 ? popUpLimit : popDownLimit
} else {
target = isNearUp ? popUpLimit : popDownLimit
}
topConstraint.constant = target
// scrollView
let atTop = target == self.popUpLimit
if !atTop {
isSubCanScroll = false
createSchedulePopView.scrollView.isScrollEnabled = false
createSchedulePopView.scrollView.setContentOffset(.zero, animated: false)
}
isSubCanScroll = atTop
UIView.animate(withDuration: 0.35, delay: 0,
usingSpringWithDamping: 0.85,
initialSpringVelocity: abs(velocity.y) / 1000,
options: [.allowUserInteraction]) {
self.layoutIfNeeded()
} completion: { _ in
self.createSchedulePopView.scrollView.isScrollEnabled = atTop
}
default:
break
}
}
override func layoutSubviews() {
super.layoutSubviews()
if !isLimitsSet {
isLimitsSet = true
popDownLimit = kScreenHeight / 3 * 2
popUpLimit = navBarView.frame.maxY
createSchedulePopView.scrollView.isScrollEnabled = false
}
}
// MARK: - Views
lazy var navBgView: UIImageView = {
let iv = UIImageView()
iv.image = UIImage(named: "Common/navBar_bg_2")
iv.contentMode = .scaleAspectFill
return iv
}()
lazy var navBarView: UIView = {
let v = UIView()
v.backgroundColor = .clear
return v
}()
lazy var backBtn: UIButton = {
let btn = UIButton(type: .custom)
btn.setImage(UIImage(named: "Common/back"), for: .normal)
btn.extendEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 30)
return btn
}()
lazy var navTitleLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 18, weight: .medium)
label.textColor = ThemeManager.shared.color.titleAuxColor
label.text = "创建行程"
return label
}()
lazy var deleteBtn: UIButton = {
let btn = UIButton()
btn.setImage(UIImage(named: "Common/delete"), for: .normal)
btn.extendEdgeInsets = UIEdgeInsets(top: 15, left: 20, bottom: 15, right: 15)
btn.isHidden = true
return btn
}()
#if !targetEnvironment(simulator)
lazy var mapView: MAMapView! = {
let mv = MAMapView()
mv.zoomLevel = 14
mv.showsUserLocation = false
mv.showsCompass = false
mv.userTrackingMode = .none
return mv
}()
#endif
#if !targetEnvironment(simulator)
func cleanupMap() {
mapView?.delegate = nil
mapView?.removeFromSuperview()
mapView = nil
}
#endif
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear
setupUI()
setupRx()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - UIScrollViewDelegate GroupView
extension CreateScheduleView: UIScrollViewDelegate {
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
//
if scrollView.contentOffset.y > 0 {
isSubCanScroll = true
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if isSubCanScroll {
if scrollView.contentOffset.y <= 0 {
isSubCanScroll = false
scrollView.contentOffset.y = 0
}
} else {
scrollView.contentOffset.y = 0
}
}
}
// MARK: - UIGestureRecognizerDelegate
extension CreateScheduleView: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith other: UIGestureRecognizer) -> Bool {
return true
}
}