사용자 정의 뷰 수정자를 생성하는 기능은 SwiftUI의 강력한 기능입니다. 이 기사에서는 이 기능을 사용하여 UI 구축을 훨씬 쉽게 만드는 방법에 대한 예를 다룰 것입니다. SwiftUI 의 ViewModifier와 사용자 정의 뷰수정 생성 방법에 익숙하지 않은 경우 여기에서 해당 내용을 읽을 수 있습니다.
이 기사의 목표는 SwiftUI에서 사용자 정의 수정자와 스타일을 생성하는 몇 가지 다양한 방법과 이를 사용하여 깨끗하고 일관된 최종 출력을 달성하면서 UI를 보다 선언적으로 만드는 방법을 다루는 것입니다.
우리가 만들고 싶은 최종 UI는 다음과 같습니다.
화면의 모든 개별 구성 요소를 고려해 보겠습니다.
수정자 없이 이 화면을 빌드하는 경우 코드는 다음과 같습니다.
struct ContentView: View { var body: some View { VStack (alignment: .leading) { Image("feature") .resizable() .aspectRatio(contentMode: .fill) .frame(minWidth: 0, maxWidth: .infinity) .frame(height: 220) .cornerRadius(12) .padding(.bottom, 12) Text("Custom ViewModifiers in SwiftUI are the best!") .foregroundColor(Color("titleTextColor")) .font(.system(size: 20, weight: .bold)) .padding(.bottom, 12) Text("Custom ViewModifiers in SwiftUI let you create resuable styles that can be applied to all your views") .foregroundColor(Color("bodyTextColor")) .font(.system(size: 14, weight: .medium)) Spacer() Button(action: { }) { Text("Label") .font(.system(size: 14, weight: .medium)) } .frame(minWidth: 0, maxWidth: .infinity) .padding(.horizontal, 10) .padding(.vertical, 12) .background(Color.blue) .foregroundColor(Color.white) .cornerRadius(12) } .padding(.all, 16) } }
일부 요소(예: 제목 및 세부 정보 텍스트)에 대한 스타일은 복제되어야 합니다.
일부 일반적인 스타일(요소 패딩, 모서리 반경 등)에 대한 변경은 여러 위치에서 이루어져야 합니다.
이제 사용자 정의 뷰를 생성하여 UIKit 방식으로 이 문제를 해결할 수 있지만 저는 이 접근 방식을 좋아하지 않습니다. 왜냐하면 내장된 뷰에서 벗어나 새로운 팀 구성원의 온보딩을 더 어렵게 만들기 때문입니다. 더 쉬운 방법은 스타일 자체 대신 적용할 수 있는 일부 범용 뷰 수정자를 정의하는 것입니다.
필요한 일반적인 스타일을 분석해 보겠습니다.
코너 반경부터 시작해 보겠습니다.
struct CommonCornerRadius: ViewModifier { func body(content: Content) -> some View { content .cornerRadius(12) } }
이것은 다소 간단합니다. 요소에 범용 코너 반경을 적용할 수 있습니다. 이를 통해 사용자 정의 뷰를 만들거나 코드베이스 전체에 걸쳐 여러 가지 변경 작업을 수행하지 않고도 전역적으로 앱 스타일을 더 쉽게 변경할 수 있습니다.
struct FullWidthModifier: ViewModifier { func body(content: Content) -> some View { content .frame(minWidth: 0, maxWidth: .infinity) } }
이를 통해 전체 너비 보기를 더 쉽게 구현할 수 있으며 더 이상 .frame
수동으로 추가할 필요가 없습니다!
struct TitleTextModifier: ViewModifier { func body(content: Content) -> some View { content .foregroundColor(Color("titleTextColor")) .font(.system(size: 20, weight: .bold)) } } struct BodyTextModifier: ViewModifier { func body(content: Content) -> some View { content .foregroundColor(Color("bodyTextColor")) .font(.system(size: 14, weight: .medium)) } }
이렇게 하면 일반적인 텍스트 스타일이 허용됩니다. 일반적으로 사용자 정의 텍스트 구성 요소나 유틸리티 함수를 만들고 코드를 통해 UI 구성 요소를 추가합니다.
extension Image { func aspectFill() -> some View { self .resizable() .aspectRatio(contentMode: .fill) } }
알겠습니다. 알겠습니다…이것은 사용자 정의 보기 수정자가 아니라 단순한 확장입니다. 이는 ViewModifier가 일반 뷰에 적용되고 resizable
과 같은 일부 기능이 이미지에만 적용되기 때문에 확장 기능과 사용자 정의 수정자의 조합을 사용하면 이 문제를 해결하는 데 도움이 됩니다.
struct FullWidthButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .fullWidth() .foregroundColor(Color.white) .font(.system(size: 14, weight: .medium)) .padding(.horizontal, 10) .padding(.vertical, 12) .background(configuration.isPressed ? Color.blue.opacity(0.2) : Color.blue) } } struct FullWidthButton: ViewModifier { func body(content: Content) -> some View { content .buttonStyle(FullWidthButtonStyle()) } }
마지막으로 이는 버튼에 대한 것입니다. 동일한 효과를 달성하기 위해 단순히 ViewModifier를 생성할 수도 있었지만 탭했을 때 버튼의 모양이 변경되지는 않았습니다. 이는 버튼에 .background
설정하면 탭한 상태와 탭하지 않은 상태 모두에서 해당 배경을 강제로 사용하기 때문입니다. ButtonStyle
하면 버튼을 눌렀는지 여부에 따라 버튼의 불투명도를 변경할 수 있습니다.
이제 편의상 다음 수정자를 사용하는 확장을 만드는 것을 좋아합니다.
extension View { func commonCornerRadius() -> some View { modifier(CommonCornerRadius()) } func fullWidth() -> some View { modifier(FullWidthModifier()) } func title() -> some View { modifier(TitleTextModifier()) } func body() -> some View { modifier(BodyTextModifier()) } func fullWidthButton() -> some View { modifier(FullWidthButton()) } } extension Image { func aspectFill() -> some View { self .resizable() .aspectRatio(contentMode: .fill) } }
struct ContentView: View { var body: some View { VStack (alignment: .leading) { Image("feature") .aspectFill() .fullWidth() .frame(height: 220) .commonCornerRadius() .padding(.bottom, 12) Text("Custom ViewModifiers in SwiftUI are the best!") .title() .padding(.bottom, 12) Text("Custom ViewModifiers in SwiftUI let you create resuable styles that can be applied to all your views") .body() Spacer() Button(action: { }) { Text("Awesome") } .fullWidthButton() .commonCornerRadius() } .padding(.all, 16) } }
훨씬 깨끗해요! 언뜻 보기에 이는 단순히 스타일을 수동으로 설정하는 것보다 더 많은 코드와 노력을 필요로 하는 것처럼 느껴지지만 장기적으로는 많은 노력을 절약할 수 있습니다. 개인적으로 이 접근 방식은 뷰별 스타일 기준보다 일반 수정자에 더 의존하여 앱 스타일의 일관성을 높이는 데에도 도움이 됩니다.
그리고 그게 전부입니다! 이것이 앱을 더 빠르고 쉽게 구축하는 데 도움이 되기를 바랍니다. 또 다른 이점은 이러한 수정자를 앱에 추가하고 스타일 지침에 맞게 조정할 수 있다는 것입니다. 나는 또한 이것을 더욱 발전시키기 위해 라이브러리 작업을 해왔습니다. 여기에서 확인할 수 있습니다. (PS: 이 글을 쓰는 시점에서 라이브러리는 매우 초기 단계에 있으며 저장소는 비어 있습니다. p 하지만 계속 지켜봐 주시기 바랍니다)