본문 바로가기

분류 전체보기

(345)
AutoScrolling #05 Tab을 전환하는 방법은 반드시 하나만 있어야 하는 건 아니다. 지금처럼 Tab들을 좌우로 Swipe 하여 이동할 수도 있지만, Tab들의 이름을 직접 선택해 다음 Tab으로 이동하는 방식도 고려해 볼 수 있다. 이번엔 지금까지 구현한 Tab Indicator에 Tap Gesture를 추가해 Tab 간의 이동을 구현해 본다. TabGesture 추가 before func TabIndicatorView() -> some View { GeometryReader { let size = $0.size let tabWidth = size.width / 3 LazyHStack(spacing: 0) { ForEach(Tab.allCases, id: \.rawValue) { tab in Text(tab.rawValue..
AutoScrolling #04 이번에는 계산된 pageOffset을 기반으로 스크롤의 진행도를 계산한다. Home before let pageOffset = minX - (size.width * CGFloat(tab.index)) print(pageOffset) } 계산된 pageOffset를 출력하는 부분에서 이를 진행하면 될 것이다. after let pageOffset = minX - (size.width * CGFloat(tab.index)) let pageProgress = pageOffset / size.width scrollProgress = pageProgress } pageOffset을 다시 화면의 너비(사진의 너비)로 나누어 주면 다음과 같은 형태로 변환이 된다. imageView의 위치값(offset)이 progr..
AutoScrolling #03 이번엔 Tab Indicator에 적절한 animation을 추가하기 위해 필요한 준비를 해 본다. Tab before enum Tab: String, CaseIterable { case dance = "Dance" case fruite = "Fruite" case mirror = "Mirror" case night = "Night" case road = "Road" } after enum Tab: String, CaseIterable { case dance = "Dance" case fruite = "Fruite" case mirror = "Mirror" case night = "Night" case road = "Road" var index: Int { return Tab.allCases.firstI..
AutoScrolling #02 화면 구성에 필요한 기본 인터페이스를 디자인한다. 사진을 표시할 ImageView들로 구성된 TabView와 해당 TabView와 연동되는 Tab Indicator를 구성한다. ImageView extension Home { func TabImageView(_ tab: Tab) -> some View { GeometryReader { let size = $0.size Image(tab.rawValue) .resizable() .aspectRatio(contentMode: .fill) .frame(width: size.width, height: size.height) .clipped() } .ignoresSafeArea(.container, edges: .bottom) } } 표시할 이미지의 크기를 기기의..
AutoScrolling #01 프로젝트는 MV 아키텍처를 사용한다. MVVM이나 MVC 대신 해당 아키텍처를 사용하도록 Apple을 권장하고 있고, 프로젝트의 구성이 조금 더 단순해진다는 장점이 있다. Assets Assets에는 TabView에 표시할 사진들을 추가한다. 원본은 3000 * 4000 크기의 사진들이지만 너무 과한 해상도의 사진은 비효율적이기에 이후에는 퍼포먼스 문제로 853 * 1280 수준으로 다운사이징 진행했다. Tab.swift // // Tab.swift // AutoScrolling // // Created by Martin.Q on 2023/03/14. // import SwiftUI /// Enum Tab Cases /// - rawValue: Asset Image Name enum Tab: String..
AutoScrolling #00 TabView와 Custom Indicator를 가지는 인터페이스를 구현한다. 크기를 조절하는 등의 방식으로 App의 배너에 유용히 사용할 수 있을 것으로 생각된다. 따라 해볼 강의는 TabView와 TabIndicator 까지만 구현하지만 심미성을 위해 약간의 디자인 요소를 추가해 본다. 기능 참고 강의는 아래와 같은 기능을 가지고 있다. 여러 사진을 하나씩 표현하는 TabView TabView와 연동되는 Custom Tab Indicator Tab Indicator의 양 끝에 적용되는 리바운드 효과 Tab Indicator에 적용되는 Animation 효과 추가적으로 구현할 기능은 다음과 같다. 표시되는 사진의 평균적인 색상을 구하기 구해진 평균 색상을 사용해 사진과 조화로운 Tab Indicator..
A&K XB10의 배터리를 교체해 보자 서론 자취방이라 스피커는 없어도 된다며 저소음 라이프를 즐기던 때가 있었다. 블루투스 송신기와 리시버를 사용했었는데 자그마한 사이즈에 평가도 좋던 XB10이 되시겠다. 아이리버 Astell&Kern AK XB10 (정품) : 다나와 가격비교 가전/TV>홈시어터/오디오>앰프/리시버>AV리시버, 요약정보 : 블루투스 수신기 / 블루투스 v4.1 / 프로파일: A2DP, AVRCP, HSP, HFP / 코덱: aptX, aptX HD, AAC, SBC / 입출력: AUX / 핸즈프리 / 내장배터리 / 충전시 prod.danawa.com 당시엔 매물이 없어 중고나라에 잠복하다 겨우 가져왔었고, 꽤나 잘 썼지만 갑자기 불어온 스피커 바람에 박스 안으로, 서람 안으로 방치만 되기 시작했다. 심야에 음악을 들을 일이..
06. 더 나아가기 더 나아가기 | 난이도 선택하기, 개선하기 강의는 끝났지만 난이도를 선택하는 부분은 빠져있다. enum FastingPlan: String { case beginner = "12:12" case intermediate = "16:8" case advanced = "20:4" var fastingPeriod: Double { switch self { case .beginner: return 12 case .intermediate: return 16 case .advanced: return 20 } } } beginner와 intermediate, advance를 화면에서 직접 선택할 수 있도록 추가로 구현한다. 난이도 선택하기 | 인터페이스 구성하기 현재 앱의 화면은 위와 같고, 난이도를 선택하기 위해 다..
05. 기능 구현 #3 기능 구현 #3 | Upcoming time, Percentage, 실제 시간 반영하기 Upcoming time 타이머의 상태가 notStarted이면 elapsedTime과 remainingTime을 표시하는 것은 부자연스럽다. 선택된 난이도에 따라서 현재시간을 기준으로 다음 식사 시간을 나타내도록 수정한다. // MARK: Timer VStack(spacing: 30) { // MARK: Upcoming Time if fastingManager.fastingState == .notStarted { VStack(spacing: 5) { Text("Upcoming fast") .opacity(0.7) Text("\(fastingManager.fastingPlan.fastingPeriod, specifie..
04. 기능 구현 #2 기능 구현 #2 | Timer 구현 Timer 구현 | fastingTime, feedingTime 계산하기 class FastingManager: ObservableObject { @Published private(set) var fastingState: FastingState = .notStarted @Published private(set) var fastingPlan: FastingPlan = .intermediate @Published private(set) var startTime: Date @Published private(set) var endTime: Date var fastingTime: Double { return fastingPlan.fastingPeriod } var feeding..