33

My App uses GeometryReader with some padding to setup a View frame dimension inside a NavigationView.

Since iOS 14 i get the following error message:

Invalid frame dimension (negative or non-finite)

Here is some example code to test:

import SwiftUI

struct ContentView: View {

    let padding:CGFloat = 16.0

    var body: some View {
        NavigationView {
            GeometryReader { p in
        Text("Hello, world!")
            .frame(width: p.size.width - padding)
            .padding()
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Removing NavigationView fix the problem, but I need the current width and height of the container View inside the NavigationView.

Any suggestion?

Franco
  • 441
  • 1
  • 4
  • 3
  • 2
    Same issue here. Though it seems to be a warning on my side. Everything works as expected but this message is printed in the XCode console – Thermometer Sep 26 '20 at 17:07
  • This question might help: https://stackoverflow.com/questions/64543714/swiftui-behavior-of-frameheight-nil – Mikael Weiss Oct 26 '20 at 21:44

9 Answers9

22

I think it might be a static analysis issue as .frame(width: p.size.width - padding) could result in a negative value. Try:

.frame(width: abs(p.size.width - padding))
stephen
  • 1,617
  • 3
  • 20
  • 27
  • If you're given a negative value it will set the width of the view to the positive counterpart of that number (ie. if given -5, width will be 5). If that's what you're looking for, then that's alright, but if you're not expecting that, then it could lead to some surprising results. – Mikael Weiss Oct 26 '20 at 12:11
7

You can not use negative value for frame parameters. If you use .infinity or negative value for frame, change this with nil:

Wrong:

.frame(width: visible ? .infinity : 0,
                           height: visible ? .infinity : 0,
                           alignment: .bottomTrailing)

Correct:

.frame(width: visible ? nil : 0,
                           height: visible ? nil : 0,
                           alignment: .bottomTrailing)
erdikanik
  • 674
  • 9
  • 11
3

When I tested with macOS 11.1, I found that GeometryReader could not return the actual size immediately at the beginning of the view init, the size obtained for the first time was incorrect, maybe need to do a dispatch async operation to avoid this warning

Warning: Invalid frame dimension (negative or non-finite)

marko-36
  • 1,309
  • 3
  • 23
  • 38
Patrick Fu
  • 61
  • 4
3

this helped me:

i added a Group { } around my first Element (which was a VStack with a .frame) in the body. The group has no ".frame" etc. worked on my MacOS app to get rid of this stupid issue.

or you can add something like

.frame(minWidth: 300, idealWidth: 400, maxWidth: 600, minHeight: 300, idealHeight: 400, maxHeight: 500, alignment: .top)

to your VStack (or whatever) to clearly say what dimensions you want.

edit strange this error will appear again, but not immediatly...

ma2412
  • 135
  • 7
2

I ran into the same issue. I didn't get to the bottom of why being in a NavigationView causes this, but a workaround is to wrap the whole NavigationView in a GeometryReader and pass the GeometryProxy down the tree.

0xWood
  • 1,326
  • 12
  • 15
2

Use this:

.frame(width: UIScreen.main.bounds.width, height: 200)
Timmy
  • 4,098
  • 2
  • 14
  • 34
Hesham Yemen
  • 437
  • 5
  • 6
1

As Patric Fu states it is normally an initialization issue that generates the warning message "Invalid frame dimension (negative or non-finite)."

In my case this warning was generated on the following statement

.frame(width: frameSide, height: frameSide)

When frameSide was generated with the following statement

let frameSide = min(innerRectangle.width, innerRectangle.height)

Changing the above statement to provide a default value as below

let frameSide = innerRectangle.width.isNaN ? 0 : min(innerRectangle.width, innerRectangle.height)

Resolved the issue and the warning is not generated.

The condition within the Ternary operator would, of course, vary depending on the construction of the variable "frameSide". I found this a simpler approach than using a "dispatch async operation". This answer would be more likely to help if it was attached as a comment to Patric Fu's original answer. However I have insufficient reputation to comment.

Byron
  • 31
  • 1
  • 4
0

try this, it works for me ~

.frame(width:abs(GeometryReader_width)))
VanErr
  • 1
  • 1
0
struct YouContentView: View {

   let padding:CGFloat = 26.0
   @State private var width:CGFloat = 0

    var body: some View {
        NavigationView {
         
        Text("Hello, world!")
                .background(
                    
                    GeometryReader(content: { geo in
                        Text("YouText Here")
                            .background(.red)
                            .frame(width: geo.frame(in: .global).width - padding)
                }))
            
          
        }
    }
}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 07 '22 at 13:53