SwiftUIViews

View Styles

全面解析 SwiftUI 的视图样式系统,涵盖按钮、开关、列表等组件的内置样式及自定义样式的实现方法。

概述

SwiftUI 提供了一套强大的样式系统,允许开发者通过简单的修饰器改变视图的外观和行为。大多数标准控件(如 ButtonToggleList)都遵循特定的样式协议。

使用样式的好处:

  • 一致性:确保应用内组件风格统一。
  • 上下文适应:系统会根据平台(iOS, macOS, tvOS, watchOS)和环境自动调整默认样式。
  • 可复用性:自定义样式可以轻松复用于多个视图。

基本用法是使用 .style() 修饰器,例如 .buttonStyle(.bordered)。这些修饰器通常会向下传递,影响层级中的所有同类视图。

控件样式(Control Styles)

ButtonStyle

用于自定义 Button 的外观和交互反馈。

VStack(spacing: 20) {
    Button("Automatic") {}
        .buttonStyle(.automatic)
    
    Button("Bordered") {}
        .buttonStyle(.bordered)
    
    Button("Bordered Prominent") {}
        .buttonStyle(.borderedProminent)
    
    Button("Borderless") {}
        .buttonStyle(.borderless)
        
    Button("Plain") {}
        .buttonStyle(.plain)
}

ToggleStyle

用于 Toggle 开关的样式。

VStack {
    Toggle("Switch", isOn: .constant(true))
        .toggleStyle(.switch)
    
    Toggle("Button", isOn: .constant(true))
        .toggleStyle(.button)
}

PickerStyle

用于 Picker 选择器的样式。

Picker("Flavor", selection: .constant("Chocolate")) {
    Text("Chocolate").tag("Chocolate")
    Text("Vanilla").tag("Vanilla")
}
.pickerStyle(.segmented) // 或 .menu, .wheel, .inline, .navigationLink

用于 Menu 视图的样式。

Menu("Options") {
    Button("Edit") {}
    Button("Delete") {}
}
.menuStyle(.button) // 默认样式,通常表现为按钮

GaugeStyle

用于 Gauge 仪表盘的样式。

Gauge(value: 0.5) {
    Text("Level")
}
.gaugeStyle(.accessoryCircular) // 或 .linearCapacity, .accessoryLinear

ProgressViewStyle

用于 ProgressView 进度条的样式。

VStack {
    ProgressView()
        .progressViewStyle(.circular)
    
    ProgressView(value: 0.5)
        .progressViewStyle(.linear)
}

LabelStyle

用于 Label(图标+文本)的样式。

Label("Favorite", systemImage: "star.fill")
    .labelStyle(.titleOnly) // 或 .iconOnly, .titleAndIcon

输入样式(Input Styles)

TextFieldStyle

用于 TextField 的样式。

TextField("Username", text: .constant(""))
    .textFieldStyle(.roundedBorder) // 或 .plain, .automatic

TextEditorStyle

用于 TextEditor 多行文本输入的样式。

TextEditor(text: .constant("Enter bio..."))
    .textEditorStyle(.plain) // 或 .automatic

LabeledContentStyle

用于 LabeledContent 的样式,常用于表单中显示键值对。

LabeledContent("Version", value: "1.0.0")
    .labeledContentStyle(.automatic)

容器与布局样式(Container & Layout Styles)

ListStyle

用于 List 的样式,不同平台默认值不同。

List {
    Text("Item 1")
    Text("Item 2")
}
.listStyle(.insetGrouped) // 或 .plain, .grouped, .inset, .sidebar

TableStyle

用于 Table (主要在 macOS/iPadOS) 的样式。

Table(items) {
    TableColumn("Name", value: \.name)
}
.tableStyle(.inset) // 或 .bordered

TabViewStyle

用于 TabView 的样式。

TabView {
    Text("Tab 1").tabItem { Text("1") }
}
.tabViewStyle(.page) // 或 .automatic (底部标签栏)

GroupBoxStyle

用于 GroupBox 的样式。

GroupBox(label: Text("Settings")) {
    Toggle("Enable", isOn: .constant(true))
}
.groupBoxStyle(.automatic)

ControlGroupStyle

用于 ControlGroup 的样式。

ControlGroup {
    Button("A") {}
    Button("B") {}
}
.controlGroupStyle(.navigation) // 或 .automatic

FormStyle

用于 Form 的样式。

Form {
    // fields
}
.formStyle(.grouped) // 或 .columns

DisclosureGroupStyle

用于 DisclosureGroup (折叠面板) 的样式。

DisclosureGroup("Details") {
    Text("Hidden content")
}
.disclosureGroupStyle(.automatic)

IndexViewStyle

用于 ListTabView (page style) 的索引视图样式。

TabView {
    // pages
}
.tabViewStyle(.page)
.indexViewStyle(.page(backgroundDisplayMode: .always))

导航样式(Navigation Styles)

用于 NavigationSplitView 的样式。

NavigationSplitView {
    List(items, selection: $selected) { item in
        NavigationLink(item.name, value: item)
    }
} detail: {
    Text("Detail")
}
.navigationSplitViewStyle(.balanced) // 或 .prominentDetail, .automatic

NavigationViewStyle 已被废弃,建议使用 NavigationStackNavigationSplitView 及其对应样式。

自定义样式(Custom Styles)

你可以通过遵循相应的样式协议(如 ButtonStyle)并实现 makeBody(configuration:) 方法来创建自定义样式。

自定义 ButtonStyle

ButtonStyleconfiguration 提供 label (视图) 和 isPressed (布尔值)。

struct PressableButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .padding()
            .background(Color.blue)
            .foregroundColor(.white)
            .clipShape(RoundedRectangle(cornerRadius: 8))
            .scaleEffect(configuration.isPressed ? 0.95 : 1.0) // 按下缩放效果
            .animation(.easeOut(duration: 0.2), value: configuration.isPressed)
    }
}

// 使用
Button("Press Me") {}
    .buttonStyle(PressableButtonStyle())

自定义 ToggleStyle

ToggleStyleconfiguration 提供 label (视图), isOn (绑定状态) 和 $isOn (Binding)。

struct CheckboxToggleStyle: ToggleStyle {
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            Image(systemName: configuration.isOn ? "checkmark.square.fill" : "square")
                .foregroundColor(configuration.isOn ? .blue : .gray)
                .onTapGesture {
                    configuration.isOn.toggle()
                }
            configuration.label
        }
    }
}

// 使用
Toggle("Accept Terms", isOn: $isAccepted)
    .toggleStyle(CheckboxToggleStyle())

样式环境与层级

样式修饰器会注入到环境变量中,影响子视图。

VStack {
    Button("Button 1") {} // 使用 Bordered
    
    HStack {
        Button("Button 2") {} // 使用 Bordered
        Button("Button 3") {} // 使用 Plain (覆盖)
            .buttonStyle(.plain)
    }
}
.buttonStyle(.bordered)

通过掌握这些样式,你可以快速构建符合平台规范的界面,同时也能灵活定制独特的品牌风格。

在 GitHub 上编辑

上次更新于