28

I am new to SwiftUI. I want to design a custom view with few controls and a background image. The background image should cover the complete view. It should look like a watermark for the screen. How can I achieve this using SwiftUI?

7 Answers 7

57

Use ZStack but Don't use UIScreen:

var body: some View {
    ZStack {
        Image("BG")
            .resizable()
            .scaledToFill()
            .frame(minWidth: 0) // 👈 This will keep other views (like a large text) in the frame
            .edgesIgnoringSafeArea(.all)
        
        Text("Long multiline text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer eu nulla id lorem pulvinar tincidunt. Donec ut pharetra lacus, et consectetur neque. Etiam quis congue purus. Praesent eu tempus quam, at tristique elit. Ut non urna mi. Maecenas nec arcu neque. Mauris iaculis quam at sapien luctus tristique.")
            .padding()
    }
}

Using UIScreen will lead your app to undesired behavior if you are supporting multiscreen app, resizable windows, multiwindow app, etc.

💡Important note:

Use minWidth for the frame of the image to prevent the overflowing of other views inside the ZStack.

Sign up to request clarification or add additional context in comments.

3 Comments

But how to make the Text (in my case I have some scrollview) that does not ignore safe area?
This leads to Text and generall content overflow, because the Image does not respect the width/height boundaries of the device. Even adding .frame(maxWidth: .infinity) doesnt do it. Its better to use the approach of Antonio Lopes, which is super clean.
@CameronBaba thanks for mentioning that, I have added the frame modifier to prevent that.
25

Try this:

    var body: some View {
    ZStack {
        Text("Hello")
    }
    .background(
        Image("Background")
            .resizable()
            .edgesIgnoringSafeArea(.all)
            .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
    )
}

2 Comments

Thank you, this solves the problem with running up content on an iPad.
Oh thank God. This works for me.
9

UPDATE

with iOS 15 Xcode 13.2

public extension View {
    func fullBackground(imageName: String) -> some View {
       return background(
                Image(imageName)
                    .resizable()
                    .scaledToFill()
                    .edgesIgnoringSafeArea(.all)
       )
    }
}

use

YourView
.fullBackground(imageName: "imageName")

END UPDATE

OLD ANSWER

I prefer a @ViewBuilder

Usage:

BackgroundView(imageName: "image name") {         
     Text("Hello")                               
}

@ViewBuilder

public struct BackgroundView <Content : View> : View {
    public var content : Content
    public var imageName: String
    public var opacity: Double
    public init(imageName: String, opacity: Double=1,@ViewBuilder content: () -> Content) {
        self.content = content()
        self.imageName = imageName
        self.opacity = opacity
    }
    
    public var body: some View {
        GeometryReader { geo in
            ZStack {
                Image(imageName)
                    .resizable()
                    .scaledToFill()
                    .edgesIgnoringSafeArea(.all)
                    .frame(width: geo.size.width, height: geo.size.height, alignment: .center)
                    .opacity(opacity)
                content
            }
        }
    }
}

Comments

3

The answer of Mojtaba is incomplete. The problem is the bounding box of the image can exceed the screen leading to undesired behavior. To correct this we need to add .frame to the mix. This will make sure the image is as large as the screen and fixes our problem.

struct BackgroundView: View {
    var body: some View {
        GeometryReader { proxy in
            Image("Background")
                .resizable()
                .scaledToFill()
                .frame(width: proxy.size.width, height: proxy.size.height)
                .clipped()
        }.ignoresSafeArea()
    }
}

Comments

0

If you just want to use the full screen width and height, and you don't bother about the aspect ratio of the image, you can either use a geometry reader of the UIScreen size's.

.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)

This line of code will set the Image to the max size of the device. Depending on your parent view and device, you may need to use:

.edgesIgnoringSafeArea(.all)

This will ignore the safe area's on iPhone X devices and above.

Comments

0

You can achieve this using ZStack like below. You can add more control on Image for example I added Text()

var body: some View {
      ZStack{
           Image("your image")
        .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
           Text("Hello World").foregroundColor(.white).font(.system(size: 20)).bold()
       }
}

You can Ignore SafeArea using

.edgesIgnoringSafeArea(.all)

Comments

-1
struct LoginView: View {
    var body: some View {
        Image("imageName")
            .resizable()
            .ignoresSafeArea(.all)
    }
}

1 Comment

This would be much more useful if it explained how this code answers the question. The other answers to this question have good explanations. A new answer should provide new insights and good explanations.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.