Last active
December 10, 2024 11:14
-
-
Save mbernson/3a45ca1df8deddb2d2f36e6afeed6762 to your computer and use it in GitHub Desktop.
UIPageControl for use with SwiftUI. It has a two-way binding to a generic selection value.
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
import SwiftUI | |
import UIKit | |
struct PageControl<SelectionValue: Hashable>: UIViewRepresentable { | |
let items: [SelectionValue] | |
@Binding var selection: SelectionValue? | |
func makeUIView(context: Context) -> UIPageControl { | |
let pageControl = UIPageControl() | |
pageControl.pageIndicatorTintColor = .gray | |
pageControl.currentPageIndicatorTintColor = .black | |
pageControl.addTarget(context.coordinator, action: #selector(Coordinator.valueDidChange), for: .valueChanged) | |
return pageControl | |
} | |
func updateUIView(_ pageControl: UIPageControl, context: Context) { | |
pageControl.numberOfPages = items.count | |
if let selection, let selectedIndex = items.firstIndex(of: selection) { | |
pageControl.currentPage = selectedIndex | |
} | |
context.coordinator.items = items | |
} | |
func sizeThatFits(_ proposal: ProposedViewSize, uiView: UIPageControl, context: Context) -> CGSize? { | |
uiView.size(forNumberOfPages: items.count) | |
} | |
func makeCoordinator() -> Coordinator { | |
Coordinator(selection: $selection, items: items) | |
} | |
class Coordinator { | |
@Binding var selection: SelectionValue? | |
var items: [SelectionValue] | |
init(selection: Binding<SelectionValue?>, items: [SelectionValue]) { | |
self._selection = selection | |
self.items = items | |
} | |
@objc func valueDidChange(_ sender: UIPageControl) { | |
let selectedIndex = sender.currentPage | |
assert(items.indices.contains(selectedIndex), "Invalid index received") | |
withAnimation { | |
selection = items[selectedIndex] | |
} | |
} | |
} | |
} | |
private struct PageControl_Preview: View { | |
@State var numbers: [Int] = [1, 2, 3, 4, 5] | |
@State var selection: Int? = 1 | |
var body: some View { | |
VStack { | |
Text(verbatim: "Selected:") | |
Text(selection ?? -1, format: .number) | |
PageControl(items: numbers, selection: $selection) | |
.border(.red) | |
PageControl(items: numbers, selection: $selection) | |
.border(.blue) | |
} | |
} | |
} | |
#Preview { | |
PageControl_Preview() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment