Skip to content

Instantly share code, notes, and snippets.

@tomasen
Last active January 16, 2022 04:47
Show Gist options
  • Save tomasen/172d15c6afd7091ce40efbf0c5857f73 to your computer and use it in GitHub Desktop.
Save tomasen/172d15c6afd7091ce40efbf0c5857f73 to your computer and use it in GitHub Desktop.
WatchTabView inside NavigationView demo on WatchOS
// Created by SHEN SHENG on 1/16/22.
// This code is a part of the Wordbook iOS app.
// Wordbook is an App to help memorize and learn new English words.
// website: https://www.wordbook.cool
struct WatchMasterView: View {
@FocusState private var focusTab: Int?
@State private var currentTab = 0
var body: some View {
NavigationView{
WatchTabView(tabCount: 3,
currentTab: $currentTab,
onTabChanged: { tabIdx in focusTab = tabIdx }){
pageOne()
.focused($focusTab, equals: 0)
pageTwo()
.focused($focusTab, equals: 1)
pageThree()
.focused($focusTab, equals: 2)
}
.navigationTitle(Text("Wordbook").font(.caption))
}
}
@ViewBuilder func pageOne() -> some View {
List{
Text("Page One")
}
}
@ViewBuilder func pageTwo() -> some View {
VStack{
Spacer()
Text("Page Two")
Spacer()
}
.focusable(true)
}
@ViewBuilder func pageThree() -> some View {
Text("Page Three")
.focusable(true)
}
}
struct WatchTabView<Content: View>: View {
@Binding var currentTabIndex: Int
@State private var finalOffset: CGFloat = 0
private let tabTotalCount: Int
private let onTabChanged: (Int) -> Void
private let content: Content
init(tabCount: Int,
currentTab: Binding<Int>,
onTabChanged: @escaping (Int) -> Void,
@ViewBuilder content: () -> Content) {
self._currentTabIndex = currentTab
self.content = content()
self.onTabChanged = onTabChanged
self.tabTotalCount = tabCount
}
var body: some View {
ZStack {
GeometryReader { geometry in
HStack(spacing: 0) {
self.content.frame(width: geometry.size.width)
}
.frame(width: geometry.size.width, alignment: .leading)
.offset(x: finalOffset)
.gesture(
DragGesture().onChanged{ value in
finalOffset = -CGFloat(self.currentTabIndex) * geometry.size.width + value.translation.width
}.onEnded { value in
let offset = value.translation.width / geometry.size.width
let newIndex = (CGFloat(self.currentTabIndex) - offset).rounded()
self.currentTabIndex = min(max(Int(newIndex), 0), self.tabTotalCount - 1)
let newOffset = -CGFloat(self.currentTabIndex) * geometry.size.width
let duration = abs(finalOffset - newOffset)/geometry.size.width * 0.35
withAnimation(.easeOut(duration: duration)){
finalOffset = newOffset
onTabChanged(currentTabIndex)
}
}
)
.onAppear{
finalOffset = -CGFloat(self.currentTabIndex) * geometry.size.width
}
}
VStack{
Spacer()
HStack{
ForEach(0..<tabTotalCount, id: \.self) { pageId in
Circle()
.foregroundColor(currentTabIndex==pageId ? Color.white:Color.gray)
.frame(width: 5, height: 5)
}
}
.frame(height: 5, alignment: .bottom)
.padding(.bottom, 0.5)
}
.edgesIgnoringSafeArea(.bottom)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment