Actually we are working on a paint application,
We did paint strokes using bezier path, paths array.. we almost complete the application,
But there are some performance issues to drayong paint strokes from PathsArray which is having alredy drawn Bezierpaths, like paint strokes getting slow after drawing some number of strokes,
So tho avoid this performance issue we used an temperary iamge view behind the sketchImage View,
We drawn recent one stroke on Top Sketch Image view and updated the bottom Temperary iamge view with the PathsArray,
it works fine and imrove performance, but there is an issue in Eraser..
While doing eraser the backGroun temperary iamge view updates only at the end of the stroke,
So we planned to erase the image when eraser selects, how to do this.. any other ways
-(void)updateTempararyBottomLayerImageView
{
TempararyBottomLayerImageView.frame = CGRectMake(TopLayerImageView.frame.origin.x, TopLayerImageView.frame.origin.y, TopLayerImageView.frame.size.width, TopLayerImageView.frame.size.height);
UIGraphicsBeginImageContext(TempararyBottomLayerImageView.bounds.size);
for ( NSDictionary *dict in pathsArray )
{
UIBezierPath *p = (UIBezierPath*)[dict objectForKey:#"path"];
p.lineWidth = [[dict objectForKey:#"size"]floatValue];
if ([[dict objectForKey:#"red"] floatValue] == 0) //When eraser selected
{
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
}
else
{
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeCopy);
[[UIColor colorWithRed:[[dict objectForKey:#"red"]floatValue]
green:[[dict objectForKey:#"green"]floatValue]
blue:[[dict objectForKey:#"blue"]floatValue]
alpha:[[dict objectForKey:#"alpha"]floatValue]] setStroke];
}
[p stroke];
}
[[UIColor colorWithRed:red green:green blue:blue alpha:opacity] setStroke];
if(isEraser) //When eraser selected, change color as clear
{
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
}
else
{
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeCopy);
}
if (!isTouchEnd)
[bzierPath stroke];
TempararyBottomLayerImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
- (void) updateDrawingBoard
{
UIGraphicsBeginImageContext(TopLayerImageView.bounds.size);
if (pathsArray.count > 0)
{
NSDictionary * dict = [pathsArray lastObject];
UIBezierPath *p ;
#try {
p = (UIBezierPath*)[dict objectForKey:#"path"];
}
#catch (NSException *exception)
{
NSLog(#"xxxxxxxxxxxx");
return;
}
p.lineWidth = [[dict objectForKey:#"size"]floatValue];
if ([[dict objectForKey:#"red"] floatValue] == 0) //When eraser selected
{
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
}
else
{
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeCopy);
[[UIColor colorWithRed:[[dict objectForKey:#"red"]floatValue]
green:[[dict objectForKey:#"green"]floatValue]
blue:[[dict objectForKey:#"blue"]floatValue]
alpha:[[dict objectForKey:#"alpha"]floatValue]] setStroke];
}
[p stroke];
[[UIColor colorWithRed:red green:green blue:blue alpha:opacity] setStroke];
if(isEraser) //When eraser selected, change color as clear
{
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
}
else
{
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeCopy);
}
if (!isTouchEnd)
[bzierPath stroke];
self.TopLayerImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
}
# pragma =====UITouch delegates=====
//==============================================
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if( [[event touchesForView:self.TopLayerImageView] count] == 1)
{
CGPoint touchPoint = [[touches anyObject] locationInView:self.TopLayerImageView];
bzierPath = [UIBezierPath bezierPath];
bzierPath.lineCapStyle = kCGLineCapRound;
bzierPath.lineJoinStyle = kCGLineJoinRound;
bzierPath.lineWidth = brushSize;
[bzierPath moveToPoint:touchPoint];
isTouchEnd = NO;
UIBezierPath *tempPath = bzierPath;
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
tempPath,#"path",
[NSNumber numberWithFloat:red], #"red",
[NSNumber numberWithFloat:green], #"green",
[NSNumber numberWithFloat:blue], #"blue",
[NSNumber numberWithFloat:opacity], #"alpha",
[NSNumber numberWithFloat:brushSize], #"size", nil];
if (isEraser)
{
dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
tempPath,#"path",
[NSNumber numberWithFloat:0], #"red",
[NSNumber numberWithFloat:0], #"green",
[NSNumber numberWithFloat:0], #"blue",
[NSNumber numberWithFloat:0], #"alpha",
[NSNumber numberWithFloat:brushSize], #"size", nil];
}
[pathsArray addObject:dict];
[self updateDrawingBoard];
}
//To hide bottom scroll wheel where ever tap in view
UITouch *touch = [[event allTouches] anyObject];
if ([touch view] != bottomScrollWheel)
{
[self minimizeScrollWheel];
}
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if( [[event touchesForView:self.TopLayerImageView] count] == 1)
{
CGPoint touchPoint = [[touches anyObject] locationInView:self.TopLayerImageView];
[bzierPath addLineToPoint:touchPoint];
[bzierPath moveToPoint:touchPoint];
isTouchEnd = NO;
[self updateDrawingBoard];
}
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if( [[event touchesForView:self.TopLayerImageView] count] == 1)
{
// NSLog(#"**********************bzierPath == \r\n %#",bzierPath);
CGPoint touchPoint = [[touches anyObject] locationInView:self.TopLayerImageView];
[bzierPath moveToPoint:touchPoint];
[bzierPath addLineToPoint:touchPoint];
//bzierPath = nil;
isTouchEnd = YES;
[self updateDrawingBoard];
[self updateTempararyBottomLayerImageView];
if (TopLayerImageView.image != nil)
{
[undoArray addObject:TempararyBottomLayerImageView.image];
[redoArray removeAllObjects];
[redoPathsArray removeAllObjects];
}
self.TopLayerImageView.image = nil;
[self EnableDisableUndoRedo];
}
}
# pragma mark =====Settings =====
//==============================
// Slider Position can be changed from left to right or right to left
-(void)sliderPositionChange
{
if (settingsPopoverVC.index == 0)
{
if (iOS7)
[sliderView setFrame:CGRectMake(19, 128, 100, 357)];
else
[sliderView setFrame:CGRectMake(19, 108, 100, 357)];
}
else if(settingsPopoverVC.index == 1)
{
if (iOS7)
[sliderView setFrame:CGRectMake(900, 128, 100, 357)];
else
[sliderView setFrame:CGRectMake(900, 108, 100, 357)];
}
}
This below links are you to get understand
stack over flow - setneedslayout
SetNeedDisplay
Thanks
Related
Please see the attached image. I have created an app in which user can draw with multiple images but when I am using the Clear Color in order to erase the drawing it is showing the black border around the erased path. I have used UIBezierPath for drawing. Is this an issue with UIBezierPath or it can be resolved by any other way? Please let me know if you want code snippet! I will post that here.
Edit: Code has been added! Please ignore the irrelevant code.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if(isFirstTapp)
{
isFirstTapp=NO;
[self.spooleteView removeHandView];
}
isdraw=YES;
mouseSwapped=NO;
UITouch *touch = [touches anyObject];
lastPoint=[touch locationInView:self];
//UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[currentPath moveToPoint:p];
currentPath.flatness = 10.0;
NSMutableDictionary *dict=[[NSMutableDictionary alloc]init];
UIBezierPath *myPath=[[UIBezierPath alloc]init];
myPath.lineCapStyle=kCGLineJoinRound;
[dict setObject:myPath forKey:#"Path"];
colorTag= [[AppHelper userDefaultsForKey:#"ColorTag"]intValue];
self.selectedColor = [brushColor objectAtIndex:colorTag];
if(isErase)
{
myPath.lineWidth=30.0;
self.selectedColor = [UIColor whiteColor];
}
else
{
if([mode isEqualToString:#"morsecode"])
myPath.lineWidth=10.0;
else
myPath.lineWidth=5.0;
}
//[dict setObject:[brushColor objectAtIndex:colorTag] forKey:#"Colors"];
[dict setObject:self.selectedColor forKey:#"Colors"];
[myArr addObject:dict];
currentPath=myPath;
currentPath.flatness = 0.1;
currentPath.lineJoinStyle = kCGLineJoinRound;
currentDict=dict;
[myPath moveToPoint:lastPoint];
[myPath release];
[dict release];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if(!istapped)
{
if([mode isEqualToString:#"morsecode"])
{
mouseSwapped=YES;
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
currentPoint=[mytouch locationInView:self];
[currentPath addLineToPoint:currentPoint];
[self setNeedsDisplay];
}
}
// UITouch *touch = [touches anyObject];
// CGPoint p = [touch locationInView:self];
// [currentPath addLineToPoint:p];
// [self setNeedsDisplay];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if(!istapped)
{
mouseSwapped=YES;
UITouch *objTouch=[[touches allObjects] objectAtIndex:0];
if (!mouseSwapped&&(objTouch.tapCount==1))
{
lastPoint=currentPoint;
}
}
if(isErase)
{
if ([myArr count]>50)
{
[myArr removeAllObjects];
}
}
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[currentPath addLineToPoint:p];
[self drawBitmap]; // (3)
[self setNeedsDisplay];
[currentPath removeAllPoints]; //(4)
}
- (void)drawRect:(CGRect)rect
{
[self.incrementalImage drawInRect:rect]; // (3)
[currentPath stroke];
//[self.incrementalImage drawInRect:rect];
for (NSMutableDictionary *dictionary in myArr)
{
UIBezierPath *_path = [dictionary objectForKey:#"Path"];
self.selectedColor = [dictionary objectForKey:#"Colors"];
[self.selectedColor setStroke];
[_path stroke];
[_path strokeWithBlendMode:kCGBlendModeCopy alpha:1.0];
_path.lineCapStyle = kCGLineJoinRound;
if([mode isEqualToString:#"morsecode"])
{
if (!isErase)
{
const float p[2] = {1,15.9};
[_path setLineDash:p count:2 phase:0.9];
}
else
{
self.selectedColor = [UIColor whiteColor];
}
}
}
}
- (void)drawBitmap // (3)
{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
[self.selectedColor setStroke];
if (!self.incrementalImage) // first draw; paint background white by ...
{
UIBezierPath *rectpath = [UIBezierPath bezierPathWithRect:self.bounds]; // enclosing bitmap by a rectangle defined by another UIBezierPath object
[[UIColor clearColor] setFill];
[rectpath fill]; // filling it with white
}
[self.incrementalImage drawAtPoint:CGPointZero];
[currentPath stroke];
self.incrementalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
I have found the reason of that issue. It was because of the second line of the following function.
- (void)drawRect:(CGRect)rect
{
[self.incrementalImage drawInRect:rect]; // (3)
[currentPath stroke];
i.e., [currentPath stroke];
I have commented that and now there is no black border around the path.
I hope this will help other.
I have a UIView called myView
#interface MyView : UIView { UIImage *myPic;
NSMutableArray *myDrawing; }
#end
and i updated this array using touches began, and in the touches moved and touches ended by adding values.
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// myDrawing = [[NSMutableArray alloc] initWithCapacity:4];
[myDrawing addObject:[[NSMutableArray alloc] initWithCapacity:4]];
CGPoint curPoint = [[touches anyObject] locationInView:self];
[[myDrawing lastObject] addObject:[NSNumber numberWithFloat:curPoint.x]];
[[myDrawing lastObject] addObject:[NSNumber numberWithFloat:curPoint.y]];
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint curPoint = [[touches anyObject] locationInView:self];
[[myDrawing lastObject] addObject:[NSNumber numberWithFloat:curPoint.x]];
[[myDrawing lastObject] addObject:[NSNumber numberWithFloat:curPoint.y]];
[self setNeedsDisplay];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint curPoint = [[touches anyObject] locationInView:self];
[[myDrawing lastObject] addObject:[NSNumber numberWithFloat:curPoint.x]];
[[myDrawing lastObject] addObject:[NSNumber numberWithFloat:curPoint.y]];
[self setNeedsDisplay];
}
then i use the draw rect method to update the lines
- (void)drawRect:(CGRect)rect
{
// Drawing code
float newHeight;
float newWidth;
if (!myDrawing) {
myDrawing = [[NSMutableArray alloc] initWithCapacity:0];
}
CGContextRef ctx = UIGraphicsGetCurrentContext();
if (myPic != NULL)
{
float ratio = myPic.size.height/460;
if (myPic.size.width/320 > ratio)
{
ratio = myPic.size.width/320;
}
newHeight = myPic.size.height/ratio;
newWidth = myPic.size.width/ratio;
[myPic drawInRect:CGRectMake(0,0,newWidth,newHeight)];
}
if ([myDrawing count] > 0) {
CGContextSetLineWidth(ctx, 3);
NSData *colorData = [[NSUserDefaults standardUserDefaults] objectForKey:#"SwatchColor"];
UIColor *color;
if (colorData!=nil) {
// If the data object is valid, unarchive the color we've stored in it.
color = (UIColor *)[NSKeyedUnarchiver unarchiveObjectWithData:colorData];
}
if (color)
{
CGContextSetStrokeColorWithColor(ctx, color.CGColor);
}
else
{
CGContextSetStrokeColorWithColor(ctx,[UIColor blackColor].CGColor);
}
for (int i = 0 ; i < [myDrawing count] ; i++) {
NSArray *thisArray = [myDrawing objectAtIndex:i];
if ([thisArray count] > 2)
{
float thisX = [[thisArray objectAtIndex:0] floatValue];
float thisY = [[thisArray objectAtIndex:1] floatValue];
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, thisX, thisY);
for (int j = 2; j < [thisArray count] ; j+=2)
{
thisX = [[thisArray objectAtIndex:j] floatValue];
thisY = [[thisArray objectAtIndex:j+1] floatValue];
CGContextAddLineToPoint(ctx, thisX,thisY);
//CGContextAddQuadCurveToPoint(ctx, 150, 10, thisX, thisY);
// CGContextAddCurveToPoint(ctx , 0, 50, 300, 250, thisX, thisY);
}
CGContextStrokePath(ctx);
}
}
}
}
i have a color picker in my code to change the colors and i want to draw lines of different colors each time by selecting colors, but as of now since i am constructing lines and rendering it when i choose initially red and draw a line, and then later select blue and draw a line , now the older line also change to blue instead of red, However i want the red to remain as red, and blue as such can any one help?
You are always drawing over the drawn lines. Clear myDrawing to make it store only the points that need to be processed, keep already processed points at another array if you need undo/optimized-save functionality.
I want to draw a random design on uiview just like we are drawing on paint brush I want where user touch on screen start drawing. like if he wants to write ok in it than he can draw it.if he want to make duck or ball so he can make it. plz help me.
Try this.
In this, You will have to use self.view.frame wherever i have myPic.Frame.
In Header File:
#import <Foundation/Foundation.h>
#interface DrawView : UIView {
UIImage *myPic;
NSMutableArray *myDrawing;
}
-(void)drawPic:(UIImage *)thisPic;
-(void)cancelDrawing;
#end
and in Implementation File:
#import "DrawView.h"
#implementation DrawView
-(void)drawPic:(UIImage *)thisPic {
myPic = thisPic;
[myPic retain];
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
float newHeight;
float newWidth;
if (!myDrawing) {
myDrawing = [[NSMutableArray alloc] initWithCapacity:0];
}
CGContextRef ctx = UIGraphicsGetCurrentContext();
if (myPic != NULL) {
float ratio = myPic.size.height/460;
if (myPic.size.width/320 > ratio) {
ratio = myPic.size.width/320;
}
newHeight = myPic.size.height/ratio;
newWidth = myPic.size.width/ratio;
[myPic drawInRect:CGRectMake(0,0,newWidth,newHeight)];
}
if ([myDrawing count] > 0) {
CGContextSetLineWidth(ctx, 5);
for (int i = 0 ; i < [myDrawing count] ; i++) {
NSArray *thisArray = [myDrawing objectAtIndex:i];
if ([thisArray count] > 2) {
float thisX = [[thisArray objectAtIndex:0] floatValue];
float thisY = [[thisArray objectAtIndex:1] floatValue];
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, thisX, thisY);
for (int j = 2; j < [thisArray count] ; j+=2) {
thisX = [[thisArray objectAtIndex:j] floatValue];
thisY = [[thisArray objectAtIndex:j+1] floatValue];
CGContextAddLineToPoint(ctx, thisX,thisY);
}
CGContextStrokePath(ctx);
}
}
}
}
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[myDrawing addObject:[[NSMutableArray alloc] initWithCapacity:4]];
CGPoint curPoint = [[touches anyObject] locationInView:self];
[[myDrawing lastObject] addObject:[NSNumber numberWithFloat:curPoint.x]];
[[myDrawing lastObject] addObject:[NSNumber numberWithFloat:curPoint.y]];
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint curPoint = [[touches anyObject] locationInView:self];
[[myDrawing lastObject] addObject:[NSNumber numberWithFloat:curPoint.x]];
[[myDrawing lastObject] addObject:[NSNumber numberWithFloat:curPoint.y]];
[self setNeedsDisplay];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint curPoint = [[touches anyObject] locationInView:self];
[[myDrawing lastObject] addObject:[NSNumber numberWithFloat:curPoint.x]];
[[myDrawing lastObject] addObject:[NSNumber numberWithFloat:curPoint.y]];
[self setNeedsDisplay];
}
-(void)cancelDrawing {
[myDrawing removeAllObjects];
[self setNeedsDisplay];
}
- (void)dealloc {
[super dealloc];
[myPic release];
[myDrawing release];
}
#end
you will have to make your UIView a subClass of the above. And in your controller class.m file, you'll have to add and call these two methods.
I hope this works.
-(IBAction)clear {
[self.view cancelDrawing];
}
-(IBAction)saveDrawing
{
UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *finishedPic = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(finishedPic, self, #selector(exitProg:didFinishSavingWithError:contextInfo:), nil);
}
I'm trying to implement a very simple drawing view in my app. This is only a small part of my app but it's turning into a real hassle. This is what I have so far, but all it's displaying right now is morse code like dots and lines.
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *savePath = [NSString stringWithFormat:#"%#/notePadImage.jpg",docsPath];
NSData *data = [NSData dataWithContentsOfFile:savePath];
UIImage *image = [UIImage imageWithData:data];
if (image == nil) {
NSString *pathToBlank = [[NSBundle mainBundle]pathForResource:#"blankNotePadPage" ofType:#"png"];
NSData *data = [NSData dataWithContentsOfFile:pathToBlank];
image = [UIImage imageWithData:data];
}
arrayOfTouches = [[NSMutableArray alloc] initWithCapacity:10];
self.drawImage.image = image;
mouseMoved = 0;
[self.view bringSubviewToFront:closeButton];
[self.view bringSubviewToFront:clearButton];
self.timer = [NSTimer scheduledTimerWithTimeInterval:.02 target:self selector:#selector(drawIt) userInfo:nil repeats:YES];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
[arrayOfTouches addObject:touch];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
[arrayOfTouches addObject:touch];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
}
-(void) drawIt {
NSMutableArray *tempArray = [NSMutableArray arrayWithArray:arrayOfTouches];
[arrayOfTouches removeAllObjects];
if ([tempArray count]>1) {
[arrayOfTouches removeAllObjects];
CGPoint point1 = [[tempArray objectAtIndex:0] previousLocationInView:self.view];;
CGPoint point2;
CGPoint point3;
for (int i = 0; i < [tempArray count]-1;i = i+1) {
UIGraphicsBeginImageContext(self.view.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 3.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0.0, 0.0, 0.0, 1.0);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), point1.x, point1.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), point2.x, point2.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.view bringSubviewToFront:closeButton];
[self.view bringSubviewToFront:clearButton];
point1 = point2;
}
}
}
One of my apps also needed some simple drawing. Here is a slightly modified version of it. It works basically like hotpaw2 describes. I created a "canvas" view that handles all the drawing and I just add it wherever it's needed.
The speed is fine for my purposes.
CanvasView.h:
#interface CanvasView : UIView {
NSMutableArray *points;
}
#property (nonatomic, retain) NSMutableArray *points;
#end
CanvasView.m:
#import "CanvasView.h"
#implementation CanvasView
#synthesize points;
- (id) initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor blueColor];
}
return self;
}
-(void)drawRect:(CGRect)rect
{
if (self.points.count == 0)
return;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0); //white
CGContextSetLineWidth(context, 1.0);
CGPoint firstPoint = [[self.points objectAtIndex:0] CGPointValue];
CGContextBeginPath(context);
CGContextMoveToPoint(context, firstPoint.x, firstPoint.y);
int i = 1;
while (i < self.points.count)
{
CGPoint nextPoint = [[self.points objectAtIndex:i] CGPointValue];
if (nextPoint.x < 0 && nextPoint.y < 0)
{
CGContextDrawPath(context, kCGPathStroke);
if (i < (self.points.count-1))
{
CGContextBeginPath(context);
CGPoint nextPoint2 = [[self.points objectAtIndex:i+1] CGPointValue];
CGContextMoveToPoint(context, nextPoint2.x, nextPoint2.y);
i = i + 2;
}
else
i++;
}
else
{
CGContextAddLineToPoint(context, nextPoint.x, nextPoint.y);
i++;
}
}
CGContextDrawPath(context, kCGPathStroke);
}
-(void)dealloc
{
[points release];
[super dealloc];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event touchesForView:self] anyObject];
CGPoint location = [touch locationInView:self];
if (self.points == nil)
{
NSMutableArray *newPoints = [[NSMutableArray alloc] init];
self.points = newPoints;
[newPoints release];
}
[self.points addObject:[NSValue valueWithCGPoint:(location)]];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event touchesForView:self] anyObject];
CGPoint location = [touch locationInView:self];
[self.points addObject:[NSValue valueWithCGPoint:(location)]];
[self setNeedsDisplay];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event touchesForView:self] anyObject];
CGPoint location = [touch locationInView:self];
[self.points addObject:[NSValue valueWithCGPoint:(location)]];
CGPoint endPoint = CGPointMake(-99, -99); //"end of path" indicator
[self.points addObject:[NSValue valueWithCGPoint:(endPoint)]];
[self setNeedsDisplay];
}
#end
Adding the canvasView where it's needed:
CanvasView *cv = [[CanvasView alloc] initWithFrame:CGRectMake(0, 0, 320, 640)];
[self.view addSubview:cv];
[cv release];
Don't draw while handling touches. It will slow down the touch handler so much you might get the connect-the-dots-effect you are seeing.
Save the touch coordinates in an array and plan to draw them later.
Look at some simple animation tutorials for how to draw in a drawRect based on a setNeedsDisplay called by an animation UITimer or CADisplayLink. Draw all your line segments there at a more suitable rate.
Hello I want to make an application in which I have to display pdffile on iphone screen, which has a functionality of zooming. I have multiple pages of pdffile, but the problem is i can get display only one page.
Here is the code :
/*myView.m*/
#implementation MyView
- (void)configureTiledLayer {
if([global getfirsttime] == 0)
{
[global fetchpageCtr : 1];
[global fetchfirsttime:1];
}
zoom = 1.0f;
tiledLayer = [CATiledLayer layer];
TiledDelegate *delegate = [[TiledDelegate alloc] init];
tiledLayer.delegate = delegate;
// get tiledLayer size
CGRect pageRect = CGPDFPageGetBoxRect(delegate.map, kCGPDFCropBox);
int w = pageRect.size.width;
int h = pageRect.size.height;
NSLog(#"height==%d,weight=%d",h,w);
// get level count
int levels = 1;
while (w > 1 && h > 1) {
levels++;
w = w >> 1;
h = h >> 1;
}
NSLog(#"Layer create");
// set the levels of detail
tiledLayer.levelsOfDetail = levels;
// set the bias for how many 'zoom in' levels there are
tiledLayer.levelsOfDetailBias = 5;
// setup the size and position of the tiled layer
CGFloat width = CGRectGetWidth(pageRect);
CGFloat height = CGRectGetHeight(pageRect);
tiledLayer.bounds = CGRectMake(0.0f, 0.0f, width, height);
CGFloat x = width * tiledLayer.anchorPoint.x;
CGFloat y = -height * tiledLayer.anchorPoint.y;
tiledLayer.position = CGPointMake(x * zoom, y * zoom);
tiledLayer.transform = CATransform3DMakeScale(zoom, zoom, 1.0f);
// transform the super layer so things draw 'right side up'
CATransform3D superTransform = CATransform3DMakeTranslation(0.0f, self.bounds.size.height, 0.0f);
self.layer.transform = CATransform3DScale(superTransform, 1.0, -1.0f, 1.0f);
[self.layer addSublayer:tiledLayer];
[tiledLayer setNeedsDisplay];
moving = NO;
NSLog(#"in layer");
}
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor blueColor];
[self configureTiledLayer];
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self configureTiledLayer];
}
return self;
}
- (void)setZoom:(CGFloat)newZoom {
zoom = newZoom;
tiledLayer.transform = CATransform3DMakeScale(zoom, zoom, 1.0f);
}
- (void)zoomIn {
[self setZoom:zoom * 2.0f];
}
- (void)zoomOut {
[self setZoom:zoom * 0.5f];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(touches.count == 1) {
previousPoint = [[touches anyObject] locationInView:self];
} else if(touches.count == 2) {
// pinch zoom
pinchZoom = YES;
NSArray *touches = [event.allTouches allObjects];
CGPoint pointOne = [[touches objectAtIndex:0] locationInView:self];
CGPoint pointTwo = [[touches objectAtIndex:1] locationInView:self];
previousDistance = sqrt(pow(pointOne.x - pointTwo.x, 2.0f) +
pow(pointOne.y - pointTwo.y, 2.0f));
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(touches.count == 1) {
CGPoint currentPoint = [[touches anyObject] locationInView:self];
CGPoint delta = CGPointMake(currentPoint.x - previousPoint.x, currentPoint.y - previousPoint.y);
tiledLayer.position = CGPointMake(tiledLayer.position.x + delta.x * zoom,
tiledLayer.position.y + delta.y * zoom);
previousPoint = currentPoint;
moving = YES;
} else if(touches.count == 2) {
// pinch zoom stuff
NSArray *touches = [event.allTouches allObjects];
CGPoint pointOne = [[touches objectAtIndex:0] locationInView:self];
CGPoint pointTwo = [[touches objectAtIndex:1] locationInView:self];
CGFloat distance = sqrt(pow(pointOne.x - pointTwo.x, 2.0f) +
pow(pointOne.y - pointTwo.y, 2.0f));
CGFloat newZoom = fabs(zoom + (distance - previousDistance) / previousDistance);
[self setZoom:newZoom];
previousDistance = distance;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(!moving) {
if(touches.count == 1) {
// realy should recenter on a click but I'm being lazy
if([[touches anyObject] tapCount] == 2) {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self zoomOut];
} else {
[self performSelector:#selector(zoomIn) withObject:nil afterDelay:0.25];
}
}
} else {
moving = NO;
}
}
- (void)dealloc {
[tiledLayer release];
[super dealloc];
}
/*TiledDelegate.m*/
#implementation TiledDelegate
- (CGPDFDocumentRef)sfMuni {
if(NULL == sfMuni) {
NSString *path = [[NSBundle mainBundle] pathForResource:#"Hunting-TrappingSynopsis_0910" ofType:#"pdf"];
NSURL *docURL = [NSURL fileURLWithPath:path];
sfMuni = CGPDFDocumentCreateWithURL((CFURLRef)docURL);
}
return sfMuni;
}
- (CGPDFPageRef)map {
int temppageno = [global getpageCtr];
NSLog(#"page ctr ==%d ",temppageno);
if(NULL == map) {
map = CGPDFDocumentGetPage(self.sfMuni, temppageno);
}
return map;
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
NSLog(#"\ndrawLayer:inContext:");
NSLog(#"ctm = %#", NSStringFromCGAffineTransform(CGContextGetCTM(ctx)));
NSLog(#"box = %#\n", NSStringFromCGRect(CGContextGetClipBoundingBox(ctx)));
CGContextDrawPDFPage(ctx, self.map);
}
- (void)dealloc {
CGPDFPageRelease(map);
CGPDFDocumentRelease(sfMuni);
[super dealloc];
}
/*TiledLayerAppDelegate*/
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
/*TiledLayerViewController*/
- (void)viewDidLoad
{
l_pagectr = 2;
UIButton *btn1 = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
btn1.frame = CGRectMake(0,0,70,50);
[btn1 setBackgroundColor: [UIColor whiteColor]];
btn1.exclusiveTouch = YES;
[btn1 setTitle:#"Next" forState:UIControlStateNormal];
[btn1 setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[btn1 addTarget:self action:#selector(NextPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn1];
}
-(IBAction)NextPressed : (id)sender
{
[global fetchpageCtr : l_pagectr++];
MyView *myview1=[[MyView alloc]init];
}
#end
Here when I pressed Next button it will have to display me the nest page but will display me the same page.I also get the page referance in "CGPDFPageRef" incremented but not displayed.
Plz help me for this.
Why don't you just use UIWebView? It renders PDF and offers zoom controls. No need to reinvent the wheel. Here is documentation for UIWebView.