Created
February 24, 2024 12:34
-
-
Save TAATHub/1ac8c5cd7dea57609690e9c08e29680c to your computer and use it in GitHub Desktop.
Custom Segmented Control with SwiftUI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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