네비게이션 뷰 안에 탭뷰를 감싸서 코드를 작성하고 있었는데
타이틀이 나오지 않는 다는 걸 알게됨
탭뷰는 각각 새로운 뷰이기 때문에 NavigationView도 각각 넣어줘야함!!
다시 시작함
모델에서 Date 값을 저장하는 게 아니라
String으로 변환된 Date값을 저장하고 이걸 왔다갔다 dateformatter를 사용해서 왔다갔다 하게끔 구성을 해줬다
Storage는 FileManager 사용했고!
이 글도 지금 두번째 쓰는중...
저장을 안해버렸다 ㅠ
현재까지 타이머 뷰 구현 완료했고, 타임 리스트 구현도 성공!!
그리고 각각의 Item들을 클릭하면 Navigation을 타고 들어가서 아이템에 대한 정보들도 표시해줬다!
또 데이터 persist까지 완료한 상태다
🥹 한 가지 아쉬운 점
타이머 저장하는 게 공부한 시점마다 다 개별적으로 나오는 데
같은 일자(day) 라면 하나로 합쳐서 보여주고, 터치해서 들어가면 그날의 공부 시간들을 보여주고 싶음
위에꺼 구현 못 해도 해야될 것들
현재 전체적으로 저장 되고 있는 게 [TimeModel] 리스트고,
이걸 년도+월 키워드로 grouping해서 [년도월 key : [TimeModel]]
로 섹션에 뿌려주고 있음
23년 3월
16일 20초, 18일 30초 ,19일 20초, 19일 40초, 19일 1분
Array 메소드들 개념 다시 잡고 시작하자
https://www.youtube.com/watch?v=-mx_Kf3qKJY
struct IndieApp {
let name: String
let monthlyPrice: Double
let users: Int
}
let appPortfolio: [IndieApp] = [
IndieApp(name: "Creator View", monthlyPrice: 11.99, users: 4356),
IndieApp(name: "FitHero", monthlyPrice: 0.00, users: 1756),
IndieApp(name: "Buckets", monthlyPrice: 3.99, users: 7598),
IndieApp(name: "Connect Four", monthlyPrice: 1.99, users: 34081),
]
// Filter
let freeApps = appPortfolio.filter { $0.monthlyPrice == 0 }
let highUsers = appPortfolio.filter { $0.users > 5000 }
print(freeApps)
print(highUsers)
filter는 조건 만족하는 애들만 걸러줌
HighUsers는 요런 뉘앙스겠죠
map은 다른 새로운 Array로 바꿔주는 메소드
appNames는 String 배열로 바꼈다
요런식으로!
reduce는 초기값 0 넣고 연산자로 합칠 수 있게 됨
요렇게 $0 $1의 users를 받아서 합칠 수 도 있겠고
여러개를 Chaining해서 써줄 수도 있겠다!!
요렇게 nil만 빼서 새로 배열 만들어주는 compactMap 메소드도 있고
어레이의 어레이를 하나로 다 합쳐주는 것도 있음
flatMap이 이런 메소드
여기에 또 map을 써서 바꿔주는 것도 가능합니다~!
오케이 다시 해봅시다
자 섹션이 나뉜 거 까지는 오케이.
나뉜 거에다가 만약에 TimeModel의 monthlyIdentifier가 같다면 TimeModel을 합쳐주는 메소드를 만들어야겠죠
.reduce를 쓰는 거!!
func getSectionTimeData(key: String) -> [TimeModel] {
let items = sectionTimeDic[key] ?? []
let mergedItems = items.reduce(into: [String: TimeModel]()) { result, timeModel in
let dailyIdentifier = timeModel.dailyIdentifier
// if the result dictionary already contains a TimeModel with the same dailyIdentifier,
// update its studySeconds by adding the current timeModel's studySeconds
if var existingModel = result[dailyIdentifier] {
existingModel.studySeconds += timeModel.studySeconds
existingModel.breakSeconds += timeModel.breakSeconds
result[dailyIdentifier] = existingModel
} else {
// otherwise, add the current timeModel to the result dictionary
result[dailyIdentifier] = timeModel
}
}.values.sorted { $0.fullDate < $1.fullDate}
// mergedTimeModels will contain an array of TimeModels with unique dailyIdentifiers
// and their studySeconds merged if they have the same dailyIdentifier
return mergedItems
}
func getSectionTimeData(key: String) -> [TimeModel] {
let items = sectionTimeDic[key] ?? []
let mergedItems = Dictionary(grouping: items) { $0.dailyIdentifier }
.map { (_, models) in
models.reduce(TimeModel(fullDate: "", studySeconds: 0, breakSeconds: 0)) { result, model in
TimeModel(fullDate: model.fullDate,
studySeconds: result.studySeconds + model.studySeconds,
breakSeconds: result.breakSeconds + model.breakSeconds)
}
}.sorted { $0.fullDate < $1.fullDate }
return mergedItems
}
grouping을 dailyIdentifier로 다시해주고 reduce하는 거!!
2번으로 갑시다~
타이머가 실행중일 때 타임리스트에서 아이템 선택해서 안으로 들어가면
네비게이션이 바로 닫히는 현상 발생
Timer가 업데이트 될 때마다 NavigationView가 닫힘
원래쓰던 getSectionTimeData로 할 때는 괜찮은데
merge 하는 과정에서 뭔가가 있나봄
2번으로 쓰던 방법 1번으로 바꾸니까 괜찮아졌다!!
그리고 타임리스트 day로 합친게 업데이트가 안되고 있음
합친 모델들도 삭제 안되고 있고
결국에는 이거 TimeModel 합칠라면
TimerListView에서 뿌릴 때 day로 나뉜 것도 [TimeModel]의 형태를 가지고 있어야하겠네
잠깐 그전에 타임셀에서
@State로 TimeModel을 받고 있었는데 이게 잘못됬었음
let 으로 해줘야함!!
어쩐지 업데이트가 될때도 있고 안될 때도 있더라..
저장하는 로직에서도 get으로 fetch해서 리로드하는것도 지워줌
이제 잘되네
자자 타임디테일뷰에서 받은 타임의 monthlyIdentifier를
활용해서 TimeList 필터를 거친 담에
해당 [TimeModel]을 이용해서 List 형태로 뿌려주면 되겠다
후우우...
긴 여정이었다
NavigationLink를 쓰게 되면 List에서 첨 그려질 때 로드가 되서
어떻게 하면 필터링 된 데이터들을 전달할 지에서 한참을 막혔음
결국 구현한 방법은 Detail에 dayKey 구멍을 뚫고
onAppear될 때 vm의 메소드가 실행되게 한 다음
vm의 dayTimeList를 가지고 리스트가 그려지게 해줬다
특정한 조건을 만족하는 아이템들을 array에서 삭제하는 건
.removeAll을 사용해서 가능함
dailyIdentifier의 키가 동일한 애들은 다 삭제되게 해줬다!
근데...
이러면 다 삭제되잖음
List로 보여지고 있는 개별적인 elements도 삭제가 가능하게 해줘야겠지
.onDelete로 가능한데 지금 기존의 timeList에도 접근해서 같이 삭제를 해줘야 하잖음?
어떻게 해줘야할까?
vm에서 offSets를 넘겨받아서 timeList에서 dayTimeList의 index에 해당하는 아이템의 id와 timeList의 id가 동일한 애를 찾고 remove를 이용해서 삭제, 그리고 dayTimeList에도 마찬가지로 삭제해줬다!!
마지막으로 해줄건 Delete 버튼을 눌렀을 때 alert이 뜨게 해주는 거!
참 alert이 deprecated 되서 confirmationDialog로 구현해줌!
Lottie 써서 애니메이션 추가해야할 거 같음
로티 깃
로티가 UIKit으로 구현이 가능하지만 UIViewRepresentable을 사용해서 SwiftUI에서도 사용가능하게 해주자
SPM으로 설치해주고 새로운 LottieView라는 swift파일을 만들어줌
그리고 UIViewRepresentable을 채택해주면 경고가 나오는데
typealias로 UIViewType을 LottieAnimationView로 한 다음에 fix 버튼 눌러주자
그럼 LottieAnimationView에 맞는 메소드들이 구성됨
typealias는 다시 지워주고!!
JSON 로티파일 임포트 한 다음에
import SwiftUI
import Lottie
struct LottieView: UIViewRepresentable {
func makeUIView(context: Context) -> Lottie.LottieAnimationView {
let animationView = LottieAnimationView(name: "LottieProgressView")
animationView.play()
return animationView
}
func updateUIView(_ uiView: Lottie.LottieAnimationView, context: Context) {
}
}
임포트한 JSON파일 이름을 LottieAnimationView의 이름으로 넣어주면 됨
그리고 사용하고자 하는 곳에서 방금 만들어준 LottieView를 불러와주면 5초정도 animation이 되는 걸 볼 수 있습니다~!
다시 로티뷰로 돌아와서 재사용 가능하게 바꿔줍시다
name이랑 loopMode 밖으로 빼줫으~!
근데 스케일 크기 안바껴서 아래처럼 LottieView 바꿔줌
struct LottieView: UIViewRepresentable {
typealias UIViewType = UIView
let name: String
let loopMode: LottieLoopMode
func makeUIView(context: UIViewRepresentableContext<LottieView>) -> UIView {
let view = UIView(frame: .zero)
//Add Animation
let animationView = LottieAnimationView(name: name)
animationView.loopMode = loopMode
animationView.play()
view.addSubview(animationView)
animationView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
animationView.widthAnchor.constraint(equalTo: view.widthAnchor),
animationView.heightAnchor.constraint(equalTo: view.heightAnchor)
])
return view
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<LottieView>) {
}
}
로티 루프한 거 stop하는 기능 못찾아서 그냥
play중인 뷰 stop중인 뷰 두개 만들고 transition 처리해줌
숫자가 계속해서 바뀌는데 Digit의 크기가 숫자에따라 바껴서 그럼
요거 적용해주면 됩니다
monoSpaced()도 텍스트에서 같은 용도임
https://sarunw.com/posts/swiftui-tabview-color/
이걸로는 해결 안됐고..
Appearnce를 수정해주는 걸로 색이 아까처럼 스크롤뷰 탭하면 달라지는 건 고쳐졌다!
결국 찾은 방법은 App파일에서 init될 때 Tabbar Appearnce 를 override해주는 거!
그러고 나서 .toolbar랑 .toolbarBackground를 적용해줌
탭바 아이템 색 바꾸는 건
AccentColor 바꿔주면 된다!!
NavigationBar Appearance도 바꿔줌!!