-
-
Save maysamsh/327c77e3f3c98ec29faf026ed5bd143e to your computer and use it in GitHub Desktop.
A custom segment control for 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
| // | |
| // SegmentedControl.swift | |
| // | |
| // Created by Maysam Shahsavari on 2024-12-12. | |
| // | |
| // Original code: https://github.com/NilCoalescing/SwiftUI-Code-Examples/blob/main/Custom-Segmented-Control-with-Matched-Geometry-Effect/CustomSegmentedControl.swift | |
| // Example code for the original code: https://nilcoalescing.com/blog/CustomSegmentedControlWithMatchedGeometryEffect/ | |
| // Final document: https://maysamsh.me/2024/12/16/animated-segmented-control/ | |
| import SwiftUI | |
| /// A type that provides a title for contr | |
| protocol SegmentedControlType: CaseIterable { | |
| var title: String { get } | |
| } | |
| enum SampleOptions: Identifiable, CustomStringConvertible, SegmentedControlType, Hashable { | |
| var id: Self { self } | |
| case option1 | |
| case option2 | |
| var title: String { | |
| switch self { | |
| case .option1: | |
| return "Option 1" | |
| case .option2: | |
| return "Option 2" | |
| } | |
| } | |
| var description: String { | |
| return title | |
| } | |
| } | |
| struct SegmentedControl<T: SegmentedControlType>: View where T: Identifiable, T: Hashable, T.AllCases: RandomAccessCollection { | |
| @Binding private var selection: T | |
| @Namespace private var segmentedControl | |
| init(selection: Binding<T>) { | |
| self._selection = selection | |
| } | |
| var body: some View { | |
| HStack { | |
| ForEach(T.allCases) { state in | |
| Button { | |
| withAnimation { | |
| selection = state | |
| } | |
| } label: { | |
| Text(state.title) | |
| .padding(10) | |
| } | |
| .matchedGeometryEffect( | |
| id: state, | |
| in: segmentedControl | |
| ) | |
| } | |
| } | |
| .background( | |
| Capsule() | |
| .fill(.background.tertiary) | |
| .matchedGeometryEffect( | |
| id: selection, | |
| in: segmentedControl, | |
| isSource: false | |
| ) | |
| ) | |
| .padding(6) | |
| .background(.indigo) | |
| .clipShape( | |
| Capsule() | |
| ) | |
| .buttonStyle(.plain) | |
| } | |
| } | |
| #Preview { | |
| @Previewable @State var options: SampleOptions = .option1 | |
| SegmentedControl(selection: $options) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment