I created a view similar to a wheel picker, and when I swiped the scrollView, I wanted the color of the text to change to white when it coincided with the rectangle.I can use .blendMode to achieve the effects in the images, but when the background changes, the effect also changes. I want to know if there is any method that can be unaffected by the background color.I searched for a long time but couldn't find a suitable solution. If you could provide any help, I would greatly appreciate it.
import SwiftUI
import SwiftUIIntrospect
import Combine
@_spi(Advanced) import SwiftUIIntrospect
struct TimePickerView: View {
@StateObject var viewModel = TimerPickerViewModel()
private let date = ["", "1", "5", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55", "60", "70", "80", "90", "100", "110", "120", ""]
var body: some View {
ZStack {
Image("bg")
.resizable()
.aspectRatio(contentMode: .fill)
.ignoresSafeArea(.all)
ScrollView(showsIndicators: false) {
LazyVStack(spacing: 0) {
ForEach(0..<date.count, id: \.self) { index in
let time = date[index]
Text(time + (time.isEmpty ? "":" MIN"))
.frame(width: 200, height: 50)
}
}
.font(.system(size: 25))
.foregroundStyle(Color(hex: 0x638FFF))
.background(GeometryReader { geometry in
Color.clear
.preference(key: TimerOffsetPreferenceKey.self,
value: geometry.frame(in: .named("TimePickeView")).origin.y)
})
.onPreferenceChange(TimerOffsetPreferenceKey.self) { value in
viewModel.contentOffset = -value
}
}
.introspect(.scrollView, on: .iOS(.v15, .v16, .v17), customize: { scrollView in
viewModel.scrollView = scrollView
})
.frame(height: 150)
.coordinateSpace(name: "TimePickeView")
RoundedRectangle(cornerRadius: 25)
.fill(Color(hex: 0x638FFF))
.frame(width: 180, height: 50)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
class TimerPickerViewModel: ObservableObject {
@Published var contentOffset: CGFloat = 0
private var cancellable = Set<AnyCancellable>()
@Weak var scrollView: UIScrollView?
init() {
$contentOffset
.dropFirst(2)
.debounce(for: .seconds(0.3), scheduler: RunLoop.main)
.sink { [weak self] output in
let remainder = Int(output) % 50
var offset = Int(output) - remainder + (remainder > 25 ? 50:0)
offset = max(0, min(offset, 1000))
self?.scrollView?.setContentOffset(CGPoint(x: 0, y: CGFloat(offset)), animated: true)
}
.store(in: &cancellable)
}
}
struct TimerOffsetPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value += nextValue()
}
}
#Preview {
TimePickerView()
}
The following is a similar effect that I want:

You might be able to get it to work using
.blendModewith the following small changes:.compositingGroup()to theZStack.backgroundThe modifier
.compositingGroupisolates the effect of the blend mode to the contents of theZStack. However, whether or not it works may depend on the colors you are using and/or the color scheme in operation (light/dark mode).As a more general-purpose solution, I would suggest showing a
Capsuleshape which has the list of dates shown as an overlay, clipped to the shape. This way, you can use any styling you like.GeometryReaderin the background. You were doing this already, but you were then using aPreferenceKeyto pass on the value. I would suggest, it is simpler to use an.onChangehandler to update a state variable..scrollTargetLayout..scrollPosition. The selected index can then be derived from the scrolled index.Here is an example of how it can be implemented this way. It does not need a
PreferenceKey, nor does it use introspection.