I am looking to have something which is marked in red, as marker but don't know whether it can be done or not.
It looks like marking using a calligraphy pen.
Trying out the following code as answered by "Andrey", I get the following output with spaces while drawing.
Brush image can be found here used is
Making further changes to the code, I found still I am not able to get the expected result.
Here I am trying to fill the path which exists between the current point and the next point.
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGFloat w=5.0;
CGFloat h=2.0;
CGContextRef context=UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
for(int i=0;i<self.positions.count;i++){
CGPoint point=[[self.positions objectAtIndex:i] CGPointValue];
CGFloat x=point.x-(w/2.0);
CGFloat y=point.y-(h/2.0);
CGContextFillRect(context, CGRectMake(x, y, w, h));
if(i+1<self.positions.count){
CGPoint nextPoint=[[self.positions objectAtIndex:(i+1)] CGPointValue];
CGMutablePathRef myPath=CGPathCreateMutable();
CGPathMoveToPoint(myPath, NULL, x, y);
CGFloat x1=nextPoint.x-(w/2.0);
CGFloat y1=nextPoint.y-(h/2.0);
CGPathAddLineToPoint(myPath, NULL, x1, y1);
CGPathAddLineToPoint(myPath, NULL, w, y1);
CGPathAddLineToPoint(myPath, NULL, w, y);
CGPathCloseSubpath(myPath);
CGContextFillPath(context);
}
}
}
Those picture can be done via drawing an image in UIView subclass with overridden drawRect: message. You also need to add some kind of touch handler for catching touches. So here is some code:
#interface DrawingView ()
#property (nonatomic, strong) NSMutableArray *positions;
#end
#implementation DrawingView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self awakeFromNib];
}
return self;
}
- (void)awakeFromNib
{
self.positions = [[NSMutableArray alloc] init];
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
UIImage *brushImage = [UIImage imageNamed:#"brush.png"];
for (NSValue *object in self.positions) {
CGPoint point = [object CGPointValue];
[brushImage drawAtPoint:CGPointMake(point.x - brushImage.size.width / 2.0, point.y - brushImage.size.height / 2.0)];
}
}
- (void)addPoint:(CGPoint)point
{
[self.positions addObject:[NSValue valueWithCGPoint:point]];
[self setNeedsDisplay];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
[self addPoint:[touch locationInView:self]];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
[self addPoint:[touch locationInView:self]];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
[self addPoint:[touch locationInView:self]];
}
#end
You only need to create a proper brush.png and place those view anywhere you want.
Related
I have a custom UIView , inside which I have to show an image as a background and draw freely on to it using touches and drags, referred to this question iOS: iOS: Drawing line is appearing behind subviews,
but doesn't seems to be working,it draws image but doesn't draw anything over it , please help
Following custom UiView draws free hand
- (id)initWithCoder:(NSCoder *)aDecoder // (1)
{
if (self = [super initWithCoder:aDecoder])
{
[self setMultipleTouchEnabled:NO]; // (2)
[self setBackgroundColor:[UIColor whiteColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (void)drawRect:(CGRect)rect // (5)
{
[[UIColor blackColor] setStroke];
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path moveToPoint:p];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p]; // (4)
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesMoved:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
#end
This is the main UIView inside I have added UiImage and OverlayView
- (id)initWithCoder:(NSCoder *)aDecoder // (1)
{
if (self = [super initWithCoder:aDecoder])
{
CGRect rect1 = self.frame;
rect1.origin.x = 0;
rect1.origin.y = 0;
UIImageView *dot =[[UIImageView alloc] initWithFrame:CGRectMake(rect1.origin.x,rect1.origin.y,rect1.size.width,rect1.size.height)];
dot.image = [UIImage imageNamed:#"miter"];
[self addSubview:dot];
OverlayView *overlay = [[OverlayView alloc] initWithFrame:CGRectMake(rect1.origin.x,rect1.origin.y,rect1.size.width,rect1.size.height)];
[overlay setBackgroundColor:[UIColor clearColor]];
[self addSubview:overlay];
[self bringSubviewToFront:overlay];
}
return self;
}
#end
Set the userInteractionEnabled property to YES. The default for UIViews is YES, but for UIImageViews is NO so you have to explicitly turn it on.
I'm making an app for iPhone in Xcode, and It requires a box to follow my finger only on the X axis. I couldn't find any solution online to this, and my coding knowledge isn't that great.
I've been trying to use touchesBegan and touchesMoved.
Could someone please write me up some code please?
First you need the UIGestureRecognizerDelegate on your ViewController.h file:
#interface ViewController : UIViewController <UIGestureRecognizerDelegate>
#end
Then you declare an UIImageView on your ViewController.m, like so, with a BOOL to track if a touch event is occurring inside your UIImageView:
#interface ViewController () {
UIImageView *ballImage;
BOOL touchStarted;
}
Then you initialize the UIImageView on viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
UIImage *image = [UIImage imageNamed:#"ball.png"];
ballImage = [[UIImageView alloc]initWithImage:image];
[ballImage setFrame:CGRectMake(self.view.center.x, self.view.center.y, ballImage.frame.size.width, ballImage.frame.size.height)];
[self.view addSubview:ballImage];
}
After that you can start doing your modifications to what's best for you using these methods:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touch_point = [touch locationInView:ballImage];
if ([ballImage pointInside:touch_point withEvent:event])
{
touchStarted = YES;
} else {
touchStarted = NO;
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([touches count]==1 && touchStarted) {
UITouch *touch = [touches anyObject];
CGPoint p0 = [touch previousLocationInView:ballImage];
CGPoint p1 = [touch locationInView:ballImage];
CGPoint center = ballImage.center;
center.x += p1.x - p0.x;
// if you need to move only on the x axis
// comment the following line:
center.y += p1.y - p0.y;
ballImage.center = center;
NSLog(#"moving UIImageView...");
}
}
I followed a tutorial online to draw in a subclassed UIView. The tutorial showed a UIView with a white background, I fixed this by simply changing the super's bg color. The problem is, when touches end, the background does not remain clear. I have no idea. I simply tried setting the fill color to [uicolor clearcolor]; unsuccessfully. Here is the code I am using:
#implementation CachedLIView
{
UIBezierPath *path;
UIImage *incrementalImage; // (1)
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self setMultipleTouchEnabled:NO];
[self setBackgroundColor:[UIColor whiteColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
[incrementalImage drawInRect:rect]; // (3)
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path moveToPoint:p];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p];
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event // (2)
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p];
[self drawBitmap]; // (3)
[self setNeedsDisplay];
[path removeAllPoints]; //(4)
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
- (void)drawBitmap // (3)
{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
[[UIColor blackColor] setStroke];
if (!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
}
[incrementalImage drawAtPoint:CGPointZero];
[path stroke];
incrementalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
Put the following in your view -(id) initWithFrame method:
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
}
return self;
That way your view subclass will have a transparent background = no black when drawing using bezier, CGContext… methods or anything else.
Can someone here please show me how to draw a single dot using UIBezierpath? I am able to draw a line using the UIBezierpath but if I remove my finger and put it back and then remove nothing get drawn on the screen.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[pPath moveToPoint:p];
[pPath stroke];
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
[pPath stroke];
}
Your path doesn't include any line or curve segments to be stroked.
Try this instead:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint p = [touches.anyObject locationInView:self];
static CGFloat const kRadius = 3;
CGRect rect = CGRectMake(p.x - kRadius, p.y - kRadius, 2 * kRadius, 2 * kRadius);
pPath = [UIBezierPath bezierPathWithOvalInRect:rect];
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
[[UIColor blackColor] setFill];
[pPath fill];
}
I used this code:
-(void)handleTap:(UITapGestureRecognizer*)singleTap {
//draw dot on screen
CGPoint tapPoint = [singleTap locationInView:self];
[bezierPath_ moveToPoint:tapPoint];
[bezierPath_ addLineToPoint:tapPoint];
[self setNeedsDisplay];
}
I need to draw a line with dashes at regular gaps by sliding finger on the iPhone screen. I have written the following code for that and it is drawing the dashed line but the gaps are not regular and thus it is not looking good. I have also provided a link to the video showing the issue Please help!
Link to video:
https://dl.dropbox.com/u/56721867/Screen%20Recording%207.mov
CGPoint mid1 = midPoint__5(previousPoint1, previousPoint2);
CGPoint mid2 = midPoint__5(currentPoint, previousPoint1);
CGContextRef context = UIGraphicsGetCurrentContext();
[self.layer renderInContext:context];
self.lineColor=[arrObj objectAtIndex:colorTag];
CGContextMoveToPoint(context, mid1.x, mid1.y);
CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
CGContextSetLineCap(context, kCGLineCapRound);//use for making circle ,rect or line effect===============
CGFloat dashArray[] = {2,8,2,8};
CGContextSetLineDash(context,2,dashArray, 4);
CGContextSetLineWidth(context, self.lineWidth);
self.lineColor=[arrObj objectAtIndex:[AppHelper userDefaultsForKey:#"ColorTag"].intValue];//color tag on button click
CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);
CGContextSetAlpha(context, 1.0);
CGBlendMode blendStyle=isErase?kCGBlendModeClear:kCGBlendModeNormal;
CGContextSetBlendMode(context,blendStyle);
CGContextStrokePath(context);
[super drawRect:rect];
It's rather unclear from the code, but based on the video and a hunch I'd guess a single swipe with the finger ends up as several line segments. This means that the dashing starts from scratch several times, creating the effect in the video. You probably need a NSMutableArray where you put all those currentPoint into, and then draw them all as one line every time you redraw.
This question has more information on how to draw lines in this way: iPhone Core Graphics thicker dashed line for subview
Here is the code to do what you are trying, it uses Quartz instead of CoreGraphics. You can use either Quartz or CoreGraphics.
//
// TestView.h
//
//
// Created by Syed Arsalan Pervez on 2/18/13.
// Copyright (c) 2013 SAPLogix. All rights reserved.
//
#import <UIKit/UIKit.h>
#interface TestView : UIView
{
NSMutableArray *_points;
}
#end
//
// TestView.m
//
//
// Created by Syed Arsalan Pervez on 2/18/13.
// Copyright (c) 2013 SAPLogix. All rights reserved.
//
#import "TestView.h"
#interface UserPoint : NSObject
{
BOOL _start;
BOOL _end;
CGPoint _point;
}
#property (assign, nonatomic) BOOL start;
#property (assign, nonatomic) BOOL end;
#property (assign, nonatomic) CGPoint point;
#end
#implementation UserPoint
#end
#implementation TestView
- (id)init
{
self = [super init];
if (self)
{
_points = [NSMutableArray new];
}
return self;
}
- (UserPoint *)addPoint:(CGPoint)point
{
UserPoint *_userPoint = [UserPoint new];
_userPoint.point = point;
[_points addObject:_userPoint];
[_userPoint release];
return [_points lastObject];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
UserPoint *_userPoint = [self addPoint:[touch locationInView:self]];
_userPoint.start = YES;
_userPoint = nil;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
UserPoint *_userPoint = [self addPoint:[touch locationInView:self]];
_userPoint = nil;
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
UserPoint *_userPoint = [self addPoint:[touch locationInView:self]];
_userPoint.end = YES;
_userPoint = nil;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
[[UIColor blackColor] setStroke];
[[UIColor blackColor] setFill];
UIBezierPath *path = [UIBezierPath bezierPath];
[path setLineWidth:15];
CGFloat dash[] = {15,20};
[path setLineDash:dash count:2 phase:0];
[path setLineCapStyle:kCGLineCapRound];
CGPoint lastPoint;
for (UserPoint *_point in _points)
{
if (_point.start || _point.end)
[path moveToPoint:_point.point];
else
{
[path addQuadCurveToPoint:_point.point controlPoint:lastPoint];
}
lastPoint = _point.point;
}
[path stroke];
}
- (void)dealloc
{
[_points release];
[super dealloc];
}
#end