ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • SwiftUI - DisclosureGroup & OutlineGroup
    SwiftUI 2022. 9. 29. 10:24

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

    이번 포스팅에서는 SwiftUI의 Lists에 속해있는 타입인 DisclosureGroup & OutlineGroup에 대해 학습해보겠습니다🙌

     

    우선 이 둘이 대략적으로 어떤건지 간단히 설명해볼께요.

    단순하게 생각해서 접고 펼 수 있는 파일인 트리 구조의 형태를 가지게 할 수 있는 뷰라고 보면 됩니다.

    이정도로만 설명해서는 감이 안오실 수도 있겠지만 실제로 아래 구현된걸 보면 바로 빡 아~ 이런거 할거에요😄

    아..! 참고로 이 두가지는 SwiftUI라고 다 사용할 수 있는것이 아니고 iOS 14 이상에서만 제공합니다🕺🏻

     

    그럼 이건 코드가 더 쉽게 이해될 수 있으니 바로 본론으로 가볼께요!

     

    DisclosureGroup?

    제어의 상태에 따라 하위 컨텐츠를 표시하거나 숨길 수 있는 뷰입니다.

    선언은 아래와 같습니다.

    struct DisclosureGroup<Label, Content> where Label : View, Content : View

    제네릭하게 Label을 받고 하위 담길 Content를 받아 구성하는 형식이죠.

    즉 해당 뷰는 내용을 식별할 수 있는 Label과 표시 및 숨김 처리 할 수 있는 하위 뷰로 컨트롤합니다.

    이건 정말 코드와 동작을 보는게 훨 쉬우니 바로 고고...!!

     

    DisclosureGroup 구현

    import SwiftUI
    
    struct DisclosureGroupView: View {
      @State private var expanded: Bool = false
      
      var body: some View {
        DisclosureGroup("1번 그룹", isExpanded: $expanded) {
          Text("1번 그룹에 속한 텍스트")
          DisclosureGroup("2번 그룹") {
            Text("2번 그룹에 속한 텍스트")
          }
        }
      }
    }

    아주 심플하게 이렇게 구성해볼께요.

    그럼 일단 동작은 어떻게 될까요?

    요런식입니다.

    위에서 expanded라는 펼치고 숨기는 상태를 제어하는 변수를 만들어줬습니다.

    그리고 1번 즉 최상위에서 DisclosureGroup을 만들고 이 변수를 통해 넣어주면 내부 구현을 통해 이 값을 사용자의 액션에 따라 바꿔줍니다.

    isExpanded 파라미터는 Binding<Bool> 타입인것이죠.

    public init(_ titleKey: LocalizedStringKey, isExpanded: Binding<Bool>, @ViewBuilder content: @escaping () -> Content)

    그리고 하위에 Content로 원하는 텍스트를 넣거나 또 이어서 DisclosureGroup을 만들어 넣을 수도 있습니다.

    여기서 중요한건 두 DisclosureGroup은 상하위 개념으로 속하고 있지만 펼침 숨김에 대해서는 따로 가져가거나 같이 가져갈 수도 있습니다.

    따로 가져가려면 아래와 같이 별도 변수로 컨트롤 해주면 됩니다.

    import SwiftUI
    
    struct DisclosureGroupView: View {
      @State private var firstExpanded: Bool = false
      @State private var secondExpanded: Bool = true
      
      var body: some View {
        DisclosureGroup("1번 그룹", isExpanded: $firstExpanded) {
          Text("1번 그룹에 속한 텍스트")
          DisclosureGroup("2번 그룹", isExpanded: $secondExpanded) {
            Text("2번 그룹에 속한 텍스트")
          }
        }
      }
    }

    이렇게 입맛대로 커스텀한 DisclosureGroup를 만들어 파일 구조처럼 뷰를 만들 수 있습니다.

     

    자 다음으로 OutlineGroup은 DisclosureGroup와 어떤 차이를 가지는지 보시죠!

     

    OutlineGroup?

    트리 구조로 식별된 데이터의 기본 컬렉션에서 요청 시 뷰 및 DisclosureGroup을 계산하는 구조라고 설명합니다.

    음... 조금 이해가 안갈 수 있는데 핵심은 이거입니다.

    DisclosureGroup과 비슷한 개념을 사용한다와 트리 구조로 View를 나타낸다!

    이 두가지가 핵심이라고 생각해요.

    선언은 아래와 같습니다.

    struct OutlineGroup<Data, ID, Parent, Leaf, Subgroup> where Data : RandomAccessCollection, ID : Hashable

    선언이 DisclosureGroup보다 쪼오금 더 복잡해보이죠?

    그치만 별거 없을거에요.

    Disclosure View를 사용해서 데이터 계층을 트리 구조로 볼 수 있게 할 때 사용한다고 공식문서 개요에 나와있습니다.

     

    여기서 다섯개의 제네릭 파라미터를 통해 해당 인스턴스를 정의하고 만들 수 있습니다.

     - Data: 트리 구조의 데이터에서 차일드를 포함하는 컬렉션 유형

     - ID: 식별자

     - Parant: 하위 속성이 nil이 아닌 요소의 시각적 표현 유형

     - Leaf: 하위 속성이 nil인 요소의 시각적 표현 유형

     - Subgroup: 일반적으로 하위 항목을 표시하고 숨기기 위한 메커니즘이 있는 상위 뷰와 하위 항목을 나타내는 뷰를 그룹화하는 유형

     

    우리는 이 핵심만 알면 사용에 별거 없어요.

     

    그럼 바로 구현으로 보시죠🏃🏻

     

    OutlineGroup 구현

    import SwiftUI
    
    struct FileItem: Hashable, Identifiable, CustomStringConvertible {
      var id: Self { self }
      var name: String
      var children: [FileItem]? = nil
      var description: String {
        switch children {
        case nil:
          return "📄 \(name)"
        case .some(let children):
          return children.isEmpty ? "📂 \(name)" : "📁 \(name)"
        }
      }
    }
    
    struct OutlineGroupView: View {
      let data =
      FileItem(
        name: "GREEN",
        children: [
          FileItem(
            name: "GREEN1229",
            children: [
              FileItem(
                name: "Posting",
                children:  [
                  FileItem(
                    name: "SwiftUI.jpg"),
                  FileItem(name: "UIKit.jpg")]),
              FileItem(
                name: "Simulator",
                children: [
                  FileItem(name: "Run.mp4")]),
              FileItem(name: "Etc", children: [])
            ]),
          FileItem(
            name: "Hobby",
            children: [
              FileItem(name: "Travel", children: [])
            ])
        ])
      
      var body: some View {
        OutlineGroup(data, children: \.children) { item in
          Text("\(item.description)")
        }
      }
    }

    어질어질 하시나요?

    뜯어보면 별거 없습니다!

    보시면 FileItem이라는걸 별도로 먼저 만들어서 편리하게 사용할 수 있습니다.

    즉 FileItem으로 나중에 실제 컨텐츠 뷰에서 data로 쓰일 수 있게 id, name, children, description의 프로퍼티를 정의합니다.

    그리고 data는 FileItem 타입의 인스턴스로 원하는 파일 구조의 data 형식을 생성하면 됩니다.

    마지막으로 컨텐츠뷰에서 OutlineGroup을 사용해 요구하는 data 파라미터는 만들어진 data로 children은 역시 만든 data의 children 프로퍼티의 키패스를 가져와 넣어줍니다.

    그리고 뷰를 보여주면 끝입니다.

    구동은 아래와 같아요!

    보시다 시피 파일 구조로 우리가 원했던 구동이죠?

    DisclosureGroup과 작동과 보기에는 다 유사합니다.

    OutlineGroup은 별도 expanded 변수 없이도 각 하위 그룹들이 별개로 동작할 수 있습니다.

    또한 OutlineGroup가 DisclosureGroup가 다른 하나의 차이는 하위 즉 Child에 대해 OutlineGroup으로 생성하지 않아도

    알아서 파일 구조로 만들어줍니다.

    이게 어떻게 보면 조금 편리할 수 있는 장점이겠네요.

    DisclosureGroup은 하위에서도 매번 DisclosureGroup로 감싸 만들어줘야하니까요.

    다만 FileItem 타입을 만들고 data 인스턴스를 만드는 과정을 거칠 수도 있으니 선택하기 나름일것 같긴합니다.

     

    마무리

    자 오늘은 많이 사용될것 같은 두 요소에 대해 알아봤어요!

    사실 저는 사용을 거의 안해봤는데 지식 +1이 되었네요ㅎㅎ

    역시나 선언형 UI는 구현단에서 편리하게 사용될 수 있도록 해놓았군요👍

    오늘 공식문서를 바탕으로 구현해본 코드는 아래 제 깃헙에 올려두었으니 필요하시면 관람을..https://github.com/GREENOVER/playground/tree/main/DisclosureGroup_OutlineGroup

     

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

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

    github.com

     

    [참고자료]

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

     

    Apple Developer Documentation

     

    developer.apple.com

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

     

    Apple Developer Documentation

     

    developer.apple.com

Designed by Tistory.