More than once I've encountered the situation where a UIView (subclass) ends up on a fractional offset, e.g. because its dimensions are odd and it's centered, or because its location is based on the center of an odd-sized container.
This results in blurred text (or images), because iOS will try to render the view (and subviews) on half-pixel offsets. I feel that calling CGRectIntegral()
for every frame-change is not a perfect solution.
I'm looking for the best way to detect those situations easily. While writing this question I came up with quite a drastic approach, which revealed more off-by-½'s in my current project than I could imagine.
So this is for sharing. Comments and suggestions for better or less drastic alternatives are more than welcome.
#import <objc/runtime.h>
#import "UIViewOverride.h"
int main(int argc, char *argv[]) {
#ifdef DEBUGVIEW
Method m1,m2;
IMP imp;
m1 = class_getInstanceMethod([UIView class], @selector(setFrame:));
m2 = class_getInstanceMethod([UIViewOverride class], @selector(setFrameOverride:));
imp = method_getImplementation(m2);
class_addMethod([UIView class], @selector(setFrameOverride:), imp, method_getTypeEncoding(m1));
m2 = class_getInstanceMethod([UIView class], @selector(setFrameOverride:));
method_exchangeImplementations(m1,m2);
m1 = class_getInstanceMethod([UIView class], @selector(setCenter:));
m2 = class_getInstanceMethod([UIViewOverride class], @selector(setCenterOverride:));
imp = method_getImplementation(m2);
class_addMethod([UIView class], @selector(setCenterOverride:), imp, method_getTypeEncoding(m1));
m2 = class_getInstanceMethod([UIView class], @selector(setCenterOverride:));
method_exchangeImplementations(m1,m2);
#endif
// etc
This is implemented as a UIView subclass, which avoids casts and/or compiler warnings.
#define FRACTIONAL(f) (fabs(f)-floor(fabs(f))>0.01)
@implementation UIViewOverride
#ifdef DEBUGVIEW
-(void)setFrameOverride:(CGRect)newframe
{
if ( FRACTIONAL(newframe.origin.x) || FRACTIONAL(newframe.origin.y) )
{
[self setBackgroundColor:[UIColor redColor]];
[self setAlpha:0.2];
NSLog(@"fractional offset for %@",self);
}
[self setFrameOverride:newframe]; // not a typo
}
-(void)setCenterOverride:(CGPoint)center
{
[self setCenterOverride:center]; // not a typo
if ( FRACTIONAL(self.frame.origin.x) || FRACTIONAL(self.frame.origin.y) )
{
[self setBackgroundColor:[UIColor greenColor]];
[self setAlpha:0.2];
NSLog(@"fractional via center for %@",self);
}
}
#endif
Problematic views will generate log messages and turn up transparent and either red or green.
-DDEBUGVIEW
to be set as compiler flag in Debug mode.
You can get this same functionality through the CoreAnimation instrument and its misaligned flag.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With