Problem with pdf while opening in UIWebView - iphone

I have a problem while opening a pdf in a UIWebView. Zoom in and Zoom out doesn't work and even double tap doesn't enlarge the pdf font size.
Guys is there any way to do that....
If not can anyone share some code ....

#import
#interface TiledPDFView : UIView {
CGPDFPageRef pdfPage;
CGFloat myScale;
}
- (id)initWithFrame:(CGRect)frame andScale:(CGFloat)scale;
- (void)setPage:(CGPDFPageRef)newPage;
#end
#import "TiledPDFView.h"
#import
#implementation TiledPDFView
// Create a new TiledPDFView with the desired frame and scale.
- (id)initWithFrame:(CGRect)frame andScale:(CGFloat)scale{
if ((self = [super initWithFrame:frame])) {
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
tiledLayer.levelsOfDetail = 4;
tiledLayer.levelsOfDetailBias = 4;
tiledLayer.tileSize = CGSizeMake(512.0, 512.0);
myScale = scale;
}
return self;
}
// Set the layer's class to be CATiledLayer.
+ (Class)layerClass {
return [CATiledLayer class];
}
// Set the CGPDFPageRef for the view.
- (void)setPage:(CGPDFPageRef)newPage
{
CGPDFPageRelease(self->pdfPage);
self->pdfPage = CGPDFPageRetain(newPage);
}
-(void)drawRect:(CGRect)r
{
// UIView uses the existence of -drawRect: to determine if it should allow its CALayer
// to be invalidated, which would then lead to the layer creating a backing store and
// -drawLayer:inContext: being called.
// By implementing an empty -drawRect: method, we allow UIKit to continue to implement
// this logic, while doing our real drawing work inside of -drawLayer:inContext:
}
// Draw the CGPDFPageRef into the layer at the correct scale.
-(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context
{
// First fill the background with white.
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,0.5);
CGContextFillRect(context,self.bounds);
CGContextSaveGState(context);
// Flip the context so that the PDF page is rendered
// right side up.
CGContextTranslateCTM(context, 0.0, self.bounds.size.height);
// Scale the context so that the PDF page is rendered
// at the correct size for the zoom level.
CGContextScaleCTM(context, myScale,myScale);
CGContextDrawPDFPage(context, pdfPage);
CGContextRestoreGState(context);
}
// Clean up.
- (void)dealloc {
CGPDFPageRelease(pdfPage);
[super dealloc];
}
#end
Add this to your project...
Hope it helped....

Make sure that «Multiple Touch» is enabled for the UIWebView.

Related

how to customize MKPolyLineView to draw different style lines

