92

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()
}
4
  • 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) Commented Aug 30, 2019 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: stackoverflow.com/questions/57441654/… Commented Aug 30, 2019 at 13:33
  • maybe this could help stackoverflow.com/questions/57577462/… Commented Aug 30, 2019 at 14:28
  • 1
    Possible duplicate of Get width of a view using in SwiftUI Commented Aug 31, 2019 at 11:42

6 Answers 6

136

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
Sign up to request clarification or add additional context in comments.

8 Comments

GeometryReader is the way to go! It will update according to system event such as orientation change
And this would require UIKit, so no more building that SwiftUI for Mac.
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.
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.
UIScreen.main.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.
|
75

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

5 Comments

I tested the code but, at least in my preview, the image was still the same size of the screen instead of a square.
@RubensNeto - 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.
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 have you looked at GeometryProxy? "A proxy for access to the size and coordinate space (for anchor resolution) of the container view."
Note that there is a typo in this answer....the height should be geometry.size.height (not width)
33

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

5 Comments

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.
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.
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 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).
Not work in Xcode preview but work for simulators and devices.
28

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

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

5 Comments

GeometryReader is the way to go! It will update according to system event such as orientation change
@Sajjon GeometryReader also breaks your screen and causes things to expand unnecessarily causeing more headaches.
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.
@Justacoder you can wrap GeometryReader in .background() or hide it in ZStack to avoid layout problems
UIScreen.main.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.
12

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)
}

2 Comments

This is the officially documented answer. I don't get why other answers got so many upvotes and not this one.
@Farid Perhaps because the question is "How to get the iPhone's screen width in SwiftUI?", but the intentions was "resize an Image frame to be a square that takes the same width of the iPhone's screen" This answer is perfect in relation to the Image, while others answer the direct question about the screen size. Both are valuable.
0

Well, I fix it by apply GeometryReader to get the screen width and use it.

    var body: some View {
        GeometryReader { proxy in
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(spacing: 20) {
                    ForEach(Array(zip(tabBarOptions.indices, tabBarOptions)),
                            id: \.0,
                            content: {
                        index, name in
                        TabBarItem(currentTab: $currentTab,
                                   namespace: namespace,
                                   tabBarItemName: name,
                                   tab: index)
                    })
                }
                .padding(.horizontal)
                .frame(width: proxy.size.width)
//                .border(.red, width: 4)
            }
            .frame(maxWidth: .infinity)
            .frame(height: 80)
//            .border(.green, width: 2)
            .ignoresSafeArea(.all)
        }
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.