Why `childView.convert(childView.bounds, to: parentView)` is not equal to `childView.frame` for UIPickerTableViewWrapperCell?

Viewed 81

According to my understanding, frame is a view's location and size using the parent view's coordinate system, bounds is a view's location and size using its own coordinate system, which means childView.convert(childView.bounds, to: parentView) should be equal to childView.frame.

enter image description here

enter image description here

But I have found this is not the case for UIPickerTableViewWrapperCell, as you can see from the picture, the frame is (origin = (x = 0, y = 160032.44775782057), size = (width = 134, height = 30.248210824676789)), and the convert(bounds, to: parentView) is (origin = (x = -71.984082796011151, y = 160032.44585526528), size = (width = 134.04199076956854, height = 29.70736933068838))

Why these two values are different?

1 Answers

With a little bit of digging around, I've located an instructive article that may help you understand why this seeming discrepancy is occurring.

For the sake of avoiding a broken link in the future, I'll add the scenario below.

The author begins by taking an example rectangle drawn on the screen. They print out the frame and bounds values with the following function:

private func printAll() {
    print("=========================")
    print("X : ", self.orangeView.frame.origin.x)
    print("Y : ", self.orangeView.frame.origin.y)
    print("width : ", self.orangeView.frame.size.width)
    print("height : ", self.orangeView.frame.size.height)
    print("=========================")
    print("X : ", self.orangeView.bounds.origin.x)
    print("Y : ", self.orangeView.bounds.origin.y)
    print("width : ", self.orangeView.bounds.size.width)
    print("height : ", self.orangeView.bounds.size.height)
    print("=========================")
}

Using this, they initially begin with an equal width and height:

=========================
X :  87.0
Y :  384.0
width :  240.0
height :  128.0
=========================
X :  0.0
Y :  0.0
width :  240.0
height :  128.0
=========================

They then perform a transformation on the rectangle to rotate it:

self.orangeView.transform = CGAffineTransform(rotationAngle: 5)

This, of course, results in the height and width of the element changing within the parent (.frame) scope:

=========================
X :  111.58938416597198
Y :  314.7747071707769
width :  190.82123166805604
height :  266.45058565844624
=========================
X :  0.0
Y :  0.0
width :  240.0
height :  128.0
=========================

The reason for this, of course, is that by rotating the drawn rectangle, the framing rectangle has changed in size, even though the drawn rectangle has not changed dimensions. Apple is also fully aware of this as indicated in their documentation, stating the following:

Warning

If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.

This is important because we can infer some important behavior from the data you've provided. Specifically, if we ignore the coordinates and instead look at the height and width values, we see the following differences:

frame:   (width = 134, height = 30.248210824676789)
bounds:  (width = 134, height = 32)
convert: (width = 134.04199076956854, height = 29.70736933068838)

Looking at the UI element itself in the image, we even see text that is being "squished" vertically, indicating that these elements are most likely having transforms applied in some manner. This can result in the the aforementioned undefined behavior indicated in Apple's documentation. And as Apple has indicated in their documentation, when the transform property is not the identity transform, the property's value should be ignored.

Unfortunately we cannot verify that this is the cause of the problem as UIKit, as far as I can tell from digging around, does not share the source code for it, but transforms causing undefined behavior seems like the best explanation we can reasonably arrive at.

Related