Reputation: 1644
So technically I want to show a loading screen view. I'm using fullScreenCover
.
struct ContentView: View {
@State private var isLoading = false
var body: some View {
VStack {
Text("Hello there")
Button("Start loading") {
isLoading.toggle()
}
.fullScreenCover(isPresented: $isLoading) {
ZStack{
Color.black.opacity(0.5).edgesIgnoringSafeArea(.all)
VStack {
ProgressView()
Button("Stop loading") {
isLoading.toggle()
}
}
}
}
}
}
}
The problem is that I cannot make this loading screen translucent. sheet
or popover
behave the same way.
Upvotes: 45
Views: 29265
Reputation: 454
Use presentationBackground to set desired background for modals (fullScreenCover, sheet, popover). From the documentation:
Allows views behind the presentation to show through translucent styles.
struct ContentView: View {
@State private var isLoading = false
var body: some View {
VStack {
Text("Hello there")
Button("Start loading") { isLoading.toggle() }
.fullScreenCover(isPresented: $isLoading) {
ZStack{
VStack {
ProgressView()
Button("Stop loading") { isLoading.toggle() }
}
}
.presentationBackground(.black.opacity(0.5))
}
}
}
}
Availability
@available(iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4, *)
Upvotes: 39
Reputation: 463
struct TransparentBackgroundView: UIViewRepresentable {
var color: UIColor = .clear
func makeUIView(context: Context) -> UIView {
let view = _UIBackgroundBlurView()
view.color = color
return view
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
class _UIBackgroundBlurView: UIView {
var color :UIColor = .clear
override func layoutSubviews() {
super.layoutSubviews()
superview?.superview?.backgroundColor = color
}
}
Usage:
PresenterView().fullScreenCover(isPresented: $isPresented) {
PresentedView().background(TransparentBackgroundView())
}
Upvotes: 1
Reputation: 804
Update: please use Povilas's answer above to avoid flickering screen issue
Asperi's answer is beautiful, but in case you want background to be transparent and not blurred, here is how you can modify it. I also moved the code into a modifier for convenience. (xcode 13.3, iOS 15.4.1)
extension View {
func transparentFullScreenCover<Content: View>(isPresented: Binding<Bool>, content: @escaping () -> Content) -> some View {
fullScreenCover(isPresented: isPresented) {
ZStack {
content()
}
.background(TransparentBackground())
}
}
}
struct TransparentBackground: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
let view = UIView()
DispatchQueue.main.async {
view.superview?.superview?.backgroundColor = .clear
}
return view
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
Upvotes: 10
Reputation: 1286
I found this cleaner solution for the flicker issue in the clear background.
struct ClearBackgroundView: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
return InnerView()
}
func updateUIView(_ uiView: UIView, context: Context) {
}
private class InnerView: UIView {
override func didMoveToWindow() {
super.didMoveToWindow()
superview?.superview?.backgroundColor = .clear
}
}
}
Usage
PresenterView()
.fullScreenCover(isPresented: $isPresented) {
PresentedView()
.background(ClearBackgroundView())
}
Upvotes: 23
Reputation: 258375
Here is a demo of possible way. Parameters of visual effect you can tune for your needs.
Tested with Xcode 12 / iOS 14.
// ... other code
.fullScreenCover(isPresented: $isLoading) {
ZStack{
Color.black.opacity(0.5).edgesIgnoringSafeArea(.all)
VStack {
ProgressView()
Button("Stop loading") {
isLoading.toggle()
}
}
}
.background(BackgroundBlurView())
}
}
}
}
struct BackgroundBlurView: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
let view = UIVisualEffectView(effect: UIBlurEffect(style: .light))
DispatchQueue.main.async {
view.superview?.superview?.backgroundColor = .clear
}
return view
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
Upvotes: 55
Reputation: 565
Building on top of f3dm76 answer:
I changed it so content behind would not flicker (that happened to me with lazy images loading behind fullScreenCover). Also I wanted to use custom transitions for full screen content (or in some cases no animation at all), so I removed default animations with this approach as well.
extension View {
func transparentNonAnimatingFullScreenCover<Content: View>(isPresented: Binding<Bool>, content: @escaping () -> Content) -> some View {
modifier(TransparentNonAnimatableFullScreenModifier(isPresented: isPresented, fullScreenContent: content))
}
}
private struct TransparentNonAnimatableFullScreenModifier<FullScreenContent: View>: ViewModifier {
@Binding var isPresented: Bool
let fullScreenContent: () -> (FullScreenContent)
func body(content: Content) -> some View {
content
.onChange(of: isPresented) { isPresented in
UIView.setAnimationsEnabled(false)
}
.fullScreenCover(isPresented: $isPresented,
content: {
ZStack {
fullScreenContent()
}
.background(FullScreenCoverBackgroundRemovalView())
.onAppear {
if !UIView.areAnimationsEnabled {
UIView.setAnimationsEnabled(true)
}
}
.onDisappear {
if !UIView.areAnimationsEnabled {
UIView.setAnimationsEnabled(true)
}
}
})
}
}
private struct FullScreenCoverBackgroundRemovalView: UIViewRepresentable {
private class BackgroundRemovalView: UIView {
override func didMoveToWindow() {
super.didMoveToWindow()
superview?.superview?.backgroundColor = .clear
}
}
func makeUIView(context: Context) -> UIView {
return BackgroundRemovalView()
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
Upvotes: 23
Reputation: 7
You can also use the Material background type:
ZStack{
...
}
.background(.ultraThinMaterial)
See documentation for more usage: https://developer.apple.com/documentation/swiftui/material
Upvotes: -2