SwiftUI

[SwiftUI] ViewModifier, modifier(_:)

yujaehui 2024. 5. 3. 11:56

SwiftUI에서 ViewModifier와 modifier(_:)

SwiftUI에서 ViewModifier는 뷰의 외형과 동작을 변경하거나 확장하는 데 사용하는 재사용 가능한 방법.

이를 통해 코드 중복을 줄이고, 더 읽기 쉽고 유지 관리하기 쉬운 코드를 작성할 수 있음.


1. ViewModifier란?

정의

  • ViewModifier는 SwiftUI의 프로토콜로, 뷰의 외형이나 동작을 캡슐화하여 여러 뷰에 재사용할 수 있음.
  • 뷰의 꾸미기 작업, 레이아웃 변경, 행동 추가 등을 위해 사용.

구조

ViewModifier는 다음과 같은 요구사항을 가진 프로토콜

  • body(content:) 메서드: 원래의 뷰(content)를 기반으로 수정된 뷰를 반환.
    • 원래의 뷰를 변경 시키는 것이 아니라 수정된 뷰를 반환!
  • AssociatedType: Body라는 제네릭 타입을 사용하며, 반환값은 반드시 SwiftUI View.

2. ViewModifier 기본 사용법

1. ViewModifier 구현

struct HighlightModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(Color.yellow)
            .cornerRadius(8)
    }
}

2. View에 적용

  • modifier(_:) 메서드를 사용하여 적용.
struct ContentView: View {
    var body: some View {
        Text("Hello, SwiftUI!")
            .modifier(HighlightModifier())
    }
}

3. 결과


3. modifier(_:) 메서드

  • modifier(_:)는 View 프로토콜의 기본 메서드.
  • ViewModifier를 사용하는 뷰에 수정 사항을 적용하는 데 사용.

선언

func modifier<T>(_ modifier: T) -> some View where T : ViewModifier

특징

  • 뷰의 스타일이나 동작을 동적으로 변경할 수 있음.
  • 매개변수로 사용자 정의한 ViewModifier를 전달해야 함.
  • 뷰에 적용된 순서에 따라 결과가 달라질 수 있음.

사용 예시

Text("Hello, World!")
    .modifier(ModifierA())
    .modifier(ModifierB())
  • ModifierA가 먼저 적용되고, 그 위에 ModifierB가 추가로 적용.
  • 수정자의 순서는 UI 결과에 직접적인 영향.

4. ViewModifier를 재사용 가능한 뷰 스타일로 사용

ViewModifier는 뷰의 스타일을 캡슐화하여 재사용성을 높일 수 있음.

사용자 정의 스타일

struct TitleStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .font(.largeTitle)
            .foregroundColor(.blue)
    }
}

간결한 호출을 위한 확장

반복적인 .modifier(TitleStyle()) 호출을 줄이기 위해 익스텐션을 사용할 수 있음.

extension View {
    func titleStyle() -> some View {
        self.modifier(TitleStyle())
    }
}

사용 예시

Text("SwiftUI is amazing!")
    .titleStyle()

5. ViewModifier의 실용적 활용

1. 조건부 스타일링

struct ConditionalModifier: ViewModifier {
    var isActive: Bool

    func body(content: Content) -> some View {
        content
            .padding()
            .background(isActive ? Color.green : Color.red) // 조건 설정
            .cornerRadius(10)
    }
}

extension View {
    func conditionalStyle(isActive: Bool) -> some View {
        self.modifier(ConditionalModifier(isActive: isActive))
    }
}

// 사용 예시
Text("Dynamic Styling")
    .conditionalStyle(isActive: true) // 녹색 배경

2. 복합 스타일 캡슐화

struct ShadowedModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(Color.white)
            .cornerRadius(10)
            .shadow(color: .gray, radius: 5, x: 0, y: 5)
    }
}

extension View {
    func shadowedStyle() -> some View {
        self.modifier(ShadowedModifier())
    }
}

// 사용 예시
Text("Shadowed Text")
    .shadowedStyle()

6. ViewModifier와 직접 스타일링 비교

  직접 스타일링 ViewModifier 사용
재사용성 낮음 높음
코드 중복 제거 반복적인 코드 작성 필요 중복 코드 캡슐화 가능
가독성 복잡한 뷰에서 낮아질 수 있음 스타일링 로직이 독립적이므로 더 명확
조건부 스타일링 if-else 또는 삼항 연산자 사용 필요 ViewModifier로 캡슐화 가능

예시 비교

// 직접 스타일링
Text("Hello")
    .padding()
    .background(Color.blue)
    .cornerRadius(10)

// ViewModifier 사용
Text("Hello")
    .modifier(CustomModifier())

7. ViewModifier와 환경 데이터(@Environment)

ViewModifier는 환경 데이터를 통해 뷰의 스타일을 동적으로 변경할 수도 있음.

예제: 다크 모드 감지

struct AdaptiveModifier: ViewModifier {
    @Environment(\\.colorScheme) var colorScheme

    func body(content: Content) -> some View {
        content
            .padding()
            .background(colorScheme == .dark ? Color.black : Color.white)
            .cornerRadius(10)
    }
}

extension View {
    func adaptiveStyle() -> some View {
        self.modifier(AdaptiveModifier())
    }
}

// 사용 예시
Text("Adaptive Modifier")
    .adaptiveStyle()

8. 주의할 점

  1. 지나친 사용 자제
    • 너무 많은 ViewModifier를 사용하면 코드 복잡도가 증가할 수 있음.
    • 간단한 스타일은 일반적인 뷰 수정 체인을 사용하는 것이 더 적합할 수 있음.
  2. 중첩된 Modifier
    • 여러 Modifier를 중첩해서 사용하면 관리가 어려워질 수 있기에 적절히 분리하여 사용하는 것이 좋음.

'SwiftUI' 카테고리의 다른 글

[SwiftUI] NavigationView vs NavigationStack  (0) 2024.05.09
[SwiftUI] SwiftUI의 View Rendering  (0) 2024.05.08
[SwiftUI] Source of Truth  (0) 2024.04.29
[SwiftUI] @State와 @Binding의 projectedValue ($)  (0) 2024.04.29
[SwiftUI] @State, @Binding  (0) 2024.04.29