UIBezierPath drawing issues - ios

I created a class called myBezierPaths, with two member variables of type UIBezierPath and UIColor, and then I tried to use object of this class to draw the bezierpath, but I am not getting bezierpath, below is my code
//Bezierpath.h
#interface BezierPath : NSObject
{
UIBezierPath *m_bezierPath;
UIColor *m_pathColor;
}
#property (nonatomic, strong) UIBezierPath *bezierPath;
#property (nonatomic, strong) UIColor *pathColor;
#end
//drawingView.m
- (void)drawRect:(CGRect)rect
{
for (BezierPath *pathobj in m_pathArray)
{
[pathobj.pathColor setStroke];
[pathobj.pathColor setFill];
[pathobj.bezierPath strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
*self.myPath = [[BezierPath alloc] init];*
myPath.bezierPath.lineWidth = 5;
myPath.bezierPath.lineCapStyle = kCGLineCapRound;
myPath.bezierPath.flatness = 0.0;
myPath.bezierPath.lineJoinStyle = kCGLineJoinRound;
myPath.bezierPath.miterLimit = 200.0;
self.myPath.pathColor = [UIColor redColor];
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[myPath.bezierPath moveToPoint:[mytouch locationInView:self]];
[m_pathArray addObject:self.myPath];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[self.myPath.bezierpath addLineToPoint:[mytouch locationInView:self]];
[self setNeedsDisplay];
}
hope I am clear with my explanation, waiting for reply
Regards
Ranjit

First, in your drawRect you don't actually fill the shape. You do stroke it (basically draw a border) but you need to call [pathObj.bezierPath fill]; to actually fill the shape with a color.
There's a lot of code you're not showing, so it's hard to tell where the problem might be. I would create a breakpoint in the drawRect method to determine if all of the variables are there. For example, you show (what I assume) to be an array: m_pathArray. Are you sure it's initialized? Are you sure the properties of BezierPath are working correctly? I notice that on touchesBegan you set the pathColor but only access/read the bezierPath variable. Has that been initialized? Where do you create the 'm_bezierPath' instance variable? Do you create it in the BezierPath init method or lazily instantiate it on access?
So those are a few things you could check.

You don't show your touchesMoved event handler , but i would expect something like this. Otherwise all you are doing is creating a set of paths of length zero.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[self.myPath.bezeirPath lineToPoint:[mytouch locationInView:self]];
}

#Ranjit need some help I've same problem ...
I want to change the colors of UIbezierpath.
but wen i select the color my previous path also automatically gets colored with new color.
In My first Image i draw a line and selected color is red.
but when i select yellow color to draw another line the previous line also change to yellow color.(image no 2)

Related

Undo and Redo functionality in drawing application ios

I am working on a drawing app and I am able to draw with my finger touch. Now I am trying to implement clear, undo, and redo functionality.
In my view controller I have two IBAction methods for "clearAll" and "Undo". I have created a custom class called drawing.h and .m where I have written functions for handling touch events. Below are my functions.
The problem is undo and redo work but the last color select in all line in drawn in undo and redo.
I Have Mistake In Touch End Method In Array Last Array Not Remove on last object
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UIGraphicsBeginImageContext(self.tempimage.bounds.size);
[self.tempimage.layer renderInContext:UIGraphicsGetCurrentContext()];
rawImage = UIGraphicsGetImageFromCurrentImageContext();
[self.tempimage.image drawInRect:CGRectMake(0, 0, tempimage.frame.size.width, tempimage.frame.size.height) blendMode:blendmode alpha:opacity];
UIGraphicsEndImageContext();
#if PUSHTOFILE
lineIndex++;
[self performSelectorInBackground:#selector(writeFilesBG)
withObject:nil];
#else
NSDictionary *lineInfo = [NSDictionary dictionaryWithObjectsAndKeys:rawImage, #"IMAGE",nil];
[pointsArray addObject:lineInfo];
UIBezierPath *_path=[pointsArray lastObject];
[_stack addObject:_path];
[pointsArray removeLastObject];
[self.tempimage setNeedsDisplay];
#endif
}

UIButton like behavior on custom subclass of UIView

I have subclass of UIView
#interface TBL_CardView : UIView
It it internally have UIImageView for card image.
I need to handle touching of card in following way:
When I touch (and still hold) TBL_CardView I need to set orange borderColor.
If I am stil on top of TBL_CardView I need to set red borderColor.
If I am not on top of TBL_CardView, I have moved my finger, because I want to cancel touch, then borderColor is removed.
I know how to handle setting of borderColor:
self.layer.borderColor = [UIColor redColor].CGColor;
self.layer.borderWidth = 3.0f;
But I do not know what is the easiest way to implement thin button like behavior ?
Should I make it just as button, or use UIResponder or something else ?
What are pros and cons for each case ?
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"%s", __PRETTY_FUNCTION__);
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView: self];
BOOL isInside = [self pointInside: touchLocation withEvent: event];
if (isInside)
{
//if logic
NSLog(#"INSIDE");
}
else
{
//else logic
NSLog(#"OUTSIDE");
}
}
You could check out this post for information on how to implement button-like behaviour and respond to touches on a UIView or a subclass.
Touch Event on UIView
What I usually do myself, however, is put a transparent (clear color and no title, not alpha = 0.0) UIButton on top of my UIView matching it s size and responding to the events I need instead. Check if it fits your needs.

How to write sketch app on iOS

I'm trying to create sketch app that can draw shapes/path by finger.
What I've done so far is create UIBezierPath when touch start, and draw the path while finger moves.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
CGPoint locationInDrawRect = [mytouch locationInView:self.drawingView];
[self.drawingView.currentPath addLineToPoint:locationInDrawRect];
[self.drawingView setNeedsDisplay];
}
when touch is done, save it to an array of UIBezierPath.
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.drawingView.pathsArray addObject:self.drawingView.currentPath]; //add latest current path to pathArray
[self.drawingView clearCurrentPath]; //clear current path for next line
[self.drawingView setNeedsDisplay];
}
in drawRect, draw current path and paths in array using for loop
- (void)drawRect:(CGRect)rect
{
if(!self.currentPath.empty){
[self.currentPath stroke];
}
for (UIBezierPath *path in self.pathsArray){
[path stroke];
}
}
This works for couple of paths objects, but it gets slow when the array holds more than 5 paths.
I tried to limit the area to render using setNeedsDisplayInRect: method.
[self.drawingView setNeedsDisplayInRect:rectToUpdateDraw];
and render path in array only when rect size is full canvas size, which is when touch is ended.
But this draws weird shape line and also gets slow when there are many objects in the array.
I don't know how I can solve this problem and need some help.
Thank you!

