Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS blurred text: detecting & solving it once and for all?

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.

main.m

#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

UIViewOverride.m

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.

like image 268
mvds Avatar asked Jan 09 '11 03:01

mvds


1 Answers

You can get this same functionality through the CoreAnimation instrument and its misaligned flag.

like image 163
Joshua Weinberg Avatar answered Sep 25 '22 21:09

Joshua Weinberg