iPad app - Drawing objects on the screen one at a time - ios

What is the best way to go about making an app that does the following: the user taps at locations, and on the locations, a square is drawn. There is no way to erase the squares. You simply tap wherever you want and squares of a predefined size are drawn.
I was thinking to make a custom UIView and override the drawRect method by keeping a list of all the (x,y) locations of the squares and then calling [customView setNeedsDisplay] and drawing all the squares everytime a new square is drawn.
Is there a better way?
In Java, I would use an offscreen image, draw the square onto the offscreen image, and then draw the image onto the screen on every repaint() call. But, is this good to do for iPad? If so, what is code that will let me initialize a UIImage and draw a square onto it?

You could just add an empty UIView, which you only set the background color on, and then add it to the view you are tapping.
Example of adding a yellow square in the viewcontroller's viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *square = [[UIView alloc] initWithFrame:CGRectMake(30, 30, 30, 30)];
square.backgroundColor = [UIColor yellowColor];
[self.view addSubview:square];
[square release];
}
Note: You can also give each square an id in its tag and later retrieve them with viewWithTag: from their superview (i.e. self.view from the controller)

Related

Drawing geometry (circle or rect) on zoomable CGPDF document using QuartzCore

I am trying to draw on touches (mouse tap) on the pdf document generated by CGContextRef from my resource bundle. I have started from Apple zoomingpdfviewer
where I have a UIScrollView and Pdf is generated by CGContextDrawPDFPage(context, pdfPage);
I am have also added a CATiledLayer on the scrollView and it is drawn each time in layoutSubviews - when UIScrollView is zoomed. I am confused a bit that should I draw the mouse points on scrollView CGContext or TileLayers.
--> Moving ahead, I wanted to add a rect/ circle/point on the pdf document where the user taps. Just to start with I am : CGContextMoveToPoint(context, 5,5);
CGContextSetLineWidth(context, 12.0f);
CGContextSetStrokeColorWithColor(context, [[UIColor redColor] CGColor]);
CGContextAddRect(context, CGRectMake(50, 50, 20, 50));
CGContextAddLineToPoint(context, 25, 5);
CGContextStrokePath(context);
drawing it to the current context. Similarly I plan to draw when user taps in. But when I zoom and TitledLayer is redrawn to match the zoom content, these drawn rect/ circle disappears. If I am not wrong then CATiledLayer is being drawn above the currentContext rects/ circle . The end functionality is quite similar to the Maps App where the Tile Layers are added but the dropped points are located exactly on the same location even after the map is zoomed. Quite lost after seeing many such posts as drawing on scrollView , pdf iOS viewer. Does anyone know how can I draw geomtry (rect/points) on the pdf document and keep their location exactly same even if PdfView Zoom in and out? Should I convert a pdf into image or any other way to do this?
After searching and trying some stuff on CoreAnimation,CALayer I came up with:
If you want to add anything on zoomable PDF viewer,
1) Add a UITapGestureRecognizer for handling single touch
UITapGestureRecognizer *singleTapGestureRecognizer=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTapOnScrollView:)];
singleTapGestureRecognizer.numberOfTapsRequired=1;
singleTapGestureRecognizer.delegate=self;
[self addGestureRecognizer:singleTapGestureRecognizer];
[singleTapGestureRecognizer requireGestureRecognizerToFail:doubleTapGestureRecognizer]; // added if you have a UITapGestureRecognizer for double taps
2) Simply add the new views (circle,rect any geometry) as layers on the Tiled PDF view
[self.titledPdfView.layer addSublayer:view1.layer];
where you can subclass your geometry views to draw with CGContext currentContext. This automatically reposition the layers when pdf is zoomed in/out
I was trying to add the views by drawing context on drawLayer:(CALayer*)layer inContext:(CGContextRef)context of titledPdfView class which gave a positional error
You can also implement a TouchesMoved, TouchesEnded method if you want to draw a resizable TextView, arow, Line.
I hope this helps for someone who needs to draw customize objects on Apple's ZoomingPDFViewer.
Further to add (if anyone arrives here), replace the Apple code of TiledPdfView *titledPDFView = [[TiledPdfView alloc] initWithFrame:pageRect scale:pdfScale];
[titledPDFView setPage:pdfPage];
[self addSubview:titledPdfView];
[titledPDFView setNeedsDisplay];
[self bringSubviewToFront:titledPdfView];
self.titledPdfView = titledPDFView;
With :
titledPdfView = [[TiledPdfView alloc] initWithFrame:pageRect scale:pdfScale];
[titledPdfView setPage:pdfPage];
[self addSubview:titledPdfView];
[self bringSubviewToFront:titledPdfView];
I dont know why have they added a view like that (member object to class object) which restricts the -(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context being before zooming. I have tried with setNeedsDisplay with no effect then replaced n it works. Esp. this is annoying if you want to draw annots on your TileLayer and nothing appears. Hope this helps!!

How to make animation from popoverview to backview?

when I select a image, I want an animation from the picker to the mainView(which is the blur area), anybody have a good idea?
Basic Idea:
Create a new temp-ImageView with the selected Image.
add this to the window at the exact position of the original-ImageView.
remove original-ImageView (if you want so)
Move/scale the temp-ImageView over the desired position on the screen.
Add the final-ImageView to the desired position in the blue area.
Remove temp-ImageView.
You will have to do some calculations with - (CGRect)convertRect:(CGRect)rect fromView:(UIView *)view
Not sure, but the Picker may lay in a different window than your blue area.
CGRect destRect; // position in window, height/width need to be flipped, so the size is correct after rotation
// add a ContainerView so rotation-handling is easier, esp. durring moving/scaling
UIView *containerView = [[UIView alloc] initWithFrame:destRect];
// set the frame of the imageView, origin 0/0 so rotation is easy to handle, height/width flipp back
UIImageView *tempImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, destRect.size.height, destRect.size.width)];
// flip layer center as well
[tempImageView setCenter:CGPointMake(camView.center.y, camView.center.x)];
// add the rotation
[tempImageView setTransform:CGAffineTransformMakeRotation(M_PI_2)];
[tempImageView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];

How to cut out or disable a section of a UIView?

I have a custom subclass of UIView, LineDrawingView. I have this as a #property in my main view controller and I add it to the view. This works fine - it creates a transparent view on top of the current view and uses UIBezierPath, touchesBegan, touchesMoved etc so you can draw all over the view by dragging your finger around.
I need to make that drawing view "L" shaped, so I can have an area in the bottom left corner where various controls are located. I can think of no way to make the drawing view "L" shaped, except maybe to add two separate rectangular drawing views, but this doesn't work because touches are interrupted when you drag your finger from one rectangle into the other. The other solution I've come up with is to add another view on top of the drawing view. This view should prevent drawing and I could also locate my controls within it so they are still usable while drawing is enabled.
I tried creating a UIView and adding it as a subview of the drawing view. I gave it a tint so I could check it was present and in the right place. I expected this to prevent drawing within the area of the new UIView, but drawing continues all over the area of the LineDrawingView. I also tried inserting the new UIView at index 2, with the LineDrawingView inserted at index 1. It still didn't affect the drawing.
self.drawView = [[LineDrawingView alloc] initWithFrame:CGRectMake(0, 50, 768, 905)];
[self.drawView setBackgroundColor:[UIColor clearColor]];
// not effective in preventing drawing!!
UIView *controlView = [[UIView alloc] initWithFrame:CGRectMake(0, 530, 310, 575)];
[controlView setUserInteractionEnabled:NO];
[controlView setBackgroundColor:[UIColor grayColor]];
[controlView setAlpha:0.3];
[self.view addSubview:drawView];
[self.drawView addSubview:controlView];
I would love to know: how can I either...
Create an "L" shaped drawing view?
OR
Cut out a section of the drawing view so users can interact with what is behind it?
OR
Impose an area on top of the drawing view where I can disable drawing and add my controls?
Here is a tutorial on creating a transparent rounded rectangle UIView, which I think you could modify in a straightforward manner to make it L shaped.
The most important thing to keep in mind that you'll have to implement your own drawRect and I believe also your own hitTest or touches (e.g. event handling like touchedBegan:withEvent:) methods.
I contacted Apple about this. It turns out there is not a way to create an "L" shaped view. The solution was to, in the drawing view, test whether touches are within the forbidden area and, if so, prevent the line from being drawn. My controls have been moved to a separate UIView which is moved to the front when drawing is enabled. This means the controls remain active the whole time and drawing cannot take place within the area of the controls.
if you want to draw an L shape drawing view you can do this by concatenating two lines only .. just like i have created an arrow .. and if you want to stop drawing while you are moving (dragging ) any object .. you just need to apply a check in your touches moved method (like is moved , )..
here is the code to draw an arrow (you can use this as a reference to draw any kind of shape) : How can I draw an arrow using Core Graphics?
code to check if you have hit the path (means your touch point is in the bezier path )
if([[self tapTargetForPath:((UIBezierPath *)[testDict objectForKey:#"path"])] containsPoint:startPoint])// if starting touch is in bezierpath
{
ishitInPath = YES;
isMoving = YES;
}
// to easily detect (or select a bezier path object)
- (UIBezierPath *)tapTargetForPath:(UIBezierPath *)path
{
if (path == nil) {
return nil;
}
CGPathRef tapTargetPath = CGPathCreateCopyByStrokingPath(path.CGPath, NULL, fmaxf(35.0f, path.lineWidth), path.lineCapStyle, path.lineJoinStyle, path.miterLimit);
if (tapTargetPath == NULL) {
return nil;
}
UIBezierPath *tapTarget = [UIBezierPath bezierPathWithCGPath:tapTargetPath];
CGPathRelease(tapTargetPath);
return tapTarget;
}

Overlaying a UIView onto a Cocos layer?

I'm new to iOS and Cocos development.
I currently have a basic app going on in my HelloWorldLayer class. It contains my sprites and touch interaction methods and all is well.
I'm trying to add another "panel" (UIView?) over top of what is currently seen. Eventually this panel will have buttons or other things that will interact with the main canvas.
How can I include another UIView onto the canvas screen? Through my appDelegate, or my HelloWorldLayer?
Thanks
Here is one way to do it. I've used UITextView here but you could use the approach for any descendant of UIView. Bear in mind that UIKit's y coordinate is zero at the top-left of the screen, whereas Cocos2D's is zero at the bottom left.
// Make your subview
UITextView* t = [[UITextView alloc] initWithFrame: CGRectMake(10, 10, 200, 200)];
t.backgroundColor = [UIColor blackColor];
t.textColor = [UIColor whiteColor];
t.text = #"Hello UIKit!";
t.editable = NO;
// Add it as a subview of the Cocos2D view
UIView* cocosView = [[CCDirector sharedDirector] openGLView];
[cocosView addSubview:t];
Alternatively you could try Blue Ether's CCUIViewWrapper, repository here.

Fill In UIView With Multiple Colors iOS

I'd like to fill in a UIView Background with multiple colors. I want to use it as a status bar of sorts, so if 1/2 the necessary steps are completed, the UIView Background will be 1/2 green and 1/2 red. When the user completes more steps (say 2/3), more of the UIView Background turns green (2/3 in this case).
I'm guessing I need to override
-(void) drawREct: (CGRect) rect
I imagine I would get the UIView, figure out how big it is, and divide it into 2 rectangles and then fill in those rectangles.
Another option would be to add 2 UIViews programmatically, but I'm a fan of IB.
Is it possible to divy up a UIView like I want?
Thanks
This is not an IB solution, but you can subclass UIView and override the drawRect method to add a custom gradient. This will allow you to put any number of colors you like and have them transition hard or smoothly. There are many nice tutorials online that should different ways to do this (some more elaborate, some quite simple).
Another option that is fairly simple, is to override drawRect, make the background red, then fill a rectangle that takes up the bottom half of the view. It doesn't allow for fancy or smooth transitions between colors, but it's very easy to implement. For instance, something along these lines should work:
-(void)drawRect:(CGRect)rect {
CGRect upperRect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height * percentDone);
CGRect lowerRect = CGRectMake(rect.origin.x, rect.origin.y + (rect.size.height * percentDone), rect.size.width, rect.size.height *(1-percentDone));
[[UIColor redColor] set];
UIRectFill(upperRect);
[[UIColor greenColor] set];
UIRectFill(lowerRect);
}
Here percentDone is a float (declare, property(nonatomic), synthesize) that you can tie to the user's steps. Then just update the view when the user does something by
splitView.percentDone = .5;
[splitView setNeedsDisplay];
You can smooth this out with animations as well.
An easy way would be to set the background color for your background view to, say, red. Then add another view to that background view and call that the indicator view. Size and position the indicator view to cover the background, and set its background color to green. Connect the indicator view to an outlet in your view controller, and have the view controller adjust its width (or height) as necessary to correspond with the progress of the task at hand.
Another way would be as #PengOne suggests: create a custom view, give it a 'progress' property, and override -drawRect: to draw the contents appropriately. If you're going this route, there's nothing to stop you from getting a little creative. Instead of just filling two rectangles, make the boundary between the two colors a little more interesting. You could add ripples or bubbles that, with an appropriate sound effect, might look like a container filling with liquid. Or you could do a Qix-like animation that slowly fills the screen... ;-)

Resources