iOS 응용 (SwiftUI)

[iOS - SwiftUI] 1. List 형태 UI - @State를 활용하여 로딩 상태, 로드 상태, 실패 상태 띄우기

jake-kim 2024. 4. 22. 00:42

1. List 형태 UI - @State를 활용하여 로딩 상태, 로드 상태, 실패 상태 띄우기

2. List 형태 UI - pull to refresh, 페이지네이션 구현 방법 (List, refreshable, pagination, @Sendable)

3. List 형태 UI - 검색된 결과 UI에 보여지게 하는 방법 (searchable)

상태 처리 핵심 개념 - @State

  • List형태의 UI를 구현하면 항상 로딩, 성공, 실패에 따라 UI를 다르게 보여주어야 하는데 이 때 SwiftUI의 @State를 활용하면 매우 쉽고 빠르게 구현이 가능
  • enum으로 로딩, 성공, 실패에 관한 정의를 하고 이 enum 타입의 프로퍼티를 @State로 선언하면 쉽게 상태 관리가 가능

목표) @State로 3가지 상태화면 만들기

상태 UI 구현

  • UI를 구현할 때 가장 먼저 해야하는 것은 데이터 모델 정의
    • 간혹, 데이터 없이 UI를 먼저 그리는 경우가 있는데 UI를 구현하다보면 결국 그 UI를 띄워야 하는 데이터가 필요하므로 데이터 모델을 가장 먼저 정의하는 것이 중요
enum ViewState {
    case loading
    case success
    case failed
}
  • 이 상태를 표현하는 프로퍼티를 @State 로 선언
    • 첫 상태는 loading 상태로 초기화
import SwiftUI

struct ContentView: View {
    enum ViewState {
        case loading
        case success
        case failed
    }
    
    @State private var state = ViewState.loading
}
  • 이 state를 바인딩하는 것을 var body: some View 안에 구현
    • 네비게이션 타이틀을 사용하기 위해서 Group으로 우선 묶고, 상태가 변하는 테스트를 하기 위해서 onAppear에서 0.5초 후에 failed로 변경
    • (단, 네비게이션 타이틀이 보여지기 위해서는 ContentView를 NavigationView로 감싸야 하는데 이 부분은 3번글에서 계속)
var body: some View {
    Group {
    
    }
    .navigationTitle("This is navigationTitle")
    .onAppear(perform: {
        Task {
            try await Task.sleep(nanoseconds: 500_000_000)
            state = .failed
        }
    })
}
  • 이 state를 기준으로 뷰를 그려주면 되므로 switch 사용
Group {
    switch state {
    case .loading:
        
    case .success:
        
    case .failed:
        
    }
}
...
  • loading 상태일 때는 Loading 뷰와 ProgressView 띄워주기
VStack {
    Text("loading...")
    ProgressView()
}
  • success일때는 단순히 문구 띄워주기
case .success:
    Text("Success!")
  • fail일때는 retry 버튼과 함께 retry 시 성공상태로 변경
VStack {
    Text("Failed!!")
    
    Button("Retry") {
        state = .loading
        Task {
            try await Task.sleep(nanoseconds: 500_000_000)
            state = .success
        }
    }
    .buttonStyle(.borderedProminent)
}
  • 전체 코드
import SwiftUI

struct ContentView: View {
    enum ViewState {
        case loading
        case success
        case failed
    }
    
    @State private var state = ViewState.loading
    
    var body: some View {
        Group {
            switch state {
            case .loading:
                VStack {
                    Text("loading...")
                    ProgressView()
                }
            case .success:
                Text("Success!")
            case .failed:
                VStack {
                    Text("Failed!!")
                    
                    Button("Retry") {
                        state = .loading
                        Task {
                            try await Task.sleep(nanoseconds: 500_000_000)
                            state = .success
                        }
                    }
                    .buttonStyle(.borderedProminent)
                }
            }
        }
        .navigationTitle("This is navigationTitle")
        .onAppear(perform: {
            Task {
                try await Task.sleep(nanoseconds: 500_000_000)
                state = .failed
            }
        })
    }
}

#Preview {
    ContentView()
}

완성)

* 전체: https://github.com/JK0369/ExListSwiftUI.git