mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-19 21:13:35 +00:00
1. add an new screen (layout) 2. add TR icon for the bottom tabbar 3. share current location from the TR PP 4. refactor TR manager to properly handle state updates and pass them to the LiveActivityManager and PlacePage 5. add init/update with TrackInfo/EleInfo methods to the PlacePageData and PlacePagePreviewData to update the PP state Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
161 lines
5.1 KiB
Swift
161 lines
5.1 KiB
Swift
final class TrackRecordingButtonViewController: MWMViewController {
|
|
|
|
private enum Constants {
|
|
static let buttonDiameter = CGFloat(48)
|
|
static let topOffset = CGFloat(6)
|
|
static let trailingOffset = CGFloat(10)
|
|
static let blinkingDuration = 1.0
|
|
static let color: (lighter: UIColor, darker: UIColor) = (.red, .red.darker(percent: 0.3))
|
|
}
|
|
|
|
private let trackRecordingManager: TrackRecordingManager = .shared
|
|
private let button = BottomTabBarButton()
|
|
private var blinkingTimer: Timer?
|
|
private var topConstraint = NSLayoutConstraint()
|
|
private var trailingConstraint = NSLayoutConstraint()
|
|
private var state: TrackRecordingButtonState = .hidden
|
|
|
|
private static var availableArea: CGRect = .zero
|
|
private static var topConstraintValue: CGFloat {
|
|
availableArea.origin.y + Constants.topOffset
|
|
}
|
|
private static var trailingConstraintValue: CGFloat {
|
|
-(UIScreen.main.bounds.maxX - availableArea.maxX + Constants.trailingOffset)
|
|
}
|
|
|
|
@objc
|
|
init() {
|
|
super.init(nibName: nil, bundle: nil)
|
|
let ownerViewController = MapViewController.shared()
|
|
ownerViewController?.addChild(self)
|
|
ownerViewController?.controlsView.addSubview(view)
|
|
self.setupView()
|
|
self.layout()
|
|
self.startTimer()
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
// async is for smoother appearance
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + kDefaultAnimationDuration) {
|
|
self.setState(self.state, completion: nil)
|
|
}
|
|
}
|
|
|
|
// MARK: - Public methods
|
|
|
|
@objc
|
|
func setState(_ state: TrackRecordingButtonState, completion: (() -> Void)?) {
|
|
self.state = state
|
|
switch state {
|
|
case .visible:
|
|
setHidden(false, completion: nil)
|
|
case .hidden:
|
|
setHidden(true, completion: completion)
|
|
case .closed:
|
|
close(completion: completion)
|
|
@unknown default:
|
|
fatalError()
|
|
}
|
|
}
|
|
|
|
// MARK: - Private methods
|
|
|
|
private func setupView() {
|
|
view.translatesAutoresizingMaskIntoConstraints = false
|
|
view.addSubview(button)
|
|
|
|
button.setStyleAndApply(.trackRecordingWidgetButton)
|
|
button.tintColor = Constants.color.darker
|
|
button.translatesAutoresizingMaskIntoConstraints = false
|
|
button.setImage(UIImage(resource: .icMenuBookmarkTrackRecording), for: .normal)
|
|
button.addTarget(self, action: #selector(didTap), for: .touchUpInside)
|
|
button.isHidden = true
|
|
}
|
|
|
|
private func layout() {
|
|
guard let superview = view.superview else { return }
|
|
topConstraint = view.topAnchor.constraint(equalTo: superview.topAnchor, constant: Self.topConstraintValue)
|
|
trailingConstraint = view.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: Self.trailingConstraintValue)
|
|
NSLayoutConstraint.activate([
|
|
topConstraint,
|
|
trailingConstraint,
|
|
view.widthAnchor.constraint(equalToConstant: Constants.buttonDiameter),
|
|
view.heightAnchor.constraint(equalToConstant: Constants.buttonDiameter),
|
|
|
|
button.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
|
button.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
|
button.topAnchor.constraint(equalTo: view.topAnchor),
|
|
button.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
|
])
|
|
}
|
|
|
|
private func updateLayout() {
|
|
guard let superview = view.superview else { return }
|
|
superview.animateConstraints {
|
|
self.topConstraint.constant = Self.topConstraintValue
|
|
self.trailingConstraint.constant = Self.trailingConstraintValue
|
|
}
|
|
}
|
|
|
|
private func startTimer() {
|
|
guard blinkingTimer == nil else { return }
|
|
var lighter = false
|
|
let timer = Timer.scheduledTimer(withTimeInterval: Constants.blinkingDuration, repeats: true) { [weak self] _ in
|
|
guard let self = self else { return }
|
|
UIView.animate(withDuration: Constants.blinkingDuration, animations: {
|
|
self.button.tintColor = lighter ? Constants.color.lighter : Constants.color.darker
|
|
lighter.toggle()
|
|
})
|
|
}
|
|
blinkingTimer = timer
|
|
RunLoop.current.add(timer, forMode: .common)
|
|
}
|
|
|
|
private func stopTimer() {
|
|
blinkingTimer?.invalidate()
|
|
blinkingTimer = nil
|
|
}
|
|
|
|
private func setHidden(_ hidden: Bool, completion: (() -> Void)?) {
|
|
UIView.transition(with: self.view,
|
|
duration: kDefaultAnimationDuration,
|
|
options: .transitionCrossDissolve,
|
|
animations: {
|
|
self.button.isHidden = hidden
|
|
}) { _ in
|
|
completion?()
|
|
}
|
|
}
|
|
|
|
private func close(completion: (() -> Void)?) {
|
|
stopTimer()
|
|
setHidden(true) { [weak self] in
|
|
guard let self else { return }
|
|
self.removeFromParent()
|
|
self.view.removeFromSuperview()
|
|
completion?()
|
|
}
|
|
}
|
|
|
|
static func updateAvailableArea(_ frame: CGRect) {
|
|
availableArea = frame
|
|
guard let button = MapViewController.shared()?.controlsManager.trackRecordingButton else { return }
|
|
DispatchQueue.main.async {
|
|
button.updateLayout()
|
|
}
|
|
}
|
|
|
|
// MARK: - Actions
|
|
|
|
@objc
|
|
private func didTap(_ sender: Any) {
|
|
MapViewController.shared()?.showTrackRecordingPlacePage()
|
|
}
|
|
}
|