Search code examples
iphoneiosipaduiwebviewmagnification

How to add a magnifier to custom control?


How to add a magnifier to custom control? Control is a child of UIView. (It's a UIWebView - but native magnification functionality doesn't work at some pages.)

UPDATED:

Maybe it's possible to force a draw of magnifier on UIWebView?


Solution

  • 1. Add the following files to your project:

    MagnifierView.h:

    //
    //  MagnifierView.h
    //  SimplerMaskTest
    //
    
    #import <UIKit/UIKit.h>
    
    @interface MagnifierView : UIView {
        UIView *viewToMagnify;
        CGPoint touchPoint;
    }
    
    @property (nonatomic, retain) UIView *viewToMagnify;
    @property (assign) CGPoint touchPoint;
    
    @end
    

    MagnifierView.m:

    //
    //  MagnifierView.m
    //  SimplerMaskTest
    //
    
    #import "MagnifierView.h"
    #import <QuartzCore/QuartzCore.h>
    
    @implementation MagnifierView
    @synthesize viewToMagnify;
    @dynamic touchPoint;
    
    - (id)initWithFrame:(CGRect)frame {
        return [self initWithFrame:frame radius:118];
    }
    
    - (id)initWithFrame:(CGRect)frame radius:(int)r {
        int radius = r;
    
        if ((self = [super initWithFrame:CGRectMake(0, 0, radius, radius)])) {
            //Make the layer circular.
            self.layer.cornerRadius = radius / 2;
            self.layer.masksToBounds = YES;
        }
    
        return self;
    }
    
    - (void)setTouchPoint:(CGPoint)pt {
        touchPoint = pt;
        // whenever touchPoint is set, update the position of the magnifier (to just above what's being magnified)
        self.center = CGPointMake(pt.x, pt.y-66);
    }
    
    - (CGPoint)getTouchPoint {
        return touchPoint;
    }
    
    - (void)drawRect:(CGRect)rect {
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGRect bounds = self.bounds;
        CGImageRef mask = [UIImage imageNamed: @"[email protected]"].CGImage;
        UIImage *glass = [UIImage imageNamed: @"[email protected]"];
    
        CGContextSaveGState(context);
        CGContextClipToMask(context, bounds, mask);
        CGContextFillRect(context, bounds);
        CGContextScaleCTM(context, 1.2, 1.2);
    
        //draw your subject view here
        CGContextTranslateCTM(context,1*(self.frame.size.width*0.5),1*(self.frame.size.height*0.5));
        //CGContextScaleCTM(context, 1.5, 1.5);
        CGContextTranslateCTM(context,-1*(touchPoint.x),-1*(touchPoint.y));
        [self.viewToMagnify.layer renderInContext:context];
    
        CGContextRestoreGState(context);
        [glass drawInRect: bounds];
    }
    
    - (void)dealloc {
        [viewToMagnify release];
        [super dealloc];
    }
    
    
    @end
    

    TouchReader.h:

    //
    //  TouchReader.h
    //  SimplerMaskTest
    //
    
    #import <UIKit/UIKit.h>
    #import "MagnifierView.h"
    
    @interface TouchReader : UIView {
        NSTimer *touchTimer;
        MagnifierView *loop;
    }
    
    @property (nonatomic, retain) NSTimer *touchTimer;
    
    - (void)addLoop;
    - (void)handleAction:(id)timerObj;
    
    @end
    

    TouchReader.m:

    //
    //  TouchReader.m
    //  SimplerMaskTest
    //
    
    #import "TouchReader.h"
    #import "MagnifierView.h"
    
    @implementation TouchReader
    
    @synthesize touchTimer;
    
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        self.touchTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(addLoop) userInfo:nil repeats:NO];
    
        // just create one loop and re-use it.
        if (loop == nil) {
            loop = [[MagnifierView alloc] init];
            loop.viewToMagnify = self;
        }
    
        UITouch *touch = [touches anyObject];
        loop.touchPoint = [touch locationInView:self];
        [loop setNeedsDisplay];
    }
    
    - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
        [self handleAction:touches];
    }
    
    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
        [self.touchTimer invalidate];
        self.touchTimer = nil;
        [loop removeFromSuperview];
    }
    
    - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
        [self.touchTimer invalidate];
        self.touchTimer = nil;
        [loop removeFromSuperview];
    }
    
    - (void)addLoop {
        // add the loop to the superview.  if we add it to the view it magnifies, it'll magnify itself!
        [self.superview addSubview:loop];
        // here, we could do some nice animation instead of just adding the subview...
    }
    
    - (void)handleAction:(id)timerObj {
        NSSet *touches = timerObj;
        UITouch *touch = [touches anyObject];
        loop.touchPoint = [touch locationInView:self];
        [loop setNeedsDisplay];
    }
    
    - (void)dealloc {
        [loop release];
        loop = nil;
        [super dealloc];
    }
    
    @end
    

    Based on: http://coffeeshopped.com/2010/03/a-simpler-magnifying-glass-loupe-view-for-the-iphone

    2. Add the following images:

    Used images on the code:

    [email protected]:

    loupe-hi@2x.png

    [email protected]:

    loupe-mask@2x.png

    Original but centered images with a shadow (not used at this moment):

    [email protected]:

    loupe-shadow-hi@2x.png

    [email protected]:

    loupe-shadow-mask@2x.png

    3. Replace the main UIView on your xib-file by TouchReader

    The magnifier will work automaticaly except controls that captures touch events themselfs (for example, UIWebView). And the code above doesn't support the images with a shadow. Please add new answer to the qustion if you successfully fix this issue.

    UPDATED:

    Change the following code to add UIWebView support. UIView should remain UIView.

    @interface TouchReader : UILongPressGestureRecognizer
    

    And add a gesture to webView:

    TouchReader* gestureMagnifier = [[[TouchReader alloc] initWithTarget:self action:@selector(handleMagnifier:)] autorelease];
    gestureMagnifier.webView = editSource;
    gestureMagnifier.delegate = self;
    gestureMagnifier.minimumPressDuration = 0.5;
    [webView addGestureRecognizer:gestureMagnifier];