I want to customize the lines drawn on MKMapView to show a route so that the lines have a border color and a fill color. Similar to this where it has a black border and is filled with another color:
I'm currently just returning MKPolyLineView objects from mapView:viewForOverlay: which works fine for plain lines. The docs says the MKPolyLineView is not to be subclassed, so should I subclass MKOverlayView and implement my own drawMapRect? Or should I subclass MKOverlayPathView? Or create a replacement for MKPolylineView?
EDIT - what I'm asking is: where is the place to put your own Quartz drawing code in order to draw your own annotations/overlays? Currently I've created a subclass of MKOverlayView and implement my own drawMapRect:zoomScale:inContext: It's pretty easy to draw the overlay that way but is that the best solution?
You can do this by implementing your own MKOverlayPathView subclass, which draws the path twice in the map rect. Once thicker with black and once thinner on top with another colour.
I have created a simple drop-in replacement of MKPolylineView which lets you do that: ASPolylineView.
If you want to do it yourself, the two main methods that you need to implement could look like this:
- (void)drawMapRect:(MKMapRect)mapRect
zoomScale:(MKZoomScale)zoomScale
inContext:(CGContextRef)context
{
UIColor *darker = [UIColor blackColor];
CGFloat baseWidth = self.lineWidth / zoomScale;
// draw the dark colour thicker
CGContextAddPath(context, self.path);
CGContextSetStrokeColorWithColor(context, darker.CGColor);
CGContextSetLineWidth(context, baseWidth * 1.5);
CGContextSetLineCap(context, self.lineCap);
CGContextStrokePath(context);
// now draw the stroke color with the regular width
CGContextAddPath(context, self.path);
CGContextSetStrokeColorWithColor(context, self.strokeColor.CGColor);
CGContextSetLineWidth(context, baseWidth);
CGContextSetLineCap(context, self.lineCap);
CGContextStrokePath(context);
[super drawMapRect:mapRect zoomScale:zoomScale inContext:context];
}
- (void)createPath
{
// turn the polyline into a path
CGMutablePathRef path = CGPathCreateMutable();
BOOL pathIsEmpty = YES;
for (int i = 0; i < self.polyline.pointCount; i++) {
CGPoint point = [self pointForMapPoint:self.polyline.points[i]];
if (pathIsEmpty) {
CGPathMoveToPoint(path, nil, point.x, point.y);
pathIsEmpty = NO;
} else {
CGPathAddLineToPoint(path, nil, point.x, point.y);
}
}
self.path = path;
}
You can just add two MKPolyLineView objects with the same coordinates, but different thicknesses.
Add one with a lineWidth of 10 (or whatever) with strokeColor set to black.
Then add another with a lineWidth of 6 with strokeColor set to your other desired color.
You can use the same MKPolyLine for both MKPolyLineView objects.
MKPolylineView can only be used for stroking a designated path. You can use some of the properties in MKOverlayPathView to change their appearance but only some of them would apply, e.g. fillColor, strokeColor.
If you want to draw something more complex, you can use MKOverlayPathView. It is more generic and thus suited for more than just stroking paths. For drawing simple lines, the result would be identical to MKPolylineView (at least, according to the docs).
If you want to do more complex drawing, subclass MKOverlayPathView. What you're trying to do is non-trivial.
I use a subclass NamedOverlay that holds an overlay an a name:
NamedOverlay.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface NamedOverlay : NSObject <MKOverlay>
#property (strong, readonly, nonatomic) NSString *name;
#property (strong, readonly, nonatomic) id<MKOverlay> overlay;
-(id)initWithOverlay:(id<MKOverlay>)overlay andName:(NSString *)name;
#end
NamedOverlay.m
#import "NamedOverlay.h"
#implementation NamedOverlay
- (id)initWithOverlay:(id<MKOverlay>)overlay andName:(NSString *)name
{
_name = name;
_overlay = overlay;
return self;
}
- (MKMapRect)boundingMapRect
{
return [_overlay boundingMapRect];
}
- (CLLocationCoordinate2D)coordinate
{
return [_overlay coordinate];
}
-(BOOL)intersectsMapRect:(MKMapRect)mapRect
{
return [_overlay intersectsMapRect:mapRect];
}
#end
and in the map controller I instantiate two overlays with different name, then in the MKMapViewDelegate I can identify which overlay I want to draw and do something like:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id < MKOverlay >)overlay
{
NamedOverlay *namedOverlay = (NamedOverlay *) overlay;
MKPolyline *polyline = namedOverlay.overlay;
if ([namedOverlay.name isEqualToString:#"top"]) {
MKPolylineView *view1 = [[MKPolylineView alloc] initWithOverlay:polyline];
view1.strokeColor = [UIColor whiteColor];
view1.lineWidth = 25.0;
return view1;
} else {
MKPolylineView *view1 = [[MKPolylineView alloc] initWithOverlay:polyline];
view1.strokeColor = [UIColor blueColor];
view1.lineWidth = 15.0;
return view1;
}
}
I know that this may not match the pure approach you want, but why not using MKPolygon instead of a MKPolyLine ?
Create a MKPolygon instance that represents a kind of corridor around your route, and then , when you create the MKPolygonView that corresponds to the MKPolygon/corridor you've created, set the properties of the MKPolygonView to get a different fill color and strokeColor
myPolygonView.lineWidth=3;
myPolygonView.fillColor=[UIColor blueColor];
myPolygonView.strokeColor=[UIColor darkGrayColor];
I didn't try it myself but this should work. Only drawback is that when you zoom in / out, the 'width' of the 'route' will change.... :/

My vector sprite renders in different locations in simulator and device

I'm implementing a subclass of UIView that displays a gauge dial with a sprite for the indicator. It has angle property that I can vary to make the needle point to different angles. It works, but on the same values for the position of the needle make it show up in different locations on the phone and the simulator. It's an iPhone 4, so I'm sure the double resolution thing is behind this, but I don't know what to do about it. I tried setting the UIView's layer's contentScaleFactor but that fails. I thought UIView got the resolution thing for free. Any suggestions?
I should note that the NSLog statements report 150 for both .frame.size. dimensions, in both the simulator and the device.
Here's the .m file
UPDATE: In the simulator, I found how to set the hardware to iPhone 4, and it looks just like the device now, both are scaling and positioning the sprite at half size.
UPDATE 2: I made a workaround. I set the .scale of my sprite equal to the UIView's contentScaleFactor and then use it to dived the UIView in half if it's a lo-res screen and the full width if it's hi-res. I still don't see why this is necessary, as I should be working in points now, not pixels. It must have something to do with the custom drawing code in the Sprite or VectorSprite classes.
I'd still appreciate some feedback if anyone has some...
#import "GaugeView.h"
#implementation GaugeView
#synthesize needle;
#define kVectorArtCount 4
static CGFloat kVectorArt[] = {
3,-4,
2,55,
-2,55,
-3,-4
};
- (id)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
needle = [VectorSprite withPoints:kVectorArt count:kVectorArtCount];
needle.scale = (float)self.contentScaleFactor; // returns 1 for lo-res, 2 for hi-res
NSLog(#" needle.scale = %1.1f", needle.scale);
needle.x = self.frame.size.width / ((float)(-self.contentScaleFactor) + 3.0); // divisor = 1 for hi-res, 2 for lo-res
NSLog(#" needle.x = %1.1f", needle.x);
needle.y = self.frame.size.height / ((float)(-self.contentScaleFactor) + 3.0);
NSLog(#" needle.y = %1.1f", needle.y);
needle.r = 0.0;
needle.g = 0.0;
needle.b = 0.0;
needle.alpha = 1.0; }
}
self.backgroundColor = [UIColor clearColor];
return self;
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGAffineTransform t0 = CGContextGetCTM(context);
t0 = CGAffineTransformInvert(t0);
CGContextConcatCTM(context, t0);
[needle updateBox];
[needle draw: context];
}
- (void)dealloc {
[needle release];
[super dealloc];
}
#end
I believe the answer is that iOS takes care of the resolution scaling automatically in drawRect methods, but in custom drawing code, you have to do it yourself.
In my example, I used the UIView's contentsScaleFactor to scale my sprite. In the future, in my custom draw method (not shown) I'll query [UIScreen mainScreen] scale and scale accordingly there.

Effect like Interface Builder connection lines on iPhone

Hi I'd like little bit of help on something. In my app, I have a UITableView which is populated with custom cells that represent images. (i.e. selecting a row displays a picture in an image view).
What I would like to do, is have an icon in the custom cell that I could drag to one of a series of image views. Similar to the way you can drag a line in IB to set connections. Once the user releases their finger I will have it check what part of the screen they released it and if it is one one of these rects that represent the picture frames, it will populate the picture frame with the image and the line will disappear.
I have never drawn lines in my app before so thats not something I know how to do (so im just looking for a link to a tutorial or class definition) and second, what problems will I have since the start point of the line is in a UITableViewCell and the end point is in the main view?
I have actually done this before, so I can give you the exact code :D
This is only the drawing part, you implement the touch events (in a separate class, otherwise remove the self.userInteractionEnabled = NO; in .m file
The BOOL dragged tells the LineView if the line should be drawn.
LineView.h
#import <UIKit/UIKit.h>
#interface LineView : UIView
{
CGPoint start;
CGPoint end;
BOOL dragged;
}
#property CGPoint start, end;
#property BOOL dragged;
#end
LineView.m
#import "LineView.h"
#implementation LineView
#synthesize start, end, dragged;
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame: frame])
{
self.userInteractionEnabled = NO;
self.backgroundColor = [UIColor clearColor];
}
return self;
}
#pragma mark Setters
-(void) setStart: (CGPoint) s
{
start = s;
[self setNeedsDisplay];
}
-(void) setEnd: (CGPoint) e
{
end = e;
[self setNeedsDisplay];
}
#define LINE_COLOR [UIColor redColor]
#define CIRCLE_COLOR [UIColor redColor]
#define LINE_WIDTH 5
#define CIRCLE_RADIUS 10
- (void)drawRect:(CGRect)rect
{
CGContextRef c = UIGraphicsGetCurrentContext();
if(dragged) {
[LINE_COLOR setStroke];
CGContextMoveToPoint(c, start.x, start.y);
CGContextAddLineToPoint(c, end.x, end.y);
CGContextSetLineWidth(c, LINE_WIDTH);
CGContextClosePath(c);
CGContextStrokePath(c);
[CIRCLE_COLOR setFill];
CGContextAddArc(c, start.x, start.y, CIRCLE_RADIUS, 0, M_PI*2, YES);
CGContextClosePath(c);
CGContextFillPath(c);
CGContextAddArc(c, end.x, end.y, CIRCLE_RADIUS, 0, M_PI*2, YES);
CGContextClosePath(c);
CGContextFillPath(c);
}
}
- (void)dealloc
{
[super dealloc];
}
#end

