Watch the following video which illustrates a problem that I’m having where the built-in blur effect on the iOS navigation bar seems to be working inconsistently.
Video illustrating the problem
Here is my code. Copy it into XCode and try it out.
import SwiftUI
struct ContentView: View {
@State var showingList = true
var body: some View {
NavigationView {
VStack {
if showingList {
List {
Text("Hello, world!")
.foregroundColor(.red)
.font(.system(size: 70))
Text("Hello, world!")
.foregroundColor(.red)
.font(.system(size: 70))
Text("Hello, world!")
.foregroundColor(.red)
.font(.system(size: 70))
Text("Hello, world!")
.foregroundColor(.red)
.font(.system(size: 70))
Text("Hello, world!")
.foregroundColor(.red)
.font(.system(size: 70))
}
.listStyle(.plain)
.border(.red)
.transition(.openInLeft)
} else {
List {
Text("Hello, world!")
.foregroundColor(.red)
.font(.system(size: 70))
Text("Hello, world!")
.foregroundColor(.red)
.font(.system(size: 70))
Text("Hello, world!")
.foregroundColor(.red)
.font(.system(size: 70))
Text("Hello, world!")
.foregroundColor(.red)
.font(.system(size: 70))
Text("Hello, world!")
.foregroundColor(.red)
.font(.system(size: 70))
}
.listStyle(.plain)
.border(.red)
.transition(.openInRight)
}
}
.animation(.linear, value: showingList)
.toolbar(content: {
ToolbarItem(placement: .navigationBarLeading, content: {
Button("toggle") {
showingList.toggle()
}
})
})
.background(.black)
.navigationTitle("test")
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct FoldModifier: ViewModifier {
let amount: Double
let anchor: UnitPoint
func body(content: Content) -> some View {
if anchor == .leading || anchor == .trailing {
content.rotation3DEffect(.degrees(amount), axis: (x: 0, y: 1, z: 0), anchor: anchor)
} else if anchor == .top || anchor == .bottom {
content.rotation3DEffect(.degrees(amount), axis: (x: 1, y: 0, z: 0), anchor: anchor)
}
}
}
extension AnyTransition {
static var openInLeft: AnyTransition {
.modifier(
active: FoldModifier(amount: 90, anchor: .leading),
identity: FoldModifier(amount: 0, anchor: .leading)
)
}
static var openInRight: AnyTransition {
.modifier(
active: FoldModifier(amount: -90, anchor: .trailing),
identity: FoldModifier(amount: 0, anchor: .trailing)
)
}
}
It seems almost like it is a bug in SwiftUI, but hopefully some guru out there can explain to me how to make it work correctly.
3
Answers
This is a hack at best, but so far it is the best idea I've been able to come up with. I insert a blank row at the top of the list and then in .onAppear for the list, I programmatically scroll to the second item in the list (which is the first item with real content). The programmatic scroll causes something to get triggered that lets iOS know to do the blur effect on the NavigationBar for all of the lists.
I'm still hoping someone out there can come up with a better solution.
Running the code above I got the following message:
ignoring singular matrix: ProjectionTransform(m11: 0.0, m12: 0.5, m13: 0.0013882461823229988, m21: 0.0, m22: 1.0, m23: 0.0, m31: 0.0, m32: 0.0, m33: 1.0)
Upon further inspection apple classifies "ignoring singular matrix" as a warning.
The transformation matrix is singular when the scale factor is zero, as it will be at the end of your animation.
From what I see the fix is pretty simple instead of your identity being zero use the following code:
Note while this solution makes the flying text go away, it also makes the blur go away. I think a possible reason as to why the blur is not activating, upon looking at the view hierarchy I was able to uncover:
The UIVisualEffectView effect is set to none. My best guess is UIKitNavigationBar and the UINavigationTransitionView have no way to communicate with the current configuration of anchors. I tested the transition with different configuration of anchors in the example below I flipped them. To see how a glimpse as to how SwiftUI and UIKit communicate click here!
And was able to get one of the transitions the blur effect. But of course this also changes the animation. The best way to fix this would be change how the anchors are aligned and or not use transitions to achieve this animation.
Anyway sorry I was not able to fix this question "completely" I have ran out of time, I hope you will be able to find a proper solution best of luck.
To force the Blur background to always be there (even when the list is scrolled to the top), I can revert to UIKit and add the following init() to my view.