Skip to content

Instantly share code, notes, and snippets.

@ivanopcode
Created February 21, 2025 02:01
Show Gist options
  • Save ivanopcode/f40edb949d130e7da3cab0a51fb1bb93 to your computer and use it in GitHub Desktop.
Save ivanopcode/f40edb949d130e7da3cab0a51fb1bb93 to your computer and use it in GitHub Desktop.
Reacting to ScrollView offset
// Copyright (c) 2021-2025 Alexey Grigorev, Ivan Oparin
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
class AppearanceObserver: ObservableObject {
private var pipelines: Set<AnyCancellable> = []
private let timer = Timer.publish(every: 2, on: .main, in: .common)
@Published var appearanceTransitionCompleted = false
@Published var contentFrame: CGRect = .zero
@Published var navBarBgOpacity: CGFloat = 0.0
@Published var textColorProgress: CGFloat = 0.0
let contentSpaceId = "ContentSpaceId"
var willDisappear: Bool {
appearanceTransitionCompleted
}
var willAppear: Bool {
!appearanceTransitionCompleted
}
init() {
initPipelines()
}
private func initPipelines() {
timer
.autoconnect()
.map { date in
true
}
.removeDuplicates()
.assign(to: &$appearanceTransitionCompleted)
$contentFrame
.map { frame in
print("DEBUG: contentFrame.origin.y = \(frame.origin.y)")
if frame.origin.y >= 0 {
print("DEBUG: Scrolling down or at top, textColorProgress = 0.0 (white)")
return 0.0
} else {
let normalized = min(abs(frame.origin.y) / 50, 1.0)
print("DEBUG: Pulling down, normalized = \(normalized)")
let baseProgress = pow(normalized, 4.0)
let progress = 0.2 + (0.8 * baseProgress)
print("DEBUG: textColorProgress = \(progress) (after pow and scaling)")
return progress
}
}
.removeDuplicates()
.assign(to: &$textColorProgress)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment