struct TabBarView: View {
@State private var tabSelection: String
@State private var tappedTwice: Bool = false
private let homeView = "Home"
init() {
self.tabSelection = homeView
}
var body: some View {
// タブアイコンをタップしたとき、どこをタップしたか、同じアイコンをタップしたかの判定
let handler = Binding<String>(
get: { tabSelection },
set: {
if $0 == tabSelection {
tappedTwice = true
}
tabSelection = $0
}
)
ScrollViewReader { proxy in
TabView(selection: handler) {
NavigationView {
HomeView(scrollId: homeView)
.addAction(tappedTwice: $tappedTwice, proxy: proxy, tabSelection: tabSelection, viewName: homeView)
}
.tabItem {
Image(systemName: "house")
Text("ホーム")
}
// こちらのtagはルートに戻るときに使っている
.tag(homeView)
}
}
}
}
struct AddAction: ViewModifier {
@State var isExistingView = false
@State var viewId = UUID()
@Binding var tappedTwice: Bool
let proxy: ScrollViewProxy
let tabSelection: String
let viewName: String
func body(content: Content) -> some View {
content
.id(viewId)
.onAppear {
// ios14ではこれらがうまく機能しないのでUIKitのViewWillAppearを使うとうまくいった。
isExistingView = true
}
.onDisappear {
isExistingView = false
}
.onChange(of: tappedTwice, perform: {
guard $0 else {
tappedTwice = false
return
}
if tabSelection == viewName {
if isExistingView {
withAnimation {
// 一番上に戻る
// viewNameはHomeView内で設定したもの
proxy.scrollTo(viewName, anchor: .top)
}
} else {
// ルートに戻る
viewId = UUID()
}
}
tappedTwice = false
})
}
}
extension View {
@ViewBuilder func addAction(tappedTwice: Binding<Bool>, proxy: ScrollViewProxy, tabSelection: String, viewName: String) -> some View {
self.modifier(AddAction(tappedTwice: tappedTwice, proxy: proxy, tabSelection: tabSelection, viewName: viewName))
}
}
struct HomeView: View {
let scrollId: String
var body: some View {
ScrollView {
Text("何らかのView")
// スクロールで戻りたい位置につける。基本は一番上
.id(scrollId)
}
}
}