UIBezierPath not drawing a smooth curve

I am using UIBezierPath for drawing, and I have written the code on touch events and its working fine, but my curves are not smooth, when I am move my finger around and draw some curve, they are not smooth.
- (void)drawRect:(CGRect)rect
{
[[UIColor redColor] setStroke];
for (UIBezierPath *_path in pathArray)
[_path strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];
}
#pragma mark - Touch Methods
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
myPath=[[UIBezierPath alloc]init];
myPath.lineWidth=5;
myPath.lineCapStyle = kCGLineCapRound;
myPath.flatness = 0.0;
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[myPath moveToPoint:[mytouch locationInView:self]];
[pathArray addObject:myPath];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[myPath addLineToPoint:[mytouch locationInView:self]];
[self setNeedsDisplay];
}
Here is the image
In the above image if you see the letters a and d you will see that the curve is not smooth. What should I do, to get a smooth curve?
Well you are using addLinePoint so obviously you get lines, not curves. You would be interested in other methods to give you smooth curves: addCurveToPoint or addQuadCurveToPoint. But as you can see from the API, besides the points you are actually drawing with your finger, you need also control points which do not come free with the drawing. Even Photoshop is asking you to move them around when doing curvature. In other words, making your hand drawing smooth "automagically" involves quite some mathematics. Google "smoothing hand drawn" and you will get at least these points to start with
Smoothing a hand-drawn free shape
Smoothing a hand-drawn curve
It is really not iOS specific at all.
Just use this line. it will solve your problem myPath.miterLimit=-10;.
Change the value if you need to any thing it takes float value

UIView bringSubviewToFront: does *not* bring view to front

I am implementing a simple iOS solitaire game that allows the user to drag the cards around in the usual way. The cards are represented with the UIView subclass CardView. All the card view's are siblings which are subviews of SolitaireView. The following snippet tries to "bring a card to the front" so that it is above all the other views as it is being dragged:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if (touch.view.tag == CARD_TAG) {
CardView *cardView = (CardView*) touch.view;
...
[self bringSubviewToFront:cardView];
...
}
}
Unfortunately, the card's z-order remains unchanged during the drag. In the images below, I am dragging the King. Notice how it is correctly on top the Nine in the left image, but is incorrectly under the Two (under the entire stack actually) in the right image:
I also tried alter the layer.zPosition property as well to no avail.
How can I bring the card view to the front during the drag? I am mystified.
Confirmed. bringSubviewToFront: causes layoutSubview to be invoked. Since my version of layoutSubviews sets the z-orders on all the views, this was undoing the z-order I was setting in the touchesBegan:withEvent code above. Apple should mention this side effect in the bringSubviewToFront documentation.
Instead of using a UIView subclass, I created a CALayer subclass named CardLayer. I handle the touch in my KlondikeView subclass as listed below. topZPosition is an instance var that tracks the highest zPosition of all cards. Note that modifying the zPosition is usually animated -- I turn this off in the code below:
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
CGPoint hitTestPoint = [self.layer convertPoint:touchPoint
toLayer:self.layer.superlayer];
CALayer *layer = [self.layer hitTest:hitTestPoint];
if (layer == nil) return;
if ([layer.name isEqual:#"card"]) {
CardLayer *cardLayer = (CardLayer*) layer;
Card *card = cardLayer.card;
if ([self.solitaire isCardFaceUp:card]) {
//...
[CATransaction begin]; // disable animation of z change
[CATransaction setValue:(id)kCFBooleanTrue
forKey:kCATransactionDisableActions];
cardLayer.zPosition = topZPosition++; // bring to highest z
// ... if card fan, bring whole fan to top
[CATransaction commit];
//...
}
// ...
}
}

Resources