또, 일주일 뒤에 돌아온... 블로그
오늘은 앞서 공부했던 커스텀 뷰 생성에 이어 해당 뷰를 수정하는 것을 정리해볼 것이다.!!
뷰 변경하기
SwiftUI와 함께 제공되는 모든 뷰는 커스터마이징이 필요 없을 정도로 완전히 정확하게 우리가 원하는 모양과 동작을 하는 것이 아니기 때문에 수정자(modifier)를 뷰에 적용하여 변경할 수 있다.
모든 SwiftUi 뷰에는 뷰의 모양과 동작을 변경하는 데 사용될 수 있는 수정자들이 있다. 이 수정자들은 뷰의 인스턴스에서 호출되는 메서드 형태를 취하며 원래의 뷰를 다른 뷰로 감싸는 방식으로 필요한 변경을 한다. 이 말인 즉슨, 동일한 뷰에 여러 변경을 하기 위해 수정자들이 연결될 수 있다는 의미다.
예를 들어, 다음의 코드는 Image 뷰가 허용하는 공간 안에 이미지를 정비율로 표현하도록 구성하는 수정자를 사용한다.
Image(systemName: "car.fill")
.resizable()
.aspectRatio(contentMode: .fit)
추가로, 수정자는 커스텀 하위 뷰에도 적용할 수 있다.
텍스트 스타일로 작업하기
iOS는 애플리케이션이 텍스트를 표시할 때의 텍스트 크기를 사용자가 선택할 수 있게 한다. 만약 텍스트 스타일을 이용하여 뷰의 폰트를 선언했다면, 텍스트 크기는 사용자가 지정한 폰트 크기에 따라 동적으로 맞춰진다. 다음은 현재 사용할 수 있는 텍스트 스타일의 옵션들이다.
아래로 갈수록 굵어진다.
- ultraLight
- thin
- light
- regular
- medium
- semibold
- bold
- heavy
- black
원하는 텍스트 스타일이 없을 경우 폰트 종류와 크기를 선언하여 커스텀 폰트를 적용할 수도 있다. 하지만, 이런 방식은 사용자가 선택한 텍스트 크기와 상관없이 고정된 크기로 표시된다.
Text("Sample Text")
.font(.custom("Copperplate", size: 70))
수정자 순서
수정자들을 연결할 때 수정자들이 적용되는 순서가 중요하다. 다음은 Text 뷰에 border와 padding을 적용하고 있다. 이는 텍스트에 테두리가 생기고 패딩이 적용된다.
Text("Sample Text")
.border(Color.black)
.padding()
순서를 바꾼 코드다. 위와 다르게 아래 코드는 텍스트에 패딩이 적용된 후에 테두리가 생긴다.
Text("Sample Text")
.padding()
.border(Color.black)
즉, 뷰에 적용되는 순서가 다르기 때문에 수정자들의 순서는 매우 중요하다 !!
커스텀 수정자
뷰에 자주 적용되는 대표적인 수정자들을 갖고자 할 때 유용하다. 다음의 수정자는 뷰 선언부에 공통으로 필요한 수정자라고 가정해보자.
Text("Text 1")
.font(.largeTitle)
.background(Color.white)
.border(Color.gray, width: 0.2)
.shadow(color: Color.black, radius: 5, x: 0, y: 5)
계속 위 수정자들을 적용하는 것보다 커스텀 수정자로 묶어서 필요할 때마다 참조하는 방식이 더 좋다. 커스텀 수정자는 ViewModifier 프로토콜을 따르는 구조체로 선언되며, 다음과 같이 구현된다.
struct StandardTitle: ViewModifier {
func body(content: Content) -> some View {
content
.font(.largeTitle)
.background(Color.white)
.border(Color.gray, width: 0.2)
.shadow(color: Color.black, radius: 5, x: 0, y: 5)
}
}
필요한 곳에서 modifier() 메서드를 통해 커스텀 수정자를 전달하여 적용한다.
Text("Text 1")
.modifier(StandardTitle())
Text("Text 2")
.modifier(StandardTitle())
이렇게 하면 여러 뷰에 적용한 수정자를 수동으로 수정할 필요가 없어진다.
기본적인 이벤트 처리
SwiftUI가 데이터 주도적(data-driven)이라고는 했지만, 사용자 인터페이스인 뷰를 사용자가 조작할 때 발생하는 이벤트 처리는 필요하다. Button 뷰와 같은 몇몇 뷰는 사용자의 상호작용을 유도하기 위한 목적으로만 제공된다. 사실, Button 뷰는 여러 다른 뷰를 클릭할 수 있는 버튼으로 바꾸기 위해 사용되기도 한다. Button 뷰는 버튼 내용과 함께 클릭이 감지될 때 호출된 메서드로 선언되어야 한다. 예를 들어, 뷰 전체를 하나의 버튼으로 지정할 수도 있다. 액션 함수를 지정하는 대신, 버튼이 클릭되었을 때 실행될 코드를 클로저로 지정할 수도 있다.
onAppear 메서드와 onDisappear 메서드
레이아웃 안에 뷰가 나타나거나 사라질 때 초기화 작업과 초기화 해제 작업을 수행하기 위해 지정된 뷰에 액션 메서드들을 선언하기도 한다. 바로 onAppear와 onDisappear 인스턴스 메서드다.
Text("Hello World")
.onAppear(perform: {
// 뷰가 나타날 때 수행되는 코드
})
.onDisappear(perform: {
// 뷰가 사라질 때 수행되는 코드
})
커스텀 컨테이너 뷰 만들기
하위 뷰는 뷰 선언부를 작고 가벼우며 재사용할 수 있는 블록으로 나누는 유용한 방법을 제공하지만, 하위 뷰의 한 가지 한계는 컨테이너 뷰의 콘텐트가 정적(static)이라는 점이다. 다시 말해, 하위 뷰가 레이아웃에 포함되는 시점에 하위 뷰에 포함될 뷰를 동적으로 지정할 수 있다. 즉, 하위 뷰에 포함되는 뷰들은 최초 선언부에 지정된 하위 뷰들뿐이다.
3개의 텍스트 뷰가 VStack 안에 포함되고 임의의 간격과 폰트 설정으로 구성된 하위 뷰가 다음과 같이 있다고 하자.
struct MyVStack: View {
var body: some View {
Text("Text Item 1")
Text("Text Item 2")
Text("Text Item 3")
}
.font(.largeTitle)
}
선언부에 MyVStack 인스턴스를 포함시키려면 다음과 같이 참조할 것이다.
MyVStack()
하지만, 간격이 10이며 largeTitle 폰트 수정자를 가진 VStack이 프로젝트 내에서 자주 필요하지만, 사용할 곳마다 서로 다른 뷰들이 여기에 담겨야 한다고 가정하자. 하위 뷰를 사용해서는 이러한 유연성을 갖지 못하지만, 커스텀 컨테이너 뷰를 생성할 때 SwiftUI의 ViewBuilder 클로저 속성을 이용하면 가능하다.
ViewBuilder는 스위프트 클로저 형태를 취하며 여러 하위 뷰로 구성된 커스텀 뷰를 만드는 데 사용될 수 있으며, 이 뷰가 레이아웃 선언부 내에 사용될 때까지 내용을 선언할 필요가 없다. ViewBuilder 클로저는 콘텐트 뷰들을 받아서 동적으로 만들어진 단일 뷰로 반환한다.
다음은 우리의 MyVStack 뷰를 구현하기 위하여 ViewBuilder 속성을 사용하는 예제다.
struct MyVStack<Content: View>: View {
let content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
var body: some View {
VStack(spacing: 10) {
content()
}
.font(.largeTitle)
}
}
이 선언부는 View 프로토콜을 따르며, body에는 VStack 선언부를 포함한다. 하지만 스택에 정적 뷰를 포함하는 대신에 하위 뷰들은 초기화 메서드에 전달되며, ViewBuilder에 의해 처리되어 VStack에 하위 뷰들로 포함될 것이다. 이제 커스텀 MyVStack 뷰는 레이아웃 내에 사용될 서로 다른 하위 뷰들로 초기화될 수 있다.
MyVStack {
Text("Text 1")
Text("Text 2")
HStack {
Image(systemName: "star.fill")
Image(systemName: "star.fill")
Image(systemName: "star")
}
}
지금까지 뷰들을 수정자를 적용하여 구성하고,
커스텀 컨테이너 뷰는 ViewBuilder 클로저 프로퍼티를 이용하여 생성될 수 있음을 알 수 있었다.
사실 겹치는 수정자들도 그냥 냅다 중복시켜서 썼는데, 커스텀 수정자와 커스텀 컨테이너 뷰를 적극적으로 사용해야겠다.
출처 : Swift 기반의 iOS 프로그래밍
'iOS > SwiftUI' 카테고리의 다른 글
[SwiftUI] UserDefaults와 @AppStorage (0) | 2023.02.04 |
---|---|
[SwiftUI] SwiftUI와 Firebase를 이용한 사용자 계정 관리 (회원가입) (1) | 2022.12.18 |
[SwiftUI] SwiftUI의 Stack, Frame, GeometryReader (0) | 2022.11.30 |
[SwiftUI] SwiftUI로 커스텀 뷰 생성하기 (0) | 2022.11.14 |
[SwiftUI] Xcode 파일 뜯어보기 (0) | 2022.11.06 |