오늘 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 |