はじめに
実装
import SwiftUI
extension View {
func onChangeParentScrollViewOffset(perform: @escaping (CGFloat) -> Void) -> some View {
self.background(GeometryReader {
Color.clear.preference(
key: OffsetYPreferenceKey.self,
value: $0.frame(in: .global).origin.y
)
}).onPreferenceChange(
OffsetYPreferenceKey.self,
perform: perform
)
}
}
struct OffsetYPreferenceKey: PreferenceKey {
static var defaultValue = CGFloat.zero
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value += nextValue()
}
}
import SwiftUI
struct NavigationBlurView<T: View>: View {
@State private var offsetY = CGFloat.zero
private var title: String
private var navigationHeight: CGFloat
private var content: () -> T
init(_ title: String, navigationHeight: CGFloat = 70.0, content: @escaping () -> T) {
self.title = title
self.navigationHeight = navigationHeight
self.content = content
}
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
content()
.padding(.top, navigationHeight + 30)
.onChangeParentScrollViewOffset { offsetY in
self.offsetY = offsetY
}
}
.overlay(alignment: .top, content: navigationBar)
.animation(.spring(), value: offsetY)
}
private func navigationBar() -> some View {
ZStack(alignment: .center) {
Color.clear.background(.ultraThinMaterial.opacity(offsetY < 0 ? 1 : 0))
.padding(.top, -navigationHeight)
.padding(.horizontal, -20)
.blur(radius: 15)
HStack(spacing: 15) {
Text(title)
.font(.system(size: offsetY < 0 ? 25 : 30))
.fontWeight(.heavy)
.foregroundStyle(.white)
.lineLimit(1)
Spacer()
Button {
} label: {
Image(systemName: "yensign.circle.fill")
.foregroundStyle(offsetY < 0 ? .white : .purple)
}
.padding(5)
.background(.thickMaterial, in: RoundedRectangle(cornerRadius: 10))
Button {
} label: {
Image(systemName: "moon.haze.fill")
.foregroundStyle(offsetY < 0 ? .white : .purple)
}
.padding(5)
.background(.thickMaterial, in: RoundedRectangle(cornerRadius: 10))
}
.frame(maxWidth: .infinity)
.padding(.top, offsetY < 0 ? 0 : 10)
.padding(.horizontal, 20)
}
.frame(height: navigationHeight, alignment: .top)
}
}
使い方
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationBlurView("NavigationBlur") {
VStack(spacing: 10) {
ForEach(0..<100) { _ in
Image("sample")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: .infinity)
}
}
}
}
}
おわり
右側のボタンもカスタムできるようにしたらライブラリにでもしようかな、、、