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

246 lines
7.3 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)
// scrollView pan PopView
createSchedulePopView.scrollView.rx.contentOffset
.observe(on: MainScheduler.asyncInstance)
.subscribe(onNext: { [weak self] offset in
guard let self = self, self.isSubCanScroll, offset.y <= 0 else { return }
self.createSchedulePopView.scrollView.setContentOffset(.zero, animated: false)
})
.disposed(by: disposeBag)
}
private func setupUI() {
#if !targetEnvironment(simulator)
addSubview(mapView)
#endif
addSubview(navBgView)
addSubview(navBarView)
navBarView.addSubview(backBtn)
navBarView.addSubview(navTitleLabel)
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()
#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
if isSubCanScroll {
let scrollOffset = self.createSchedulePopView.scrollView.contentOffset.y
// scrollView
if scrollOffset > 0 { return }
// view
isSubCanScroll = false
panStartTop = createSchedulePopView.frame.minY
}
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 abs(velocity.y) > 200 {
target = velocity.y < 0 ? popUpLimit : popDownLimit
} else {
target = isNearUp ? popUpLimit : popDownLimit
}
topConstraint.constant = target
// scollView
let atTop = target == self.popUpLimit
createSchedulePopView.scrollView.isScrollEnabled = atTop
if !atTop {
createSchedulePopView.scrollView.setContentOffset(.zero, animated: false)
}
UIView.animate(withDuration: 0.2, delay: 0,
options: [.curveEaseInOut, .allowUserInteraction]) {
self.layoutIfNeeded()
}
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
}()
#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
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
}
}