나의 발자취

챌린지 참여 상태에 따라 UI 변경하기(@ObservedObject, @StateObject) 본문

프로젝트

챌린지 참여 상태에 따라 UI 변경하기(@ObservedObject, @StateObject)

달모드 2024. 12. 12. 20:37

개발하려는 사항

유저가 이미 참여하고 있는 챌린지면, ChallengeDetailView에서 "나도 참여하기" 버튼이 보이면 안되고 대신 progress bar, 그리고 카메라 버튼이 보여야한다.

유저의 챌린지 참여 상태를 계속 기억해야하고, 유저의 챌린지 참여 기간이 끝날때까지 유저는 이 뷰를 봐야한다.

유저가 참여중인 챌린지는 챌린지 뷰모델에서 fetchParticipatingChallenges, isParticipatingIn 와 관련되어있다.

 

 

일단, 챌린지 디테일 뷰에서, 챌린지 뷰모델을 참고하고 있다.

이 뷰 모델의 어노테이션을 ObservedObject에서 StateObject로 바꿔준다.

 

1. @ObservedObject

  • 뷰가 다시 그려질 때마다 새로운 인스턴스가 생성될 수 있음
  • 부모 뷰로부터 전달받은 객체를 관찰할 때 사용
  • 뷰의 생명주기에 종속
  • 데이터가 예기치 않게 초기화될 수 있음

 

고로 이러한 이유로 계속 계속 뷰가 초기화되는것이었다! 따라서 로 바꿔준다.

 

 

2. @StateObject

  • 뷰가 다시 그려져도 동일한 인스턴스를 유지
  • 뷰가 직접 소유하는 객체를 생성할 때 사용
  • 뷰의 생명주기와 독립적
  • 데이터가 안정적으로 유지됨

 

우리의 경우 ChallengeDetailView에서:

  • 챌린지 참여 상태를 안정적으로 유지해야 함
  • 뷰가 다시 그려져도 데이터가 초기화되면 안됨
  • 뷰가 직접 ViewModel을 소유하고 관리해야 함

 

따라서 @StateObject  적합하다. 이렇게 하면 유저의 챌린지 참여 상태가  안정적으로 유지될 수 있다.

 

 

 


그리고, 챌린지 참여 상태에 따른 progress bar를 추가로 구현했다.

여기서는 ProgressSection에 ChallengeViewModel을 @ObservedObject로 선언했다. 

struct ProgressSection: View {
    @ObservedObject var viewModel: ChallengeViewModel

 

그 이유는, ProgressSection의 경우 ChallengeDetailView의 자식 뷰이다. 그리고 ChallengeViewModel 인스턴스를 부모 뷰(ChallengeDetailView)로부터 전달받고 있다.

 

따라서 아래와 같이, ChallengeDetailView에서는 ProgressSection 뷰에게 뷰모델을 전달해주어야한다.

 

@StateObject가 아닌 이유?

 
@StateObject는 뷰가 직접 소유하고 생성하는 객체에 사용하는데, 지금의 경우 경우 ProgressSection viewModel을 생성하지 않고
 
단지 부모로부터 전달받아 관찰만 하는 것이다. 

 

// ChallengeDetailView (부모)
struct ChallengeDetailView: View {
@StateObject var viewModel: ChallengeViewModel // 여기서 생성&소유

var body: some View {
       ProgressSection(viewModel: viewModel) // 자식에게 전달
}
}

// ProgressSection (자식)
struct ProgressSection: View {
@ObservedObject var viewModel: ChallengeViewModel // 부모로부터 전달받음
...

 

즉, 데이터의 소유권은 ChallengeDetailView에 있고, ProgressSection은 그저 관찰자 역할만 하므로 이때는 @ObservedObject 사용하는 것이다. 

 

 

Comments