I am trying to apply .glassEffect to some controls that are in a horizontal scroll view in a bottom .safeAreaBar.
No matter what I try, there is a persistent pesky flickering of the background that I can't get rid of. It happens when swiping left in the bottom safe area bar to access the next set of controls.
So far, I tried:
- Wrapping either controls or the stacks in
GlassEffectContainer - Removing .interactive() from glass effects
.drawingGroup()(incompatible with glass effects).compositingGroup()and.geometryGroup()change nothing- Tuning spacing/margins (only partially helped)
Removing the .glassEffect() from all controls fixes the flicker, but I want to use it to keep it consistent with other controls present on screen.
Any suggestions?
Here's the reproducible code and a screen recording:
import SwiftUI
struct GlassEffectScrollTest: View {
// Mock state
@State private var selectedOption1: MockOption = .option1
@State private var selectedOption2: MockOption = .optionA
@State private var value1: Double = 12
@State private var value2: Double = 20
@State private var showHighlight: Bool = false
var body: some View {
NavigationStack {
if #available(iOS 26.0, *) {
ScrollView {
VStack(spacing: 0) {
ForEach(0..<50, id: \.self) { index in
VStack(alignment: .leading, spacing: 8) {
Text("Item \(index + 1)")
.font(.headline)
Text("This is some sample content that will scroll beneath the toolbar. The toolbar should maintain a soft edge blur effect as this content scrolls underneath it.")
.font(.subheadline)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(Color(.systemBackground), in: .rect(cornerRadius: 12))
.padding(.horizontal)
.padding(.vertical, 6)
}
}
.padding(.top)
}
.background(Color(.systemGroupedBackground))
.safeAreaBar(edge: .bottom) {
mockToolbar()
}
.navigationTitle("Glass Effect Test")
} else {
// Fallback on earlier versions
}
}
}
@ViewBuilder
private func mockToolbar() -> some View {
if #available(iOS 26.0, *) {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 24) {
// Section 1: Font controls mockup
HStack(spacing: 16) {
Picker("Option 1", selection: $selectedOption1) {
ForEach(MockOption.allCases) { option in
Text(option.rawValue).tag(option)
}
}
.pickerStyle(.menu)
.labelsHidden()
.fixedSize()
.controlSize(.small)
.glassEffect(.clear, in: .capsule)
Toggle(isOn: $showHighlight) {
Image(systemName: "highlighter")
.contentTransition(.symbolEffect(.replace))
.font(.subheadline)
.foregroundStyle(.tint)
}
.toggleStyle(.button)
.tint(.yellow)
.glassEffect(.clear, in: .capsule)
Spacer()
Text("\(Int(value1))pt")
.foregroundStyle(.secondary)
.font(.subheadline)
.frame(minWidth: 35, alignment: .trailing)
Stepper("", value: $value1, in: 8...32, step: 1)
.labelsHidden()
.fixedSize()
.controlSize(.small)
.glassEffect(.clear, in: .capsule)
}
.containerRelativeFrame(.horizontal)
// Section 2: Spacing controls mockup
HStack(spacing: 16) {
Picker("Option 2", selection: $selectedOption2) {
ForEach(MockOption.allCases) { option in
Text(option.rawValue).tag(option)
}
}
.pickerStyle(.menu)
.fixedSize()
.labelsHidden()
.glassEffect(.clear, in: .capsule)
Spacer()
Text("\(Int(value2))pt")
.foregroundStyle(.secondary)
.font(.subheadline)
.frame(minWidth: 35, alignment: .trailing)
Stepper("", value: $value2, in: 0...50, step: 2)
.labelsHidden()
.glassEffect(.clear, in: .capsule)
}
.containerRelativeFrame(.horizontal)
}
.scrollTargetLayout()
}
.scrollTargetBehavior(.viewAligned)
.scrollContentBackground(.hidden)
.padding(.vertical, 20)
.contentMargins(.horizontal, 24, for: .scrollContent)
.containerRelativeFrame(.horizontal)
.overlay(alignment: .bottom) {
Text("Swipe horizontally for more tools \(Image(systemName: "hand.draw.fill"))")
.font(.caption2)
.foregroundStyle(.tertiary)
// .padding(.bottom, 4)
.padding(.top, 10)
}
}
}
}
// Mock data
enum MockOption: String, CaseIterable, Identifiable {
case option1 = "Option 1"
case option2 = "Option 2"
case option3 = "Option 3"
case optionA = "Option A"
case optionB = "Option B"
case optionC = "Option C"
var id: String { rawValue }
}
#Preview {
GlassEffectScrollTest()
}
