.buttonStyle is "lost" when including buttons in a ControlGroup

I'm creating a custom toolbar, in SwiftUI, for a macOS app. I have the buttons styled the way I want, but now I want to put some buttons in a ControlGroup. The problem is that all styling I created for the buttons is lost; what would be a suitable way to get the ControlGroup to keep the styling I have used on the buttons?

From playground preview without & with the ControlGroup.
image

The playground code.

import SwiftUI
import PlaygroundSupport

enum ToolButtonType: String {
  case none = ""
  case pointer = "cursorarrow"
  case pin = "mappin"
  case line = "pencil.line"
  case polyline = "scribble"
  case polygon = "skew"
  case measure = "ruler"
  case select = "dot.viewfinder"
  case dragselect = "viewfinder"
  case properties = "info.square"
}

let fixedTools: [ToolButtonType] = [
  .properties
]

struct FixedToolButtonStyle: ButtonStyle {
  var type: ToolButtonType
  let height: CGFloat = 48
  let width: CGFloat = 48
  let cornerRadius: CGFloat = 3
  var bgColor: Color { Color.accentColor }
  var fgColor: Color { Color.primary }
  let fontSize: CGFloat = 24
  let weight: Font.Weight = .semibold
  let textSidePadding: CGFloat = 0
  
  func makeBody(configuration: Configuration) -> some View {
    configuration.label
      .padding([.all], textSidePadding)
      .frame(minWidth: width, maxWidth: width, minHeight: height, maxHeight: height)
      .background(bgColor)
      .foregroundColor(fgColor)
      .cornerRadius(cornerRadius)
      .font(.system(size: fontSize, weight: weight, design: .default))
      .scaleEffect(configuration.isPressed ? 0.8 : 1)
      .animation(.easeOut(duration: 0.2), value: configuration.isPressed)
  }
}

struct FixedToolButtonView: View {
  var type: ToolButtonType
  var name: String { type.rawValue }
  
  var body: some View {
    Button(action: {
      print("Pressed: \(type)")
    }) {
      Text("\(Image(systemName: type.rawValue))")
    }
    .buttonStyle(FixedToolButtonStyle(type: type))
    
  }
}

let selectableTools: [ToolButtonType] = [
  .pointer,
  .pin,
  .line,
  .polyline,
  .polygon,
  .measure,
  .select,
  .dragselect,
]

struct SelectableToolButtonStyle: ButtonStyle {
  var type: ToolButtonType
  var isSelected: Bool
  let height: CGFloat = 48
  let width: CGFloat = 48
  let cornerRadius: CGFloat = 3
  var bgColor: Color {
    return !isSelected ? Color.black : Color.accentColor
  }
  var fgColor: Color {
    return isSelected ? Color.primary : Color.secondary
  }
  let fontSize: CGFloat = 24
  let weight: Font.Weight = .semibold
  let textSidePadding: CGFloat = 0
  
  func makeBody(configuration: Configuration) -> some View {
    configuration.label
      .padding([.all], textSidePadding)
      .frame(minWidth: width, maxWidth: width, minHeight: height, maxHeight: height)
      .background(bgColor)
      .foregroundColor(fgColor)
      .cornerRadius(cornerRadius)
      .font(.system(size: fontSize, weight: weight, design: .default))
      .scaleEffect(configuration.isPressed ? 0.8 : 1)
      .animation(.easeOut(duration: 0.2), value: configuration.isPressed)
  }
}


struct SelectableToolButtonView: View {
  var type: ToolButtonType
  var name: String { type.rawValue }
  
  @Binding var selectedTool: ToolButtonType
  
  var body: some View {
    Button(action: {
      self.selectedTool = type
      print("Pressed: \(type)")
    }) {
      Text("\(Image(systemName: type.rawValue))")
    }
    .buttonStyle(SelectableToolButtonStyle(type: type, isSelected: selectedTool == type))
  }
}

struct ToolBarView: View {
  @Binding var selectedTool: ToolButtonType
  
  var body: some View {
    HStack {
//      ControlGroup {
        ForEach(selectableTools, id: \.self) { tool in
          SelectableToolButtonView(type: tool, selectedTool: $selectedTool)
        }
//      }
      Spacer()
      ForEach(fixedTools, id: \.self) { tool in
        FixedToolButtonView(type: tool)
      }
    }
  }
}

struct ContentView: View {
  @State var selectedTool: ToolButtonType = .none
  
  var body: some View {
    ToolBarView(selectedTool: $selectedTool)
  }
}



let vw = ContentView()
PlaygroundPage.current.setLiveView(vw)

The Swift forums are generally meant for questions related to open-source swift and related topics, so you might have more luck if you post your question on the Apple Developer forums. However, I do wonder why you need to use a ControlGroup, in my code I would just use the working version that doesn’t use control group (to give me more design control).

1 Like