Skip to content

Instantly share code, notes, and snippets.

@pteasima
Created February 9, 2022 16:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pteasima/85ec08405fccaae4fb8d1985950e7f2a to your computer and use it in GitHub Desktop.
Save pteasima/85ec08405fccaae4fb8d1985950e7f2a to your computer and use it in GitHub Desktop.
InsetPageTabView
import SwiftUI
import Introspect
//TODO: what if we use more custom tags in nested hierarchy. We need to somehow confine them to a single level.
enum CustomTag: PreferenceKey {
static var defaultValue: [AnyHashable] = []
static func reduce(value: inout [AnyHashable], nextValue: () -> [AnyHashable]) {
value.append(contentsOf: nextValue())
}
}
extension View {
func customTag(_ tag: AnyHashable) -> some View {
self
.preference(key: CustomTag.self, value: [tag])
}
}
struct InsetPageTabView<Selection: Hashable, Content: View>: View {
@Binding var selection: Selection
var inset: CGFloat = 20
@ViewBuilder var content: () -> Content
@State private var tags: [Selection] = [ ]
var selectedPage: Int {
tags.firstIndex(of: selection) ?? 0
}
@State private var dragOffsetX: CGFloat = 0
var body: some View {
GeometryReader { geometry in
HStack(spacing: 0) {
content()
.frame(width: geometry.size.width - 2*inset)
}
.padding(.horizontal, inset)
.offset(x: -(CGFloat(selectedPage) * (geometry.size.width - 2*inset)) + dragOffsetX, y: 0)
.onPreferenceChange(CustomTag.self) { tags in
self.tags = tags.compactMap { $0 as? Selection }
}
.gesture(
DragGesture()
.onChanged { value in
dragOffsetX = value.translation.width
}
.onEnded { value in
withAnimation {
let newIndex = selectedPage - Int(value.translation.width / (geometry.size.width / 2) /*TODO: - 2*inset or something*/)
selection = tags[safe: newIndex] ?? selection
dragOffsetX = 0
}
}
)
}
}
}
struct Playground_Previews: View, PreviewProvider {
static var previews: some View {
ForEach(Tab.allCases) {
self.init(selectedTab: $0)
}
}
enum Tab: Hashable, Identifiable, CaseIterable {
case red, green, blue, yellow, gray
var id: Self { self}
}
@State var selectedTab: Tab = .red
var body: some View {
InsetPageTabView(selection: $selectedTab) {
ForEach(Tab.allCases, content: tab)
}
}
func tab(_ tab: Tab) -> some View {
Group {
switch tab {
case .red:
Color.red
case .green:
Color.green
case .blue:
Color.blue
case .yellow:
Color.yellow
case .gray: Color.gray
}
}
.customTag(tab)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment