paint-brush
SwiftUI 필수 가이드: 사용자 정의 수정자를 사용한 재사용 가능한 UI~에 의해@nemi
1,615 판독값
1,615 판독값

SwiftUI 필수 가이드: 사용자 정의 수정자를 사용한 재사용 가능한 UI

~에 의해 Nemi Shah9m2023/04/28
Read on Terminal Reader
Read this story w/o Javascript

너무 오래; 읽다

SwiftUI의 Custom ViewModifier를 사용하면 모든 뷰에 적용할 수 있는 재사용 가능한 스타일을 만들 수 있습니다. 이 기사에서는 이 기능을 사용하여 UI 구축을 훨씬 쉽게 만드는 방법에 대한 예를 다룰 것입니다. 이 글의 목표는 Swift UI에서 사용자 정의 수정자와 스타일을 생성하는 다양한 방법을 다루는 것입니다.
featured image - SwiftUI 필수 가이드: 사용자 정의 수정자를 사용한 재사용 가능한 UI
Nemi Shah HackerNoon profile picture
0-item
1-item

사용자 정의 뷰 수정자를 생성하는 기능은 SwiftUI의 강력한 기능입니다. 이 기사에서는 이 기능을 사용하여 UI 구축을 훨씬 쉽게 만드는 방법에 대한 예를 다룰 것입니다. SwiftUI 의 ViewModifier와 사용자 정의 뷰수정 생성 방법에 익숙하지 않은 경우 여기에서 해당 내용을 읽을 수 있습니다.


이 기사의 목표는 SwiftUI에서 사용자 정의 수정자와 스타일을 생성하는 몇 가지 다양한 방법과 이를 사용하여 깨끗하고 일관된 최종 출력을 달성하면서 UI를 보다 선언적으로 만드는 방법을 다루는 것입니다.


우리가 만들고 싶은 최종 UI는 다음과 같습니다.


화면의 모든 개별 구성 요소를 고려해 보겠습니다.

  • 이미지: 일부 모서리 반경이 있는 표준 이미지 구성요소
  • 텍스트: 제목과 본문 텍스트가 있습니다.
  • 버튼: 전체 너비 버튼

일반 SwiftUI 코드

수정자 없이 이 화면을 빌드하는 경우 코드는 다음과 같습니다.


 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 하지만 계속 지켜봐 주시기 바랍니다)