Skip to content

ControlGroup (SwiftUI)

A SwiftUI guide for coding agents. Also covers controlgroup view, group related buttons, controlgroupstyle protocol, segmented button cluster ios, toolbar button group.

The ControlGroup container (SwiftUI Release 3, iOS 15+) displays semantically related controls, such as an increase and decrease pair, with a look chosen for its context. Use it instead of a bare HStack when the buttons form one logical unit.

Basics

Put the controls in the ViewBuilder closure, the same way as a stack. The group draws them as one visual cluster whose style depends on placement.

ControlGroup {
  Button(action: {}) {
    Label("Decrease", systemImage: "minus")
  }

  Button(action: {}) {
    Label("Increase", systemImage: "plus")
  }
}

Built-in Styles

SwiftUI ships automatic and navigation styles. automatic is the default; SwiftUI applies navigation on its own when the group sits in a NavigationView toolbar, and controlGroupStyle(.navigation) sets it by hand.

Custom Styles

Conform to ControlGroupStyle and implement makeBody(configuration:). The configuration's content is the group's children, so the style decides the layout and chrome around them.

struct ControlGroupWithTitle: ControlGroupStyle {
  let title: LocalizedStringKey

  func makeBody(configuration: Configuration) -> some View {
    VStack {
      Text(title)
        .font(.title)
      HStack {
        configuration.content
      }
    }
  }
}

Swift 5.5 extensions on the style protocol give the custom style dot syntax at the call site:

extension ControlGroupStyle where Self == ControlGroupWithTitle {
  static func with(title: LocalizedStringKey) -> ControlGroupWithTitle {
    ControlGroupWithTitle(title: title)
  }
}

ControlGroup {
  Button("Action 1") {}
  Button("Action 2") {}
}
.controlGroupStyle(.with(title: "Actions"))

This is the same styling pattern as ButtonStyle and the other style protocols: learn it once and it applies across SwiftUI's containers.

Use this guidance in your coding agent

Install the Better Design MCP once. Your agent then loads this page with one call.

get-swiftui-guide({ topic: "swiftui-mastering-controlgroup" })
claude mcp add --scope user better-design --transport http https://better-design.com/api/mcp --header "Authorization: Bearer <YOUR_API_KEY>"
Browse related design systems