CGRectIntegral: Returns the smallest rectangle that results from converting the source rectangle values to integers.

It's important that CGRect values all are rounded to the nearest whole point. Fractional values cause the frame to be drawn on a pixel boundary. Because pixels are atomic units (cannot be subdivided) a fractional value will cause the drawing to be averaged over the neighboring pixels, which looks blurry.
CGRectIntegral will floor each origin value, and ceil each size value, which will ensure that your drawing code will crisply align on pixel boundaries.
As a rule of thumb, if you are performing any operations that could result in fractional point values (e.g. division, CGRectGetMid[X|Y], or CGRectDivide), use CGRectIntegral to normalize rectangles to be set as a view frame.

 Technically, since the coordinate system operates in terms of points, Retina screens, which have 4 pixels for every point, can draw ± 0.5f point values on odd pixels without blurriness.

CGRect r = CGRectMake(1.222, 1.3333, 1, 1.000);
NSLog(@"%@", NSStringFromCGRect(r));
// {{1.222, 1.3333}, {1, 1}}
NSLog(@"%@", NSStringFromCGRect(CGRectIntegral(r)));
//{{1, 1}, {2, 2}}
NSLog(@"%@", NSStringFromCGRect(CGRectMake(floorf(r.origin.x), floorf(r.origin.y), floorf(r.size.width), floorf(r.size.height))));
//{{1, 1}, {1, 1}}
NSLog(@"%@", NSStringFromCGRect(CGRectMake(ceilf(r.origin.x), ceilf(r.origin.y), ceilf(r.size.width), ceilf(r.size.height))));
//{{2, 2}, {2, 1}}

Of course there are a variety of reasons why you might end up with blurry text in a UILabel or elsewhere in your iOS app, but if you are computing the position of the text element (or its parent, or parent’s parent, etc.) one of the most common reasons is that you have managed to place your text at a fractional location instead of a clean integer coordinate. It is quite possible for apps experiencing this issue to have some lines of text that look perfectly clear and others that ruin your gorgeous interface with blurry, out-of-focus edges.


How did this happen?

Chances are that somewhere in your code you are computing the x, y coordinates of a frame that will contain text by dividing one value by another. This makes sense if you are trying to center one element within another, but it can leave you with coordinates like 1.358490566037, 23.789473684210 instead of 1,24. One solution is to check all of your frame-coordinate computing math and manually adjust it, but there is a much simpler solution.


How do I fix it?

Naturally you want crisp, clear, beautiful type throughout your app’s UI. No problem! The secret is to simply pass your CGRect frame through CGRectIntegral to convert any decimal values to their integer equivalents. Like so:

frame = CGRectIntegral(frame);

– or –

myTextView.frame = CGRectIntegral(myTextView.frame);

That’s it! The only thing you need to remember is that if you are frequently changing the size and position of your elements on the fly, and some of those elements contain text, you may need to keep up with cleaning your frames as you go by including this code at the point where you are making the change. Of course, during an animation sequence the blurry anti-aliasing will not be noticeable and may even be desirable. You just need to make sure that you always end up back at integer coordinates.

No comments:

Post a Comment