72

I want to resize an Image frame to be a square that takes the same width of the iPhone's screen and consequently the same value (screen width) for height.

The following code don't work cause it gives the image the same height of the view.

var body: some View {
        Image("someImage")
            .resizable()
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
            .clipped()
    }
Ky -
  • 30,724
  • 51
  • 192
  • 308
Rubens Neto
  • 802
  • 1
  • 6
  • 8
  • 1
    It sounds like you have a different issue, because you are working with `Image`. (And yes, there are two fairly simple ways to get the screen width, but I don't think they will solve your problem.) Have you tried adding this modifier? `.aspectRatio(contentMode: .fit)` –  Aug 30 '19 at 13:28
  • 1
    Second comment... I upvoted the answer by @DoesData because it's one to two `SwiftUI` ways of doing this (and didn't downvote the answer from @MehmetAliVataniar even though it makes some serious non-SwiftUI assumptions. The second way - found in the following question, uses subclassing `UIHostingController` and may get you *much* more things you can do: https://stackoverflow.com/questions/57441654/swiftui-repaint-view-components-on-device-rotation/57442517#comment101361813_57442517 –  Aug 30 '19 at 13:33
  • maybe this could help https://stackoverflow.com/questions/57577462/get-width-of-a-view-using-in-swiftui/57577752#57577752 – Giuseppe Sapienza Aug 30 '19 at 14:28
  • 1
    Possible duplicate of [Get width of a view using in SwiftUI](https://stackoverflow.com/questions/57577462/get-width-of-a-view-using-in-swiftui) – Giuseppe Sapienza Aug 31 '19 at 11:42

5 Answers5

107

You can create a UIScreen extension for the same. Like:

extension UIScreen{
   static let screenWidth = UIScreen.main.bounds.size.width
   static let screenHeight = UIScreen.main.bounds.size.height
   static let screenSize = UIScreen.main.bounds.size
}

Usage:

UIScreen.screenWidth
bestiosdeveloper
  • 2,339
  • 1
  • 11
  • 28
  • 23
    GeometryReader is the way to go! It will update according to system event such as orientation change – Sajjon Aug 30 '19 at 14:29
  • And this would require UIKit, so no more building that SwiftUI for Mac. – Andres Canella Jun 02 '20 at 22:28
  • 1
    thanks for this. hopefully soon there will be a swiftui native code for it. but this worked for what I needed as my was already nested within another view so Geometry Reader wasn't working for me. I couldn't get global width and height. – Jayson Jul 05 '20 at 09:16
  • 3
    The `.size` isn't necessary on `UIScreen.main.bounds`, so you can use `UIScreen.main.bounds.width/height` directly, eliminimating one step and making the raw calls without the exception more viable. – Jeehut Jul 17 '20 at 07:30
  • @Jeehut: deprecated since iOS 2.0 – vikingosegundo Dec 29 '21 at 19:28
  • seems cleaner than using the geometryReader – aaronium112 Jul 16 '22 at 00:51
  • [`UIScreen.main.bounds`](https://developer.apple.com/documentation/uikit/uiscreen/1617838-bounds) actually does update with orientation changes ("the value of this property may change when the device rotates"). `nativeBounds` is the one that does not. – shim Jul 21 '22 at 17:00
  • This is wrong (and has been wrong for many years). There's no guarantee your app is running on the main window. – thefaj Aug 02 '22 at 16:32
63

Try using Geometry Reader

let placeholder = UIImage(systemName: "photo")! // SF Symbols

struct ContentView: View {
    var body: some View {
        GeometryReader { geometry in
            Image(uiImage: placeholder) 
            .resizable()
            .frame(width: geometry.size.width, height: geometry.size.width, alignment: .center)
            // .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
            .clipped()
        }
    }
}

enter image description here

SHAH MD IMRAN HOSSAIN
  • 2,558
  • 2
  • 25
  • 44
DoesData
  • 6,594
  • 3
  • 39
  • 62
  • I tested the code but, at least in my preview, the image was still the same size of the screen instead of a square. – Rubens Neto Sep 03 '19 at 08:07
  • [@RubensNeto](https://stackoverflow.com/users/6316195/rubens-neto) - I'm not sure about a preview since I am not running OSX Catalina, but running this code on simulator / device yields the above output where the width / height are equal. I hope that helps. – DoesData Sep 03 '19 at 09:54
  • 4
    The problem with GeometryReader is that it applies only to the level you are at. If you trying to write a generic modal pop-up and want to obscure the screen below the pop-up, GeometryReader will not give you any information to do that. To do that properly requires a lot of code. – P. Ent Nov 04 '20 at 14:04
  • @P.Ent have you looked at [GeometryProxy](https://developer.apple.com/documentation/swiftui/geometryproxy)? "A proxy for access to the size and coordinate space (for anchor resolution) of the container view." – DoesData Nov 04 '20 at 20:00
  • 2
    Note that there is a typo in this answer....the height should be geometry.size.height (not width) – GarySabo Mar 28 '21 at 16:00
24

You can use UIScreen.main.bounds .width or .height

.frame(
   width:UIScreen.main.bounds.width,
   height:UIScreen.main.bounds.height
)
  • 2
    GeometryReader is the way to go! It will update according to system event such as orientation change – Sajjon Aug 30 '19 at 14:29
  • 16
    @Sajjon GeometryReader also breaks your screen and causes things to expand unnecessarily causeing more headaches. – Just a coder Dec 01 '20 at 13:46
  • 3
    This is the certain way to know the HARDWARE screen size, as specified in the question. And more importantly, if you need to get the hardware screen _height_ then GeometryReader will fail you -- it subtracts out the status bar at the top of the screen! And, thankfully, `let hardwareScreenSize = UIScreen.main.bounds.size` works just fine in SwiftUI. – ConfusionTowers Mar 18 '21 at 00:34
  • @Justacoder you can wrap GeometryReader in .background() or hide it in ZStack to avoid layout problems – Hai Feng Kao Jul 26 '21 at 14:25
  • 1
    [`UIScreen.main.bounds`](https://developer.apple.com/documentation/uikit/uiscreen/1617838-bounds) actually does update with orientation changes ("the value of this property may change when the device rotates"). `nativeBounds` is the one that does not. – shim Jul 21 '22 at 16:59
16

I've come up with a solution using Environment Keys, by creating the following:

private struct MainWindowSizeKey: EnvironmentKey {
    static let defaultValue: CGSize = .zero
}

extension EnvironmentValues {
    var mainWindowSize: CGSize {
        get { self[MainWindowSizeKey.self] }
        set { self[MainWindowSizeKey.self] = newValue }
    }
}

Then by reading the size from where the window is created:

var body: some Scene {
    WindowGroup {
        GeometryReader { proxy in
            ContentView()
                .environment(\.mainWindowSize, proxy.size)
        }
    }
}

Finally I can directly get the window size in any SwiftUI view, and it changes dynamically (on device rotation or window resizing on macOS):

@Environment(\.mainWindowSize) var mainWindowSize
  • Highly recommended if you are planning to support MacOS and split view on iPads. Once you define the UIScreen.main.bounds value you have to always force an update. But this example since an environment variable allows you to use it in multiple levels in your app. – Tyler Hackbart Dec 13 '22 at 18:49
  • Great tip! I use this to determine the height of a chart, a % of the visible space, which is included within a ScrollView so that the chart is the predominant thing on the screen real estate, but you can still get a sense that there is more below. – stanlemon Feb 26 '23 at 02:07
  • I'm not sure how this works. I've tried it and putting `@Environment(\.mainWindowSize) var mainWindowSize` into my views and trying to call `Text("\(mainWindowSize.width)")` for example just always shows 0.00000. I'm missing somerthing? iOS 16.4 / Xcode 14.3 – Rillieux Apr 14 '23 at 06:35
  • @Rillieux Have you wrapped the proper root `View` with `GeometryReader`? Also SwiftUI sometimes does not pass Environment variables when you're within a `.sheet()` modifier for example so this might be why you're getting the default 0 value (in this case you have to pass it again to the contained `View` with `.environment(\.mainWindowSize, main)`, having `@Environment(\.mainWindowSize) var mainWindowSize` defined in the parent `View`). – Thibault Farnier Apr 14 '23 at 15:51
8

The simplest way would be to make the image resizable and set the aspect ratio to 1.0:

var body: some View {
    Image("someImage")
       .resizable()
       .aspectRatio(1.0, contentMode: .fit)
}
Gene Z. Ragan
  • 2,643
  • 2
  • 31
  • 41
LuLuGaGa
  • 13,089
  • 6
  • 49
  • 57
  • 1
    This is the officially documented answer. I don't get why other answers got so many upvotes and not this one. – Farid Feb 25 '23 at 07:49