Trying to make segmented control animation with changing options colors. In one direction everything works good, but in other background is above content. I want to do it without GeometryReader.
Animation works good in one direction: https://i.stack.imgur.com/w0YXF.gif
But here how it works in another direction: https://i.stack.imgur.com/6TAbB.gif
Code of Picker:
struct CustomPicker<Option: CustomPickerOption>: View {
// MARK: - Properties
@Binding var selection: Option
let options: [Option]
@Namespace private var namespaceID
private let buttonBackgroundID: String = "buttonOverlayID"
private let buttonOverlayID: String = "buttonOverlayID"
// MARK: - UI
var body: some View {
HStack(spacing: 0) {
ForEach(options) { option in
Segment(
title: option.title,
isSelected: selection == option,
backgroundID: buttonBackgroundID,
overlayID: buttonOverlayID,
namespaceID: namespaceID,
action: { selection = option }
)
}
}
.padding(4)
.background(Color.blue)
.clipShape(Capsule())
}
}
Code of Segment:
private struct Segment: View {
// MARK: - Properties
let title: String
let isSelected: Bool
let backgroundID: String
let overlayID: String
let namespaceID: Namespace.ID
let action: () -> Void
@State private var isPressed: Bool = false
// MARK: - UI
var body: some View {
Button(action: action) {
titleView
.blendMode(.difference)
.overlay(
titleView
.blendMode(.hue)
)
.overlay(
titleView
.foregroundColor(.black)
.blendMode(.overlay)
)
.background {
if isSelected {
background
.matchedGeometryEffect(id: backgroundID, in: namespaceID)
.transition(.offset())
}
}
}
.buttonStyle(.customHighlighted(isPressed: $isPressed))
}
private var background: some View {
Color.white
.clipShape(Capsule())
}
private var titleView: some View {
Text(title)
.font(\.semibold)
.foregroundColor(.white)
.padding(.horizontal, 12)
.padding(.vertical, 10)
}
}
I tried to replace .transition(.offset)
and use another transitions, add .frame(maxWidth: .infinity, maxHeight: .infinity)
, but nothing works. Are there any other solutions of this problem? .animation(.default, value: isSelected)
adds fade effect, so it's not best solution.
Update: zIndex(selection == option ? 1 : 0)
helps to normalize transition. To be honest I don't understand why. If you can, please, explain this weird hack.