App hangs when setting constraint to a very specific value (a width divided by 2).

I get a surprising crash when adapting constraints.

  • Xcode 13.2.1
  • iOS 15.2 on simulator

Here is the set up in storyboard:

A subclass of UIView (PopoverView) to draw a popover like frame and label and button inside.

PopoverView can set its arrow position all around, for instance on the right, so that popover will appear on the left of an object,

or on the left, to appear on the right.

Button tap is used to loop through the arrow position.

I have defined constraints (and their IBOutlets), and notably for the centerX position of popoverView to centerX of the label.

I adapt the value of the constraints in viewWillLayoutSubviews to position the popover in correct position relative to the label.

Now the problem.

To position the popover on the right of the label, I set its centerX constraint as follows in viewWillLayoutSubviews():

popoverCenterToLabelCenterConstraint.constant = (popoverLabel.frame.width + label.frame.width) / 2

That works OK

To position the popover on the left of the label, I set its constraint as follows (offset the opposite value):

popoverCenterToLabelCenterConstraint.constant = -(popoverLabel.frame.width + label.frame.width) / 2

I also tried:

popoverCenterToLabelCenterConstraint.constant =  -popoverLabel.frame.width / 2 - label.frame.width / 2

App does not crash but hangs, apparently in an infinite loop. Idem if I divide by 2.0 instead of 2 : label.frame.width / 2.0

The width of the label, at this point of code, is 92.33333333333333

So I replaced by the value directly:

popoverCenterToLabelCenterConstraint.constant =  -popoverLabel.frame.width / 2 - 46.2

and it works OK. Idem with 46.1 So the problem is not due to the position of popoverView

If I replace the div by 2 by 2.01 :

- popoverCenterToLabelCenterConstraint.constant = -popoverLabel.frame.width / 2 - label.frame.width / 2.01

it works !!!!!!

Conclusion: it is the exact div by 2 that causes the hanging.

What could be the reason for this strange behaviour ?

This answer comes from Programming in Objective-C by Stephen Kochan, pg. 52-53, if you don't want to read that, here is the text and explanation from the book, with some commentary of my own, and since most of UIKit is written in Obj-C I surmise these limitations apply here as well

Program 4.1 uses the basic Objective-C data types.

Program 4.1
#import <Foundation/Foundation.h>
int main (int argc, char * argv[])
{
@autoreleasepool {
int integerVar = 100;
float floatingVar = 331.79;
double doubleVar = 8.44e+11;
char charVar = 'W';
NSLog (@"integerVar = %i", integerVar);
NSLog (@"floatingVar = %f", floatingVar);
NSLog (@"doubleVar = %e", doubleVar);
NSLog (@"doubleVar = %g", doubleVar);
NSLog (@"charVar = %c", charVar);
}
return 0;
}
Program 4.1 Output
integerVar = 100
floatingVar = 331.790009
doubleVar = 8.440000e+11
doubleVar = 8.44e+11
charVar = W

In the second line of the program’s output, notice that the value of 331.79 , which is assigned to floatingVar , is actually displayed as 331.790009 . The reason for this inaccuracy is the particular way in which numbers are internally represented inside the computer. You have probably come across the same type of inaccuracy when dealing with numbers on your calculator. If you divide 1 by 3 on your calculator, you get the result .33333333, with perhaps some additional 3s tacked on at the end. The string of 3s is the calculator’s approximation to one third. Theoretically, there should be an infinite number of 3s. But the calculator can hold only so many digits, thus the inherent inaccuracy of the machine. The same type of inaccuracy applies here: Certain floating-point values cannot be exactly represented inside the computer’s memory. If by any chance you know PHP, this ties in with PHP_FLOAT_EPSILON.

So in a nutshell, the computer enters into an infinite loop when you try to divide by 2 because it does not have an exact value for your constraint, which it is trying to calculate with its inherent precision limitation without success, and the low-level raw data type implementation demands that it have an exact value before program execution continues.

Hope this helps

Thanks. But that's pretty strange. What would exact value meaning here ? In that sense, no float number has an "exact" value. And we are not using decimals, we are using Float.

I also tested by pre computing the half width and use this value in the constant. Then it has a value, just as if I was using .pi. It hangs as well…

In addition, if I set like this (1):

popoverCenterToLabelCenterConstraint.constant = -label.frame.width / 2

it works

Idem with this (2):

popoverCenterToLabelCenterConstraint.constant = -popoverLabel.frame.width / 2 

Or this (no minus sign) (3)

popoverCenterToLabelCenterConstraint.constant = label.frame.width / 2 - popoverLabel.frame.width / 2

But hangs with this (4):

popoverCenterToLabelCenterConstraint.constant = -label.frame.width / 2 - popoverLabel.frame.width / 2

So if it were an exact value issue it should hang in (1) and probably (3), isn't it ?

I derived a simplified project from the one with the crash, just keeping a single view with just label and "popverView". It does not crash on

popoverCenterToLabelCenterConstraint.constant = -(popoverLabel.frame.width + label.frame.width) / 2

So I retested the crashing project, unmodified: doesn't crash either anymore…

So, I'm more and more puzzled…

App hangs when setting constraint to a very specific value (a width divided by 2).
 
 
Q