Skip to content

Instantly share code, notes, and snippets.

@TAATHub
Created February 24, 2024 12:34
Show Gist options
  • Save TAATHub/1ac8c5cd7dea57609690e9c08e29680c to your computer and use it in GitHub Desktop.
Save TAATHub/1ac8c5cd7dea57609690e9c08e29680c to your computer and use it in GitHub Desktop.
Custom Segmented Control with SwiftUI
protocol SegmentTypeProtocol: CaseIterable, Identifiable, Equatable {
var title: String { get }
var tintColor: Color? { get }
}
extension SegmentTypeProtocol {
var tintColor: Color? { nil }
}
struct SegmentedControl<SegmentType: SegmentTypeProtocol>: View where SegmentType.AllCases == [SegmentType] {
struct Configuration {
var selectedForegroundColor: Color = .white
var selectedBackgroundColor: Color = .black.opacity(0.75)
var foregroundColor: Color = .black
var backgroundColor: Color = .gray.opacity(0.25)
}
@Binding var selectedSegment: SegmentType
var configuration: Configuration = .init()
var body: some View {
HStack(spacing: 0) {
ForEach(SegmentType.allCases) { segment in
ZStack {
Rectangle()
.fill(configuration.backgroundColor)
Button(action: {
withAnimation(.interactiveSpring) {
selectedSegment = segment
}
}, label: {
Text(segment.title)
.font(.system(size: 12, weight: .bold))
.foregroundStyle(isSelected(segment: segment) ? configuration.selectedForegroundColor : configuration.foregroundColor)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.contentShape(Rectangle())
.background {
if isSelected(segment: segment) {
Rectangle()
.fill(segment.tintColor ?? configuration.selectedBackgroundColor)
.frame(height: 32)
.clipShape(RoundedRectangle(cornerRadius: 10))
.padding(4)
}
}
})
.buttonStyle(.plain)
}
}
}
.frame(height: 40)
.clipShape(RoundedRectangle(cornerRadius: 14))
}
private func isSelected(segment: SegmentType) -> Bool {
selectedSegment == segment
}
}
struct SegmentedControl_Previews: PreviewProvider {
enum SegmentType: SegmentTypeProtocol {
case segment1
case segment2
case segment3
var id: Self {
return self
}
var title: String {
switch self {
case .segment1:
return "Segment1"
case .segment2:
return "Segment2"
case .segment3:
return "Segment3"
}
}
var tintColor: Color? {
switch self {
case .segment1:
return .red.opacity(0.75)
case .segment2:
return .green.opacity(0.75)
case .segment3:
return .blue.opacity(0.75)
}
}
}
static var previews: some View {
struct PreviewView: View {
@State private var segmentType: SegmentType = .segment1
var body: some View {
SegmentedControl(
selectedSegment: $segmentType,
configuration: .init(
selectedForegroundColor: .white,
selectedBackgroundColor: .black.opacity(0.75),
foregroundColor: .black,
backgroundColor: .gray.opacity(0.25)))
.padding()
}
}
return PreviewView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment