Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does iOS Status Bar collapses because of Blur Effect since iOS 15?

I encountered a strange problem since iOS 15: I have a Blur Effect on the App's Root View, which changes depending on the scenePhase. This was working perfectly until iOS 15 got released. Now, whenever the Blur Effect is 0, the Status Bar of the App collapses and the Navigation Bar moves up and is no more interactable.

struct RootView: View {
    @Environment(\.scenePhase) var scenePhase
    @State private var blurRadius: CGFloat = 0

    var body: some View {
        Group {
            OtherViews()
        }
        .blur(radius: blurRadius)
        .onChange(of: scenePhase) { newValue in updateBlurRadius(newValue) }
    }

     private func updateBlurRadius(_ scenePhase: ScenePhase) {
         switch scenePhase {
             case .active : withAnimation { blurRadius = 0 }
             case .inactive: withAnimation { blurRadius = 16 }
             case .background: withAnimation { blurRadius = 16 }
             @unknown default: print("Unknown Case")
        }
    }
}

This code worked fine for iOS 14 and before. However, since iOS 15, the following bug appears:

Bug Preview

  • The curious thing is, that when the scenePhase becomes inactive, the Navigation Bar instantly jumps into its proper spot. And as soon as the scenePhase becomes active again, it jumps back to the top behind the Status Bar.
  • Also, when changing the Blur Radius for the active scenePhase to 0.001 instead of 0, everything works perfectly fine and the Navigation Bar does not jump behind the Status Bar.

Does anyone have an idea what could cause this strange behavior when working with Blur Effects?

Thanks a lot for your help in advance.

like image 411
christophriepe Avatar asked Dec 05 '21 11:12

christophriepe


1 Answers

I had this exact issue and was not able to find a fix, so I am now using this alternative implementation, which does pretty much the same thing:

ZStack {
    // View to be blurred goes here
    Rectangle()
        .ignoresSafeArea()
        .foregroundStyle(.ultraThinMaterial)
        .opacity(/* Your condition */ ? 1 : 0)
        .animation(.easeInOut(duration: 0.2))
}

This will overlay a blurry rectangle over your view. So in your case:

struct RootView: View {
    @Environment(\.scenePhase) var scenePhase

    var body: some View {
        ZStack {
            OtherViews()
            Rectangle()
                .ignoresSafeArea()
                .foregroundStyle(.ultraThinMaterial)
                .opacity(scenePhase != .active ? 1 : 0)
                .animation(.easeInOut)
        }
    }
}

Because this solution uses the new materials it only works on iOS 15. You can use if #available(iOS 15, *) to provide two different implementations, on for iOS 15+ and one for iOS 14 and earlier.

like image 55
wakel Avatar answered Oct 27 '22 03:10

wakel