Home > Software engineering >  Global view which blurry overlays other views and can be easily called
Global view which blurry overlays other views and can be easily called

Time:02-10

A similar question is asked here: SwiftUI: Global Overlay That Can Be Triggered From Any View. The examples however are shown a Toast, rather than a 'blocking' view. The accepted answer changes the presenting view (the toolbar is moving up after the toast is shown). Something is wrong with the code, but I don't know what (I just started learning SwiftUI). The code below is a combined effort from some other answers.

Alright, ideally I want to call a function on e.g. an EnvironmentObject which will automatically present an overlaying View with a ProgressView, to show that something is being loaded. The user should not be able to interact with the application while it is loading. The loading screen should be a bit blurry, overlapping with the presenting view.

Below shows what I have, the Text is never shown, but my breakpoint is hit when I click on the Load button. Any ideas why the LoadingView is never shown?

import SwiftUI
import UIKit

@main
struct TextLeadingApp: App {
    var body: some Scene {
        WindowGroup {
            ZStack {
                LoadingView() // This view should always be hidden, unless it is loading
                ContentView()
            }
            .environmentObject(Load())
        }
    }
}

class Load: ObservableObject {
    @Published var loader = 0

    func load() {
        loader  = 1
    }
}

struct LoadingView: View {
    @EnvironmentObject var load: Load

    var body: some View {
        if load.loader > 0 {
            GeometryReader { geometry in
                Text("LOADING")
                    .frame(
                        width: geometry.size.width,
                        height: geometry.size.height
                    )
                    .ignoresSafeArea(.all, edges: .all)
            }
        } else {
            EmptyView().frame(width: 0, height: 0)
        }
    }
}

struct ContentView: View {
    @EnvironmentObject var load: Load

    var body: some View {
        NavigationView {
            Text("hi")
                .toolbar {
                    Button("Load") {
                        load.load()
                    }
                }

                .navigationBarTitle(Text("A List"), displayMode: .large)
        }
        .navigationViewStyle(.stack)
    }
}

CodePudding user response:

There are a couple of things that could be adjusted with your code.

  • I'd make Load a @StateObject on a parent view so that the LoadingView can be conditionally displayed and not displayed all the time and just default to an EmptyView
  • In a ZStack, the topmost view should be last -- you have it first.
  • You can use .background(.ultraThinMaterial)
@main
struct TextLeadingApp: App {
    var body: some Scene {
        WindowGroup {
            ParentView()
        }
    }
}

struct ParentView : View {
    @StateObject private var load = Load()
    
    var body: some View {
        ZStack {
            ContentView()
            if load.loader > 0 {
                LoadingView()
            }
        }.environmentObject(load)
    }
}

class Load: ObservableObject {
    @Published var loader = 0

    func load() {
        loader  = 1
    }
}

struct LoadingView: View {
    @EnvironmentObject var load: Load

    var body: some View {
        VStack {
            Text("LOADING")
        }
        .ignoresSafeArea()
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(.ultraThinMaterial)
    }
}

struct ContentView: View {
    @EnvironmentObject var load: Load

    var body: some View {
        NavigationView {
            Text("hi")
                .onTapGesture {
                    print("tapped")
                }
                .font(.system(size: 100, weight: .bold, design: .default))
                .foregroundColor(.orange)
                .toolbar {
                    Button("Load") {
                        load.load()
                    }
                }
                .navigationBarTitle(Text("A List"), displayMode: .large)
        }
        .navigationViewStyle(.stack)
    }
}

I've adjusted ContentView a bit, just to make the blur effect more obvious.


Update, with OP's request that only LoadingView responds to a change in the state, and not the parent view:

@main
struct TextLeadingApp: App {
    var body: some Scene {
        WindowGroup {
            ZStack {
                ContentView()
                LoadingView()
            }
            .environmentObject(Load())
        }
    }
}


class Load: ObservableObject {
    @Published var loader = 0
    
    func load() {
        loader  = 1
    }
}

struct LoadingView: View {
    @EnvironmentObject var load: Load
    
    var body: some View {
        if load.loader > 0 {
            VStack {
                Text("LOADING")
            }
            .ignoresSafeArea()
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(.ultraThinMaterial)
        } else {
            EmptyView()
        }
    }
}
  • Related