Last active
May 31, 2024 22:08
-
-
Save satishVekariya/c52477b6acafdf200606335e39a37382 to your computer and use it in GitHub Desktop.
These modifiers are designed to observe changes in the view's bounds and provide a stretchy background effect for a list.
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
import SwiftUI | |
/// These modifiers are designed to observe changes in the view's bounds and provide a stretchy background effect for a list. | |
/// | |
/// Source: https://gist.github.com/satishVekariya/c52477b6acafdf200606335e39a37382 | |
public extension View { | |
/// List/View bound change observer | |
/// | |
/// Mostly you need to use this on List cell | |
/// | |
/// ``` | |
/// @State private var offset: CGPoint? | |
/// | |
/// List { | |
/// Text("hi") | |
/// .onBoundChange() { | |
/// offset = $0?.origin | |
/// } | |
/// } | |
/// .stretchyBackground(offsetY: offset?.y) | |
/// ``` | |
/// Reference: https://stackoverflow.com/questions/56726369/getting-content-offset-of-list-in-swiftui | |
/// | |
/// - Parameters: | |
/// - id: A unique id | |
/// - onChange: Closure to that receives new frame value | |
/// - Returns: some View | |
func onBoundChange( | |
id: String = "bound", | |
onChange: @escaping (CGRect?) -> Void | |
) -> some View { | |
self.background( | |
GeometryReader { geo in | |
Color.clear | |
.preference(key: BoundPreferenceKey.self, value: [ViewFrame(id: id, frame: geo.frame(in: .named("name")))]) | |
} | |
) | |
.onPreferenceChange(BoundPreferenceKey.self) { frames in | |
onChange(frames.first(where: { $0.id == id })?.frame) | |
} | |
} | |
/// Use this modifier on List view to set fixed and stretchy background | |
/// - Parameters: | |
/// - alignment: Alignment of the stretchy background | |
/// - fixedColor: Background color to fill entire screen. Default is white | |
/// - stretchyColor: Background color to display on top of fixed color with dynamic height | |
/// - offsetY: Scroll offset to adjust stretchy of stretchyColor. Mostly given by `onBoundChange(_:)` modifier | |
/// - Returns: some View | |
func stretchyBackground( | |
alignment: Alignment = .top, | |
fixedColor: Color = .white, | |
stretchyColor: Color = .blue, | |
offsetY: CGFloat? | |
) -> some View { | |
self.background( | |
ZStack(alignment: alignment) { | |
fixedColor | |
if let offsetY = offsetY, offsetY > 0 { | |
stretchyColor.frame(height: offsetY) | |
} | |
} | |
.ignoresSafeArea() | |
.coordinateSpace(name: "name") | |
) | |
} | |
} | |
fileprivate struct ViewFrame: Equatable { | |
let id: String | |
let frame: CGRect | |
static func == (lhs: ViewFrame, rhs: ViewFrame) -> Bool { | |
lhs.id == rhs.id && lhs.frame == rhs.frame | |
} | |
} | |
fileprivate struct BoundPreferenceKey: PreferenceKey { | |
typealias Value = [ViewFrame] // The list of view frame changes in a View tree. | |
static var defaultValue: [ViewFrame] = [] | |
/// When traversing the view tree, Swift UI will use this function to collect all view frame changes. | |
static func reduce(value: inout [ViewFrame], nextValue: () -> [ViewFrame]) { | |
value.append(contentsOf: nextValue()) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment