Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI: Prevent Image() from expanding view rect outside of screen bounds

Tags:

swift

swiftui

What I'm trying to achieve

I'm trying to create a SwiftUI view where an image should expand the entire screen (edgesIgnoringSafeArea(.all)), and then overlay a view on top of that, that also fills the entire screen, but respects the safe area.

What I've tried

This is my code, which comes close:

struct Overlay: View {
  var body: some View {
    VStack {
      HStack {
        EmptyView()
        Spacer()
        Text("My top/right aligned view.")
          .padding()
          .background(Color.red)
      }
      Spacer()
      HStack {
        Text("My bottom view")
          .padding()
          .background(Color.pink)
      }
    }
  }
}

struct Overlay_Previews: PreviewProvider {
  static var previews: some View {
    ZStack {
      Image(uiImage: UIImage(named: "background")!)
        .resizable()
        .edgesIgnoringSafeArea(.all)
        .aspectRatio(contentMode: .fill)
      Overlay()
    }
  }
}

The issue and tested solutions

The issue is that the image is not clipped it looks like, so it expands the parent view to a width larger than the screen width, which then makes the top right aligned red text box float off screen (see image).

enter image description here

I tried using .clipped() in various places, with no luck. I would preferably avoid using GeometryReader if possible.

Q: How can I make the image view only fill the screen?

like image 571
eivindml Avatar asked Aug 21 '19 14:08

eivindml


2 Answers

You have to limit the frame size of the out-of-bounds Image before it is being picked up by the ZStack to avoid the ZStack to grow and so the Overlay to go out of position.

edit: aheze shows with his answer a way around using GeometryReader by putting the Image into the background of Overlay() with .background(Image()..). This avoids the usage of ZStack and GeometryReader completely and is possibly a cleaner solution.

Based on parent view size

struct IgnoringEdgeInsetsView2: View {
    var body: some View {
        ZStack {
            GeometryReader { geometry in
                Image("smile")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .edgesIgnoringSafeArea(.all)
                    .frame(maxWidth: geometry.size.width,
                           maxHeight: geometry.size.height)
            }
            Overlay()
        }
    }
}

Based on screen size

struct IgnoringEdgeInsetsView: View {
    var body: some View {
        ZStack {
            Image("smile-photo")
                .resizable()
                .aspectRatio(contentMode: .fill)
                .edgesIgnoringSafeArea(.all)
                .frame(maxWidth: UIScreen.main.bounds.width, 
                       maxHeight: UIScreen.main.bounds.height)
            Overlay()
        }
    }
}

Example

like image 119
Fabian Avatar answered Oct 19 '22 09:10

Fabian


No need to mess with GeometryReader. Instead, you can prevent the image from overflowing by using the .background() modifier.

struct ContentView: View {
    var body: some View {
        Overlay()
        .background( /// here!
            Image("City")
            .resizable()
            .aspectRatio(contentMode: .fill)
            .ignoresSafeArea()
        )
    }
}

Result:

Image in background, content on top does not overflow
like image 10
aheze Avatar answered Oct 19 '22 09:10

aheze