paint-brush
SwiftUI 基本指南:带有自定义修饰符的可重用 UI经过@nemi
1,615 讀數
1,615 讀數

SwiftUI 基本指南:带有自定义修饰符的可重用 UI

经过 Nemi Shah9m2023/04/28
Read on Terminal Reader

太長; 讀書

SwiftUI 中的自定义 ViewModifier 可让您创建可应用于所有视图的可重用样式。在本文中,我们将介绍如何使用此功能使构建 UI 变得更加容易的示例。本文的目标是介绍在 Swift UI 中创建自定义修饰符和样式的一些不同方法。
featured image - SwiftUI 基本指南:带有自定义修饰符的可重用 UI
Nemi Shah HackerNoon profile picture
0-item
1-item

创建自定义视图修饰符的能力是 SwiftUI 中的一项强大功能。在本文中,我们将介绍如何使用此功能使构建 UI 变得更加容易的示例。如果你不熟悉SwiftUI中的 ViewModifiers 以及如何创建自定义的,你可以在这里阅读它们


本文的目标是介绍在 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) } }


好吧,你懂我了……这不是自定义视图修改器,而是一个简单的扩展。这是因为 ViewModifiers apple to the generic Views and some functions such as 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:在写这篇文章的时候,这个库还处于非常早期的阶段,repo 是空的 :p 但敬请期待)


也发布在这里。