Last active
January 16, 2022 04:47
-
-
Save tomasen/172d15c6afd7091ce40efbf0c5857f73 to your computer and use it in GitHub Desktop.
WatchTabView inside NavigationView demo on WatchOS
This file contains 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
// 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