ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • SwiftUI - refreshable
    SwiftUI 2022. 12. 8. 10:16

    안녕하세요. 그린입니다🍏

    이번 포스팅에서는 SwiftUI의 List에서 사용 가능한 refreshable이라는 새로 고침 기능에 대해 알아보겠습니다🙌

     

    우선 해당 기능은 iOS 15 부터 사용이 가능해요!

    참고로 WWDC2021에서 소개되었습니다.

    비교적 어떻게 보면 최신 메서드이기에 현업에서 13, 14부터 지원을 한다면 사용할 순 없습니다.

    그렇지만 앞으로 미니멈 타겟은 점차 올려갈것이고 배워두면 좋기에 이참에 한번 트라이 해봅니다🙌

     

    여러분 모두 테이블뷰 혹은 컬렉션뷰로 된 즉, 리스트로 데이터가 뿌려진 뷰의 경우 최상단에서 아래로 드래깅 시 새로 고침이 되면서 데이터를 최신으로 갱신하는 그런 기능을 가진 앱을 아주 많이 보셨고 익숙하실거라 생각합니다.

    모두에게 사실은 친숙한 기능이긴한데 SwiftUI에서 API로 제공하진 않았습니다.

    그런데 15에서부터 아주 손쉽게 사용 가능하도록 제공이 되었죠😀

     

    그럼 거두절미하고 바로 어떤 메서드인지 어떻게 사용하는지 보시죠!

     

    refreshable?

    SwiftUI에서 List의 뷰를 새로 고침 해줍니다.

    설명 끄읕~~👏👏👏

    한마디로 정의가 끝났습니다.

    해당 메서드를 뷰 모디파이어로 붙이면 아래 드래깅하여 새로 고침을 할 수 있도록 제공해준다는거죠.

    func refreshable(action: @escaping () async -> Void) -> some View

    선언은 이렇습니다.

    해당 뷰 모디파이어를 통해 async한 탈출 클로저를 실행시킵니다.

    주로 최신의 데이터를 다시 fetch하는 작업이 이뤄지는게 맞을것 같아요.

    사용자가 새로 고침을 요청할 때 발생하는 SwiftUI가 실행하는 비동기 처리기죠.

    실행 코드 앞 await 키워드를 사용해 비동기 호출을 해주도록 합니다.

    위와 같은 메서드를 사용하면 리스트에서 아래 드래깅을 통해 새로 고침을 해주지만 이를 커스텀한 뷰에 적용할 수 있습니다.

    즉, 버튼을 눌러 새로 고침을 한다던지 등의 조금 더 커스텀한 구성이 RefreshAction 인스턴스 설정으로 가능하죠.

    이건 또 아래에서 알아보겠습니다🙋🏻

     

    그럼 우선 refreshable 메서드를 예시를 통해 사용해보겠습니다!

     

    refreshable 사용하기

    import SwiftUI
    
    struct ContentView: View {
      @State var colors: [Color] = []
      
      var body: some View {
        VStack(spacing: 10) {
          Text("Colors")
            .font(.largeTitle)
            .padding(.top, 50)
          
          List(colors, id: \.self) { color in
            RoundedRectangle(cornerRadius: 10)
              .frame(width: 300, height: 50)
              .foregroundColor(color)
          }
          .onAppear {
            colors = initColors()
          }
          .refreshable {
            await colors.append(contentsOf: addColors())
          }
          
          Spacer()
        }
      }
      
      private func initColors() -> [Color] {
        return [.yellow, .green, .indigo, .orange, .purple]
      }
      
      private func addColors() async -> [Color] {
        return [.red, .blue, .gray]
      }
    }

    저는 별도 통신을 해주지 않고 야매로 데이터를 넣었습니다!

    그렇기에 await, async를 사실 사용하지 않아도 무방하긴 해요ㅎㅎ

    우선 List에 다양한 색상의 둥근 사각형을 초기에 5개 만들어주고 이후 refresh 될 때 3개를 추가해주는 로직입니다.

     

    그럼 어떻게 나오는지 봐볼까요?

    위와 같이 아래로 드래깅 시 새로 고침의 인디케이터가 돌고 정의한 색상이 추가되어 뷰가 업데이트 되는것을 확인할 수 있습니다.

    아주 사용이 쉽습니다ㅎㅎ

    추후에 실 데이터 통신의 역할을 addColors 메서드 대신 갈아끼워 데이터를 갱신해오는 로직을 가져가면 됩니다🙌

     

    그럼 이어서 아까 짚고 넘어가자던 RefreshAction에 대해 알아보겠습니다!

     

    RefreshAction?

    위에서 간단히 언급했듯이 새로 고침을 시작할 수 있도록 해주는 작업입니다.

    struct RefreshAction

    구조체 타입이며, 기본적으로 refresh 환경 값에 해당 인스턴스가 포함되어 있어 기본 뷰의 Environment가 새로 고침을 제공해주죠.

    디폴트하게 이 Environment의 값은 nil이지만 refreshable 메서드를 이용하였을때 지정한 처리를 하는 새로 고침 작업을 해줄 수 있습니다.

    이제 RefreshAction을 이용해 커스텀한 뷰를 만들어 새로 고침을 해줄 수 있습니다.

    refresh Environment 변수를 이용하면 됩니다🙌

    아래 예시를 들어보겠습니다. (공식문서 참고한 예시!)

    struct RefreshableView: View {
      @Environment(\.refresh) private var refresh
    
      var body: some View {
        Button("Refresh") {
          Task {
            await refresh?()
          }
        }
        .disabled(refresh == nil)
      }
    }

    refresh를 callAsFunction 이용해 직접 정의하고 호출하는 메서드로 사용할 수 있습니다.

    여기서 callAsFunction은 새로 고침 작업을 시작하는 메서드입니다.

    호출이 비동기적이므로 Task와 await를 이용합니다.

     

    설명이 조금 복잡할 수 있는데 사용은 어렵지 않으니 바로 사용법을 보시죠🕺🏻

     

    커스텀하게 새로 고침 기능 사용하기

    import SwiftUI
    
    struct ContentView: View {
      @State var colors: [Color] = []
      
      var body: some View {
        VStack(spacing: 10) {
          Text("Colors")
            .font(.largeTitle)
            .padding(.top, 50)
          
          List(colors, id: \.self) { color in
            RoundedRectangle(cornerRadius: 10)
              .frame(width: 300, height: 50)
              .foregroundColor(color)
          }
          .onAppear {
            colors = initColors()
          }
          
          CustomRefresher()
            .refreshable {
              await colors.append(contentsOf: addColors())
            }
          
          Spacer()
        }
      }
      
      private func initColors() -> [Color] {
        return [.yellow, .green, .indigo, .orange, .purple]
      }
      
      private func addColors() async -> [Color] {
        return [.red, .blue, .gray]
      }
    }
    
    struct CustomRefresher: View {
      @Environment(\.refresh) private var refresh
      
      var body: some View {
        Group {
          if let refresh = refresh {
            Button(
              action: {
                Task {
                  await refresh()
                }
              },
              label: {
                Text("Refresh Button")
              }
            )
          }
        }
      }
    }

    CustomRefresher라는 뷰를 만들어줍니다.

    refresh Environment 변수를 가지죠!

    버튼의 action 동작으로 비동기 작업을 위해 Task에 refresh의 callAsFunction을 호출해주도록 합니다.

    이 커스텀한 뷰를 원하는곳에 배치하고 위에서 알아본 refreshable 메서드를 통해 구현해주면 끝🙌

     

    그럼 요렇게 버튼을 눌렀을때 데이터가 추가되고 뷰가 업데이트 됩니다👍

     

    마무리

    이렇게 오늘도 SwiftUI의 적당한 괜찮은 기능을 알아봤습니다!

    해당 위 예시 코드들은 아래 제 깃헙에도 있으니 참고하실분들은 참고하셔도 좋겠습니다😊

    https://github.com/GREENOVER/playground/tree/main/refreshable

     

    GitHub - GREENOVER/playground: 학습을 하며 간단한 예제들을 만들어 보는 작고 소중한 놀이터

    학습을 하며 간단한 예제들을 만들어 보는 작고 소중한 놀이터. Contribute to GREENOVER/playground development by creating an account on GitHub.

    github.com

     

    [참고자료]

    https://developer.apple.com/documentation/swiftui/view/refreshable(action:) 

     

    Apple Developer Documentation

     

    developer.apple.com

    https://developer.apple.com/documentation/swiftui/refreshaction

     

    Apple Developer Documentation

     

    developer.apple.com

Designed by Tistory.