_Rey
프로그래뭄
_Rey
전체 방문자
오늘
어제
  • 분류 전체보기 (118)
    • Life (2)
    • iOS (49)
      • iOS (13)
      • Swift (19)
      • UIKit (0)
      • RxSwift (6)
      • SwiftUI (11)
    • Design Pattern (14)
      • GoF - Creational Patterns (6)
      • GoF - Structural Patterns (7)
    • Data Structure & Algorithm (48)
      • Data Structure (3)
      • Algorithm (8)
      • Advent of Code 2024 (1)
      • 코테 스터디 TIL (36)
    • English (2)
    • Book (2)
      • Clean Architecture (2)

블로그 메뉴

  • Instagram
  • Github
  • Naver Blog

공지사항

  • Hello, Programoom

인기 글

hELLO · Designed By 정상우.
_Rey

프로그래뭄

[SwiftUI] View Containers
iOS/SwiftUI

[SwiftUI] View Containers

2022. 4. 24. 01:16

오늘 SwiftUI의 UI 요소들을 알아보자.

오늘의 문서 https://developer.apple.com/documentation/swiftui

 

View Containers에는

HStack, VStack, ZStack, LazyHStack, LazyVStack, LazyHGrid, LazyVGrid, GridItem, List, ForEach, ScrollView, Form, Group, GroupBox, Section, Spacer, Divider, NavigationView, TabView, Alert, ActionSheet, EmptyView, AnyView, TupleView 등 굉장히 다양한 요소들이 존재한다.

 

이걸 전부 하나의 View에 담아보려고 했는데, TabView와 NavigationView가 있어 구현한다면 혼돈이 될게 뻔하다

그래서 먼저 TabView와 NavigationView를 썼다.

struct ViewContainers: View {
    var body: some View {
        TabView{
            ViewContainerTab1()
            .tabItem {
                Text("ViewContainers1")
            }
            
            ViewContainerTab2()
            .tabItem {
                Text("ViewContainers2")
            }
            
            ViewContainerTab3()
            .tabItem {
                Text("ViewContainers3")
            }
        }
    }
}

오른쪽 사진을 보면 알겠지만, NavigationView로부터 왔다는걸 알 수 있다. (왼쪽 위 Back Item)

이제 두 개 해결했고, 탭을 하나씩 봐보자!

 

ViewContainersTab1

struct ViewContainerTab1: View {
    @State var isActionSheet: Bool = false
    @State var isConfirmationDialog: Bool = false
    
    var body: some View {
        ScrollView(.vertical) {
            VStack(spacing: 25){
                Text("전체 View: NavigationView, TabView")
                Text("1번 탭: HStack, VStack, ZStack\nLazyHStack, LazyVStack\nScrollView, ForEach, Section\nActionSheet, ConfirmationDialog\nView Container가 최대한 다 섞인 혼종 View입니다.")
            }
            
            VStack(spacing: 15) {
                Text("해당 View는 VStack으로 감싸져있습니다.")
                HStack(spacing: 20){
                    Text("☀️")
                    Text("spacing이 20인 HStack입니다.")
                }
                Button("ActionSheet 버튼") {
                    self.isActionSheet.toggle()
                }
                Button("ConfirmationDialog 버튼") {
                    self.isConfirmationDialog.toggle()
                }
                ZStack {
                    Text("혼돈의 ZStack")
                    Text("ㅋㅌㅍㅎ")
                        .foregroundColor(.green)
                    Text("ㅂㅅㅇㅈㅊ")
                        .foregroundColor(.red)
                    Text("ㄱㄴㄷㄹㅁ")
                        .foregroundColor(.blue)
                    Text("ZStack의 제일 위")
                        .foregroundColor(.gray.opacity(0.5))
                }
            }
            .padding()
            
            LazyHStack(spacing: 20, pinnedViews: [.sectionHeaders]) {
                ForEach(1..<6) { i in
                    Section {
                        Text("LazyHStack 속 ForEach문 No.\(i)")
                        ForEach(0..<5) { j in
                            Text("Section Text\(j)")
                        }
                    } header: {
                        Text("LazyHStackSection")
                    }
                }
            }
            
            LazyVStack(spacing: 20, pinnedViews: [.sectionHeaders]) {
                ForEach(1..<6) { i in
                    Section {
                        Text("LazyVStack 속 ForEach문 No.\(i)")
                        ForEach(0..<5) { j in
                            Text("Section Text\(j)")
                        }
                    } header: {
                        Text("LazyVStackSection No.\(i)")
                    }
                }
            }
        }
        .actionSheet(isPresented: self.$isActionSheet) {
            ActionSheet(title: Text("ActionSheet Title입니다."),
                        message: Text("message입니다. actionSheet가 iOS 15.4까지만 지원된다고 합니다. 아쉬우십니까?"),
                        buttons: [.default(Text("default 네")),
                                  .destructive(Text("destructive 아니오")),
                                  .cancel()]
            )
        }
        .confirmationDialog(Text("이게 ConfirmationDialog입니다."),
                            isPresented: self.$isConfirmationDialog,
                            titleVisibility: .visible) {
            Button {
                
            } label: {
                Text("ConfirmationDialog Action")
            }
            Button("Cancel", role: .cancel) {
                
            }
        } message: {
            Text("ConfirmationDialog Message")
        }
    }
}

전체코드는 다음과 같다.

코드를 하나씩 설명하는 것보단 GIF를 보는게 더 이해가 빠를 것 같다.

