이번 클래스는 왜 View 가 클래스가 아닌 struct 인지 와 기본 템플릿의 body 는 왜 some View 를 리턴하는가에 대한 이야기 였다.
결론만 요약해서 말하면,
클래스 인 경우 쓰지 않는 속성과 메소드로 간단한 장면을 그릴때에도 무거워지기 때문에 가볍고 빠르게 만들기 위해 View 를 쓰고 있고
some View 는 역시 어떤걸 리턴할 지 모르지만
모두 대응하는 값으로 설정하기 위한것이다! 로 볼수 있겠다. (View 는 protocol 이다 )
추가적으로, modifier 의 순서는 중요하다! modifier 는 구조체의 끝에 .로 선언 및 추가 수정을 가하는것 인데,
그냥 UI 컴포넌트를 빌더 패턴 체인 호출(java에서처럼) 같이 쓴다고 보면 될듯 하다.
어쨌든 순서의 중요성은 아래의 예를 확인하면 된다.
//
// ContentView.swift
// ViewsAndModifiers
//
// Created by HanTJ on 11/18/24.
//
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Button("Hello, world!") {
print(type(of: self.body))
}
.background(.red)
.frame(width: 200, height: 200)
Button("Hello, world!") {
print(type(of: self.body))
}
.frame(width: 200, height: 200)
.background(.red)
}
.padding()
}
}
#Preview {
ContentView()
}
순차적이니 당연한거 같지만, 알아둬야 할 정보이다.
조건에 따른 View 형태 바꾸기 예제다(Conditional View). 토클을 이용해서, 속성 값을 지정하게 했다. 삼항연산자도 사용했다.
//
// ContentView.swift
// ViewsAndModifiers
//
// Created by HanTJ on 11/18/24.
//
import SwiftUI
struct ContentView: View {
@State private var useRedText = false
var body: some View {
Button("Hello World") {
// flip the Boolean between true and false
useRedText.toggle()
}
.foregroundStyle(useRedText ? .red : .blue)
}
}
#Preview {
ContentView()
}
여러개의 View 를 묶어서 취급하고 싶을때는 계속 써온 VStack, HStack, ZStack 을 쓰거나
정렬속성은 없는 Group을 쓰거나, @ViewBuilder를 쓸 수 있다.
var spells: some View {
VStack {
Text("Lumos")
Text("Obliviate")
}
}
var spells: some View {
Group {
Text("Lumos")
Text("Obliviate")
}
}
@ViewBuilder var spells: some View {
Text("Lumos")
Text("Obliviate")
}
modifier를 동일한것을 계속적으로 쓴다면 View를 별도로 구성해서 쓸수 있다.
//
// ContentView.swift
// ViewsAndModifiers
//
// Created by HanTJ on 11/18/24.
//
import SwiftUI
struct ContentView: View {
@State private var useRedText = false
var body: some View {
VStack(spacing: 10) {
Text("First")
.font(.largeTitle)
.padding()
.foregroundStyle(.white)
.background(.blue)
.clipShape(.capsule)
Text("Second")
.font(.largeTitle)
.padding()
.foregroundStyle(.white)
.background(.blue)
.clipShape(.capsule)
}
}
}
#Preview {
ContentView()
}
바꾸기!!
별도의 CapsuleText를 만들고 이를 호출하는걸로 바꿨다. 깔끔!
//
// ContentView.swift
// ViewsAndModifiers
//
// Created by HanTJ on 11/18/24.
//
import SwiftUI
struct ContentView: View {
@State private var useRedText = false
var body: some View {
VStack(spacing: 10) {
CapsuleText(text: "First")
CapsuleText(text: "Second")
}
}
}
struct CapsuleText: View {
var text: String
var body: some View {
Text(text)
.font(.largeTitle)
.padding()
.foregroundStyle(.white)
.background(.blue)
.clipShape(.capsule)
}
}
#Preview {
ContentView()
}
ViewModifier를 이용하면, View에 Modifier 를 붙일때 쓰는걸 추가로 만들 수 있다.
//
// ContentView.swift
// ViewsAndModifiers
//
// Created by HanTJ on 11/18/24.
//
import SwiftUI
struct ContentView: View {
@State private var useRedText = false
var body: some View {
Text("Hello World")
.modifier(Title()) //modifier로 스타일을 추가!
}
}
struct Title: ViewModifier { //ViewModifier protocol 을 이용
func body(content: Content) -> some View {
content
.font(.largeTitle)
.foregroundStyle(.white)
.padding()
.background(.blue)
.clipShape(.rect(cornerRadius: 10))
}
}
#Preview {
ContentView()
}
extension을 이용하면, 기존 struct에 바로 붙이기가 가능!
더욱 깔끔해 졌는데 더 머리가 아파짐 ㅡㅜ. 그리고 이건 여러명이 같이 쓸때는 위험쓰한 상황 가능
//
// ContentView.swift
// ViewsAndModifiers
//
// Created by HanTJ on 11/18/24.
//
import SwiftUI
struct ContentView: View {
@State private var useRedText = false
var body: some View {
Text("Hello World")
.titleStyle()
}
}
extension View {
func titleStyle() -> some View {
modifier(Title())
}
}
struct Title: ViewModifier { //ViewModifier protocol 을 이용
func body(content: Content) -> some View {
content
.font(.largeTitle)
.foregroundStyle(.white)
.padding()
.background(.blue)
.clipShape(.rect(cornerRadius: 10))
}
}
#Preview {
ContentView()
}
추가예제로 어디든 붙는 워터마크!
//
// ContentView.swift
// ViewsAndModifiers
//
// Created by HanTJ on 11/18/24.
//
import SwiftUI
struct ContentView: View {
@State private var useRedText = false
var body: some View {
Color.blue
.frame(width: 300, height: 200)
.watermarked(with: "Hacking with Swift")
}
}
struct Watermark: ViewModifier {
var text: String
func body(content: Content) -> some View {
ZStack(alignment: .bottomTrailing) {
content
Text(text)
.font(.caption)
.foregroundStyle(.white)
.padding(5)
.background(.black)
}
}
}
extension View {
func watermarked(with text: String) -> some View {
modifier(Watermark(text: text))
}
}
#Preview {
ContentView()
}
그리드를 표기하는 컨테이너도 다음과 같이 구현이 가능하다!
지금까지 배운것들을 응용하여, 뷰를 담는 컨테이너 형태의 그리드이다!
//
// ContentView.swift
// ViewsAndModifiers
//
// Created by HanTJ on 11/18/24.
//
import SwiftUI
struct ContentView: View {
@State private var useRedText = false
var body: some View {
GridStack(rows: 4, columns: 4) { row, col in
Image(systemName: "\(row * 4 + col).circle")
Text("R\(row) C\(col)")
}}
}
struct GridStack<Content: View>: View {
let rows: Int
let columns: Int
@ViewBuilder let content: (Int, Int) -> Content //content안에 여러개의 View를 선언할수 있게 된다!
var body: some View {
VStack {
ForEach(0..<rows, id: \.self) { row in
HStack {
ForEach(0..<columns, id: \.self) { column in
content(row, column)
}
}
}
}
}
}
#Preview {
ContentView()
}
믓지다!