Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions Atcha-iOS/DesignSource/AtchaBallon/AtchaBallon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import UIKit
import SnapKit

final class AtchaBallon: UIView {
private let topLabel: AtcahaInsetLabel = AtcahaInsetLabel()
private let bottomLabel: AtcahaInsetLabel = AtcahaInsetLabel()
var topLabel: AtcahaInsetLabel = AtcahaInsetLabel()
var bottomLabel: AtcahaInsetLabel = AtcahaInsetLabel()
private let triangeImageView: UIImageView = UIImageView()
private lazy var containerStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [topLabel, bottomLabel])
Expand Down Expand Up @@ -184,3 +184,38 @@ final class AtchaBallon: UIView {
}
}

extension AtchaBallon {
// 내부 뷰를 초기화 (모두 투명하게)
func resetAndHideAll() {
self.layer.removeAllAnimations()
self.topLabel.alpha = 0
self.bottomLabel.alpha = 0
self.triangeImageView.alpha = 0
self.isHidden = false
}

// Top 라벨 서서히 표시/숨김
func setTopVisible(_ isVisible: Bool, duration: TimeInterval = 0.3) {
self.topLabel.isHidden = false
UIView.animate(withDuration: duration) {
self.topLabel.alpha = isVisible ? 1 : 0
}
}

// Bottom 라벨(과 삼각형) 서서히 표시/숨김
func setBottomVisible(_ isVisible: Bool, duration: TimeInterval = 0.3) {
self.bottomLabel.isHidden = false
UIView.animate(withDuration: duration) {
self.bottomLabel.alpha = isVisible ? 1 : 0
self.triangeImageView.alpha = isVisible ? 1 : 0
}
}

// 택시비 AttributedString 생성 헬퍼
static func makeFareAttributedString(fareStr: String) -> NSAttributedString {
let gray = NSMutableAttributedString(string: "여기서 막차 놓치면 택시비 ", attributes: [.foregroundColor: UIColor.gray100])
let white = NSMutableAttributedString(string: "약 \(fareStr)", attributes: [.foregroundColor: UIColor.white])
gray.append(white)
return gray
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,6 @@ final class DetailRouteViewController: BaseViewController<DetailRouteViewModel>,

// 2. 좌표가 있다면 '애니메이션 없이' 즉시 현위치로 이동
if let currentCoord = viewModel.currentLocation {
mapContainerView.setupCenter(location: currentCoord) // setupZoomCenter 대신 setupCenter(이동만)
mapContainerView.setupZoomCenter(location: currentCoord) // 필요 시 줌까지
}
} else {
Expand Down Expand Up @@ -604,6 +603,8 @@ extension DetailRouteViewController {
@objc private func didTapLocationButton() {
ensureLocationPermissionOrShowToast()

viewModel.forceLocationSnap()

isFollowingUser = true
shouldCenterToCurrentLocationOnce = true
viewModel.startHeading()
Expand Down Expand Up @@ -645,7 +646,6 @@ extension DetailRouteViewController {
if isAlarmFired {
if let currentCoord = viewModel.currentLocation {
// 애니메이션 없이 즉시 이동하여 '깜빡임' 방지
mapContainerView.setupCenter(location: currentCoord)
mapContainerView.setupZoomCenter(location: currentCoord)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,14 @@ final class DetailRouteViewModel: BaseViewModel {
@Published var isRefreshing: Bool = false

private var lastValidTime: Date? = nil
private var consecutiveValidCount = 0
private var didSendInitialLocation = false

private var consecutiveValidCount = 0
func forceLocationSnap() {
self.didSendInitialLocation = false
self.lastValidTime = nil
self.consecutiveValidCount = 0
}
//#if DEBUG
//@Published var mockLocation: CLLocationCoordinate2D? = nil
//#endif
Expand Down Expand Up @@ -204,10 +210,19 @@ final class DetailRouteViewModel: BaseViewModel {
streamTask = Task {
for await location in streamUseCase.startUpdate() {
let now = Date()

let isInitialTracking = !self.didSendInitialLocation

let timeGap = self.lastValidTime != nil ? now.timeIntervalSince(self.lastValidTime!) : 999.0
let isRecovering = timeGap > 60.0

let accuracyThreshold = isRecovering ? 300.0 : 150.0
let accuracyThreshold: CLLocationAccuracy

if isInitialTracking {
accuracyThreshold = 1000.0 // 시청 탈출용 널널한 기준
} else {
accuracyThreshold = isRecovering ? 300.0 : 150.0 // 회원님의 지하철 복구 로직 유지!
}

guard location.horizontalAccuracy < accuracyThreshold else {
self.consecutiveValidCount = 0
Expand All @@ -218,7 +233,9 @@ final class DetailRouteViewModel: BaseViewModel {
// 지상 탈출이 의심될 때: 바로 안 믿고 카운터를 올립니다.
self.consecutiveValidCount += 1

if self.consecutiveValidCount >= 3 {
let requiredCount = isInitialTracking ? 1 : 3

if self.consecutiveValidCount >= requiredCount {
// 3번 연속(약 3초) 정상 신호가 들어왔다? 이건 100% 진짜 지상이다!
smoother.reset(location.coordinate)

Expand Down Expand Up @@ -252,6 +269,9 @@ final class DetailRouteViewModel: BaseViewModel {

await MainActor.run {
self.currentLocation = capturedCoord
if !self.didSendInitialLocation {
self.didSendInitialLocation = true
}
HomeArrivalManager.shared.checkHomeArrival(currentCoord: capturedCoord)
}

Expand Down
Loading
Loading