Drawing Pixels - Objective-C/Cocoa

I am trying to draw individual pixels in xcode to be outputted to the iphone. I do not know any OpenGL or Quartz coding but I do know a bit about Core Graphics. I was thinking about drawing small rectangles with width and height of one, but do not know how to implement this into code and how to get this to show in the view. Any help is greatly appreciated.
For a custom UIView subclass that allows plotting dots of a fixed size and color:
// Make a UIView subclass
#interface PlotView : UIView
#property (nonatomic) CGContextRef context;
#property (nonatomic) CGLayerRef drawingLayer; // this is the drawing surface
- (void) plotPoint:(CGPoint) point; //public method for plotting
- (void) clear; // erases drawing surface
#end
// implementation
#define kDrawingColor ([UIColor yellowColor].CGColor)
#define kLineWeight (1.5)
#implementation PlotView
#synthesize context = _context, drawingLayer = _drawingLayer;
- (id) initPlotViewWithFrame:(CGRect) frame; {
self = [super initWithFrame:frame];
if (self) {
// this is total boilerplate, it rarely needs to change
self.backgroundColor = [UIColor clearColor];
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGFloat width = frame.size.width;
CGFloat height = frame.size.height;
size_t bitsPerComponent = 8;
size_t bytesPerRow = (4 * width);
self.context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorspace, kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorspace);
CGSize size = frame.size;
self.drawingLayer = CGLayerCreateWithContext(self.context, size, NULL);
}
return self;
}
// override drawRect to put drawing surface onto screen
// you don't actually call this directly, the system will call it
- (void) drawRect:(CGRect) rect; {
// this creates a new blank image, then gets the surface you've drawn on, and stamps it down
// at some point, the hardware will render this onto the screen
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGImageRef image = CGBitmapContextCreateImage(self.context);
CGRect bounds = [self bounds];
CGContextDrawImage(currentContext, bounds, image);
CGImageRelease(image);
CGContextDrawLayerInRect(currentContext, bounds, self.drawingLayer);
}
// simulate plotting dots by drawing a very short line with rounded ends
// if you need to draw some other kind of shape, study this part, along with the docs
- (void) plotPoint:(CGPoint) point; {
CGContextRef layerContext = CGLayerGetContext(self.drawingLayer); // get ready to draw on your drawing surface
// prepare to draw
CGContextSetLineWidth(layerContext, kLineWeight);
CGContextSetLineCap(layerContext, kCGLineCapRound);
CGContextSetStrokeColorWithColor(layerContext, kDrawingColor);
// draw onto surface by building a path, then stroking it
CGContextBeginPath(layerContext); // start
CGFloat x = point.x;
CGFloat y = point.y;
CGContextMoveToPoint(layerContext, x, y);
CGContextAddLineToPoint(layerContext, x, y);
CGContextStrokePath(layerContext); // finish
[self setNeedsDisplay]; // this tells system to call drawRect at a time of it's choosing
}
- (void) clear; {
CGContextClearRect(CGLayerGetContext(self.drawingLayer), [self bounds]);
[self setNeedsDisplay];
}
// teardown
- (void) dealloc; {
CGContextRelease(_context);
CGLayerRelease(_drawingLayer);
[super dealloc];
}
If you want to be able to draw pixels that are cumulatively added to some previously drawn pixels, then you will need to create your own bitmap graphics context, backed by your own bitmap memory. You can then set individual pixels in the bitmap memory, or draw short lines or small rectangles in your graphics context. To display your drawing context, first convert it to an CGImageRef. Then you can either draw this image to a subclassed UIView in the view's drawRect, or assign the image to the contents of the UIView's CALayer.
Look up: CGBitmapContextCreate and CGBitmapContextCreateImage in Apple's documentation.
ADDED:
I wrote up a longer explanation of why you might need to do this when drawing pixels in an iOS app, plus some source code snippets, on my blog: http://www.musingpaw.com/2012/04/drawing-in-ios-apps.html
All drawing needs to go into the - (void)drawRect:(CGRect)rect method. [self setNeedsDisplay] flags the code for a redraw. Problem is your redrawing nothing.

