import SwiftUI //自作パッケージの切り出しなので変数名がめちゃくちゃです //詳しくはこれの実装を見てください https://github.com/Chronos2500/CustomNavigationTitle struct ContentView: View { var body: some View { NavigationStack{ ScrollView { Color.blue.frame(height: 200) .titleVisibilityAnchor() Text("サンプルページ") .frame(maxWidth: .infinity,alignment: .center) } .scrollAwareTitle() } } } struct BoundsPreferenceKey: PreferenceKey { typealias Value = Anchor? static let defaultValue: Value = nil static func reduce(value: inout Value, nextValue: () -> Value) { guard let newValue = nextValue() else { return } value = newValue } } extension View { public func titleVisibilityAnchor() -> some View { self.anchorPreference( key: BoundsPreferenceKey.self, value: .bounds ) { anchor in anchor } } } private struct ScrollAwareTitleModifier: ViewModifier { @State var isShowNavigationTitle = false func body(content: Content) -> some View { content .backgroundPreferenceValue(BoundsPreferenceKey.self) { anchor in GeometryReader { proxy in if let anchor = anchor { let scrollFrame = proxy.frame(in: .local).minY let itemFrame = proxy[anchor] let isVisible = (itemFrame.maxY + 22) > scrollFrame DispatchQueue.main.async{ if isVisible { isShowNavigationTitle = false } else if !isVisible { isShowNavigationTitle = true } } } return Color.clear } } .toolbarBackgroundVisibility(.hidden, for: .navigationBar) .toolbar { ToolbarItem(placement: .topBarTrailing) { Image(systemName: "xmark.circle.fill") .imageScale(.large) .foregroundStyle(isShowNavigationTitle ? .blue : .red) .animation(.easeIn(duration: 0.15), value: isShowNavigationTitle) } } } } extension View { public func scrollAwareTitle() -> some View { modifier(ScrollAwareTitleModifier()) } }