Lazy*Stack은 View안의 요소들을 전부 생성하지 않고 필요할 때(화면에 보여질 때) 생성하기 때문에 메모리 관리에 좋다고한다.

pinnedView라는 것도 처음 봤는데,

GIF에 보면 스크롤 중에 네이게이션 바 바로 아래 고정되는 문구있다.(LazyVStackSection No.1,2,3)

pinnedView를 sectionHeaders로 설정했기 때문에, Section header에 작성한 Text가 고정되는 것이다.

ActionSheet는 15.4까지만 지원된다고 하며, ConfirmationDialog는 15.0부터 지원된다고 한다.

결국 같은 역할을 하니 버전에 따라 잘 분기하거나, 골라 사용하면 될것같다.

 

ViewContainersTab2

struct ViewContainerTab2: View {
    private var gridItems = [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())]
    private var symbols = ["keyboard", "hifispeaker.fill", "printer.fill", "tv.fill", "desktopcomputer", "headphones", "tv.music.note", "mic", "plus.bubble", "video"]
    private var colors: [Color] = [.yellow, .purple, .green]
    
    var body: some View {
        VStack {
            Text("2번 탭: LazyHGrid, LazyVGrid, GridItem\nList, Form, Group\nGroupBox, Spacer, Divider\n Alert, EmptyView, AnyView, TupleView")
            ScrollView {
                LazyVGrid(columns: self.gridItems) {
                    ForEach((0...100), id: \.self) {
                        Image(systemName: self.symbols[$0 % self.symbols.count])
                            .font(.system(size: 30))
                            .frame(width: 50, height: 50)
                            .background(self.colors[$0 % self.colors.count])
                            .cornerRadius(10)
                    }
                }
            }
            ScrollView(.horizontal) {
                LazyHGrid(rows: self.gridItems) {
                    ForEach((0...100), id: \.self) {
                        Image(systemName: self.symbols[$0 % self.symbols.count])
                            .font(.system(size: 30))
                            .frame(width: 50, height: 50)
                            .background(self.colors[$0 % self.colors.count])
                            .cornerRadius(10)
                    }
                }
            }
            Form {
                Group {
                    Text("I'm Text in Group")
                    Text("I'm Text in Group")
                }
                Text("I'm Text in Form")
            }
            List {
                ForEach((0...15), id: \.self) {
                    Text("Text in List no.\($0)")
                }
            }
        }
    }
}

GridView를 써봤다.

GridItem을 Array로 받는게, Grid에 들어갈 View가 아니라, Column과 Row를 정하기 위해서였다.

 

ViewContainersTab3

struct ViewContainerTab3: View {
    @State var isAnyView: Bool = false
    @State var showAlert: Bool = false
    @State var isViewBuilder: Bool = false
    
    func showAnyView() -> some View{
        if self.isAnyView {
            return AnyView(Day20())
        } else {
            return AnyView(TestView())
        }
    }
    
    @ViewBuilder
    func showViewBuilder() -> some View{
        if self.isViewBuilder {
            Day43()
        } else {
            TestView()
        }
    }
    
    var body: some View {
        ScrollView{
            VStack(spacing: 15) {
                Text("3번 탭: GroupBox, Spacer, Divider\n Alert, EmptyView, AnyView, TupleView")
                GroupBox("GroupBox") {
                    VStack {
                        Divider()
                        Text("GroupBox Content")
                        Spacer()
                        Divider()
                    }
                }
                .padding()
                EmptyView()
                Button("Alert 버튼") {
                    self.showAlert.toggle()
                }
                
                Button("AnyView 버튼") {
                    self.isAnyView.toggle()
                }
                self.showAnyView()
                
                Button("ViewBuilder 버튼") {
                    self.isViewBuilder.toggle()
                }
                self.showViewBuilder()
            }
        }
        .alert("Alert Title", isPresented: self.$showAlert){
            Button("OK") {
                self.showAlert.toggle()
            }
        }
    }
}

AnyView와 @ViewBuilder의 차이를 잘봐야한다.

View긴 View인데 타입이 있는 View를 다뤄야할 때 사용된다.

예시 코드가 제대로 된 사용 예제는 아니지만 AnyView보다는 @ViewBuilder의 사용을 권장한다.

 

HStack, VStack, ZStack, LazyHStack, LazyVStack, LazyHGrid, LazyVGrid, GridItem, List, ForEach, ScrollView, Form, Group, GroupBox, Section, Spacer, Divider, NavigationView, TabView, Alert, ActionSheet, EmptyView, AnyView, TupleView

저작자표시 비영리 변경금지 (새창열림)

'iOS > SwiftUI' 카테고리의 다른 글

[SwiftUI] Shapes  (0) 2022.04.24
[SwiftUI] User Interface Elements  (0) 2022.04.24
[SwiftUI] @GestureState, @Namespace, @ScaledMetric  (0) 2022.04.19
[SwiftUI] @UIApplicationDelegateAdaptor, @NSApplicationDelegateAdaptor  (0) 2022.04.19
[SwiftUI] @Environment, @EnvironmentObject, @FocusedBinding, @FocusedValue  (0) 2022.04.19
    'iOS/SwiftUI' 카테고리의 다른 글
    • [SwiftUI] Shapes
    • [SwiftUI] User Interface Elements
    • [SwiftUI] @GestureState, @Namespace, @ScaledMetric
    • [SwiftUI] @UIApplicationDelegateAdaptor, @NSApplicationDelegateAdaptor
    _Rey
    _Rey
    잘 배워서 다 남주자

    티스토리툴바