How to change iphone CATiledLayer fadeDuration?

I am working on an iphone application that displays tiled maps. I am currently using a CATiledLayer in a UIScrollView :
MyTiledDelegate *delegate=[[MyTiledDelegate alloc] initWithMapLayer:map];
tileLayer = [CATiledLayer layer];
tileLayer.delegate = delegate;
[scrollView.layer addSublayer:tileLayer];
[tileLayer setNeedsDisplay];
I wrote and set my own delegate which implements the draw layer method like so :
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
CGRect rect =CGContextGetClipBoundingBox(ctx);
CGFloat x = fabs(round(rect.origin.x/tileSize));
CGFloat y = fabs(round(rect.origin.y/tileSize));
Tile *tile = [map getTileForMapZoom:z x:x y:y];
CGImageRef img=[tile getRealImage];
CGContextDrawImage(
ctx,
CGRectMake(tile.x*tileSize,tile.y*tileSize, tileSize,tileSize) ,
img);
}//edited for brevity
I am annoyed by the default behavior of the CAtiledLayer to fadein after the tile is drawn. Also, sometimes the fadein is not complete (it stops at 90 or 95% opacity).
How can i change or (preferably) remove the fadein animation ?
I played with the speed and duration properties of my CATiledLayer instance, to no avail. I don't set any animation on the tiledLayer. the [tiledLayer removeAllAnimation] does not change anything either.
Thanks for any pointers.
You should subclass the CATiledLayer and return fadeDuration of 0 to disable fade-in:
#interface FastCATiledLayer : CATiledLayer
#end
#implementation FastCATiledLayer
+(CFTimeInterval)fadeDuration {
return 0.0;
}
#end
I also had the problem with fade in animation not completing, what helped was to set the background color of the view to [UIColor clearColor]
Try subclassing and overriding the +fadeDuration accessor on the layer.
Or in Swift 4:
class CAFastTiledLayer: CATiledLayer {
class func fadeDuration() -> CFTimeInterval {
return 0.0
}
}