Last active
December 12, 2022 16:49
-
-
Save YusukeHosonuma/7bcb6afa796e076164569e22fa809e68 to your computer and use it in GitHub Desktop.
Before: [SwiftUI] タブがタップされた時に上までスクロールする。あるいはそれを共通化する話。
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
extension Binding { | |
func willSet(_ handler: @escaping (Value) -> ()) -> Binding<Value> { | |
.init( | |
get: { wrappedValue }, | |
set: { newValue in | |
handler(newValue) | |
wrappedValue = newValue | |
} | |
) | |
} | |
} | |
enum Tab: String, Identifiable, CaseIterable { | |
case first | |
case second | |
var id: String { rawValue } | |
var title: String { | |
switch self { | |
case .first: return "First" | |
case .second: return "Second" | |
} | |
} | |
@ViewBuilder | |
func tabItem() -> some View { | |
switch self { | |
case .first: Label(title, systemImage: "1.circle") | |
case .second: Label(title, systemImage: "2.circle") | |
} | |
} | |
} | |
/// イベントのトリガー | |
struct Trigger { | |
private(set) var key: Bool = false | |
mutating func fire() { | |
key.toggle() | |
} | |
} | |
extension View { | |
func onTrigger(of trigger: Trigger?, perform: @escaping () -> Void) -> some View { | |
onChange(of: trigger?.key) { _ in | |
perform() | |
} | |
} | |
} | |
/// ルートの View | |
struct ContentView: View { | |
@State private var selectedTab: Tab = .first | |
@State private var tabTappedTwices: [SelectionValue: Trigger] = .init( | |
uniqueKeysWithValues: SelectionValue.allCases.map { ($0, .init()) } | |
) | |
var body: some View { | |
TabView(selection: $selectedTab.willSet { | |
if selectedTab == $0 { | |
tabTappedTwice[selectedTab]?.fire() | |
} | |
}) { | |
ForEach(Tab.allCases) { tab in | |
SampleView( | |
title: tab.title, | |
tabTappedTwice: tabTappedTwices[tab]! | |
) | |
.tabItem(tab.tabItem) | |
.tag(tab) | |
} | |
} | |
} | |
} | |
/// タブに表示する View | |
struct SampleView: View { | |
var title: String | |
var tabTappedTwice: Trigger | |
var body: some View { | |
NavigationView { | |
ScrollViewReader { proxy in | |
List { | |
ForEach(items) { item in | |
Text(item.title) | |
.id(items.first?.id == item.id ? "top" : nil) | |
} | |
} | |
.listStyle(.plain) | |
.onTrigger(of: tabTappedTwice) { _ in | |
withAnimation { | |
proxy.scrollTo("top", anchor: .top) | |
} | |
} | |
} | |
.navigationTitle(title) | |
.navigationBarTitleDisplayMode(.inline) | |
} | |
} | |
} | |
/// 要素 | |
struct Item: Identifiable { | |
var id: Int | |
var title: String { "Item \(id)" } | |
init(_ number: Int) { | |
id = number | |
} | |
} | |
/// ダミーデータ | |
private let items: [Item] = (1..<100).map(Item.init) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment