-
Notifications
You must be signed in to change notification settings - Fork 1
Eric
Eric Kwon / 권승용 edited this page Jun 20, 2023
·
3 revisions
- 반복적인 운동 이미지를 구현하기 위해, 서로 다른 이미지 두 장을 일정 시간마다 반복적으로 바꿔 보여주는 방식으로 구현하였습니다.
// 한 운동마다 불리는 일회성 메서드
func startExercise() {
self.currentCount = 0
// 1.5초마다 이미지 바꾸기
Timer.scheduledTimer(withTimeInterval: 1.5, repeats: true) { timer in
if !self.isWorkoutPaused {
if self.isWorkoutStopped {
timer.invalidate()
self.currentImageIndex = 0
self.stopWorkout()
}
if self.isPreparingTime {
timer.invalidate()
}
let workoutImagesCount = self.workouts[self.currentWorkoutIndex].workoutImageNames.count
self.currentImageIndex += 1
self.currentImageName = self.workouts[self.currentWorkoutIndex].workoutImageNames[self.currentImageIndex % workoutImagesCount]
}
}
// 1초마다 숫자 세기
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
if !self.isWorkoutPaused {
if self.isWorkoutStopped {
timer.invalidate()
self.currentWorkoutIndex = 0
self.stopWorkout()
}
self.currentCount += 1
// 설정 횟수 모두 도달하게 되면 Rest 하고 다음 인덱스로 넘어가기
if self.currentCount == 45 {
// 만약 마지막 운동이었다면 WorkoutSummary뷰 불러올 수 있도록 flag 변수 설정
if self.currentWorkoutIndex + 1 == self.workouts.count {
timer.invalidate()
self.currentCount = 0
self.isExercising = false
self.isPreparingTime = false
self.isWorkoutStopped = true
// 마지막 운동이 아니라면 RestTimeView 불러올 수 있도록 flag 변수 설정
} else {
timer.invalidate()
self.selectedWorkouts.remove(at: 0)
self.currentCount = 0
self.isExercising = false
self.isPreparingTime = true
}
}
}
}
}
- 남은 운동 시간에 따른 원형 프로그레스 바 구현이 필요하여, Circle과 ZStack, Timer를 사용해 구현해 보았습니다.
struct CircularProgressBar: View {
// 상위 뷰에서 1초마다 변경되는 currentCount 바인딩하여 사용
@Binding var currentCount: Int
var body: some View {
ZStack {
Circle()
.stroke(style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
.fill(LinearGradient(colors: [Color("buttonBackgroundStart"), Color("buttonBackgroundEnd")], startPoint: .topLeading, endPoint: .bottomTrailing))
.frame(width: 100, height: 100)
.overlay(
Circle()
.trim(from: 0, to: progress())
.stroke(style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
.foregroundColor(.red)
.rotationEffect(Angle(degrees: -90))
)
}
.onChange(of: currentCount) { newValue in
print(newValue)
}
}
func progress() -> CGFloat {
return CGFloat(currentCount) / 45
}
}
- 앞으로 남은 운동은 무엇이 있는지 확인하고, 운동에 대한 자세한 설명을 보기 위한 디테일 화면을 커스텀 모달 뷰를 사용해 구현했습니다.
struct ExerciseListModalView: View {
@Binding var workouts: [Workout]
var body: some View {
GeometryReader { geometry in
ScrollView {
ForEach(Array(zip(workouts.indices, workouts)), id: \.1) { index, workout in
VStack(spacing: 0) {
HStack {
if index == 0 {
Text("현재 동작")
} else if index == workouts.count - 1 {
Text("마지막 동작")
} else {
Text("다음 동작")
}
Spacer()
}
.padding(EdgeInsets(top: 0, leading: 25, bottom: 5, trailing: 0))
.foregroundColor(.white)
CustomTableViewCell(imageNames: workout.workoutImageNames, exerciseName: workout.workoutName, instructions: workout.instructions, considerations: workout.considerations)
}
}
}
.background(Color("sheetBackground"))
.edgesIgnoringSafeArea(.bottom)
// bottom sheet가 덜 올라와 있으면 ScrollView 사이즈 줄이기
.if(geometry.frame(in: .global).minY > UIScreen.main.bounds.midY) { view in
view.frame(maxHeight: UIScreen.main.bounds.height * 0.2)
}
.if(geometry.frame(in: .global).minY < UIScreen.main.bounds.midY) { view in
view.frame(maxHeight: UIScreen.main.bounds.height * 0.8)
}
}
}
}