https://github.com/acerbetti/ACEDrawingView
above link i use in this project as reference for drawingpad application.
now i want only drawable part from the drawpad.
like here is in drawpad,i draw "a" in it.now i want only area of "a" which i have draw in drawpad.Not whole.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.textView && !self.textView.hidden) {
[self commitAndHideTextEntry];
return;
}
// add the first touch
UITouch *touch = [touches anyObject];
previousPoint1 = [touch previousLocationInView:self];
currentPoint = [touch locationInView:self];
// init the bezier path
self.currentTool = [self toolWithCurrentSettings];
self.currentTool.lineWidth = self.lineWidth;
self.currentTool.lineColor = self.lineColor;
self.currentTool.lineAlpha = self.lineAlpha;
if ([self.currentTool isKindOfClass:[ACEDrawingTextTool class]]) {
[self initializeTextBox: currentPoint];
}
else {
[self.pathArray addObject:self.currentTool];
[self.currentTool setInitialPoint:currentPoint];
}
// call the delegate
if ([self.delegate respondsToSelector:#selector(drawingView:willBeginDrawUsingTool:)]) {
[self.delegate drawingView:self willBeginDrawUsingTool:self.currentTool];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// save all the touches in the path
UITouch *touch = [touches anyObject];
previousPoint2 = previousPoint1;
previousPoint1 = [touch previousLocationInView:self];
currentPoint = [touch locationInView:self];
if ([self.currentTool isKindOfClass:[ACEDrawingPenTool class]]) {
CGRect bounds = [(ACEDrawingPenTool*)self.currentTool addPathPreviousPreviousPoint:previousPoint2 withPreviousPoint:previousPoint1 withCurrentPoint:currentPoint];
CGRect drawBox = bounds;
drawBox.origin.x -= self.lineWidth * 2.0;
drawBox.origin.y -= self.lineWidth * 2.0;
drawBox.size.width += self.lineWidth * 4.0;
drawBox.size.height += self.lineWidth * 4.0;
[self setNeedsDisplayInRect:drawBox];
}
else if ([self.currentTool isKindOfClass:[ACEDrawingTextTool class]]) {
[self resizeTextViewFrame: currentPoint];
}
else {
[self.currentTool moveFromPoint:previousPoint1 toPoint:currentPoint];
[self setNeedsDisplay];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// make sure a point is recorded
[self touchesMoved:touches withEvent:event];
if ([self.currentTool isKindOfClass:[ACEDrawingTextTool class]]) {
[self startTextEntry];
}
else {
[self finishDrawing];
}
}
- (void)finishDrawing
{
// update the image
[self updateCacheImage:NO];
// clear the redo queue
[self.bufferArray removeAllObjects];
// call the delegate
if ([self.delegate respondsToSelector:#selector(drawingView:didEndDrawUsingTool:)]) {
[self.delegate drawingView:self didEndDrawUsingTool:self.currentTool];
}
// clear the current tool
self.currentTool = nil;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
// make sure a point is recorded
[self touchesEnded:touches withEvent:event];
}
#pragma mark - Text Entry
- (void) initializeTextBox: (CGPoint)startingPoint {
if (!self.textView) {
self.textView = [[UITextView alloc] init];
self.textView.delegate = self;
self.textView.returnKeyType = UIReturnKeyDone;
self.textView.autocorrectionType = UITextAutocorrectionTypeNo;
self.textView.backgroundColor = [UIColor clearColor];
self.textView.layer.borderWidth = 1.0f;
self.textView.layer.borderColor = [[UIColor grayColor] CGColor];
self.textView.layer.cornerRadius = 8;
[self.textView setContentInset: UIEdgeInsetsZero];
[self addSubview:self.textView];
}
int calculatedFontSize = self.lineWidth * 3; //3 is an approximate size factor
[self.textView setFont:[UIFont systemFontOfSize:calculatedFontSize]];
self.textView.textColor = self.lineColor;
self.textView.alpha = self.lineAlpha;
int defaultWidth = 200;
int defaultHeight = calculatedFontSize * 2;
int initialYPosition = startingPoint.y - (defaultHeight/2);
CGRect frame = CGRectMake(startingPoint.x, initialYPosition, defaultWidth, defaultHeight);
frame = [self adjustFrameToFitWithinDrawingBounds:frame];
self.textView.frame = frame;
self.textView.text = #"";
self.textView.hidden = NO;
}
- (IBAction)takeScreenshot:(id)sender
{
[redoimage removeAllObjects];
// _previewImageView.frame=CGRectMake(x, y, w,h);
self.previewImageView.image = self.drawingView.image;////herer i can get image from the drawpad.
// self.previewImageView.hidden = NO;
CGSize nsize={100,100};
CGImageRef imageref = [self.drawingView.image CGImage];
UIImage *newimg=[self ManipulateImagePixelData:imageref];
UIImage *newimage=[self scaleImage:newimg toSize:nsize];
// self.previewImageView.image=newimage;
}
////////// this code use geting pixel data from the Uimage/////
-(UIImage *) ManipulateImagePixelData:(CGImageRef) inImage
{
// Create the bitmap context
CGContextRef cgctx = [self CreateARGBBitmapContext:inImage];
if (cgctx == NULL)
{
// error creating context
return NULL;
}
// Get image width, height. We'll use the entire image.
size_t w = CGImageGetWidth(inImage);
size_t h = CGImageGetHeight(inImage);
CGRect rect = {{0,0},{w,h}};
// Draw the image to the bitmap context. Once we draw, the memory
// allocated for the context for rendering will then contain the
// raw image data in the specified color space.
CGContextDrawImage(cgctx, rect, inImage);
CGImageRef imageRef = CGBitmapContextCreateImage(cgctx);
UIImage * finalImage = [UIImage imageWithCGImage:imageRef];
// Now we can get a pointer to the image data associated with the bitmap
// // context.
// void *data = CGBitmapContextGetData (cgctx);
// if (data != NULL)
// {
//
// // **** You have a pointer to the image data ****
//
// // **** Do stuff with the data here ****
//
// }
// When finished, release the context
CGContextRelease(cgctx);
// Free image data memory for the context
// if (data)
// {
// free(data);
// }
return finalImage;
}
- (CGContextRef) CreateARGBBitmapContext:(CGImageRef) inImage
{
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void * bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
// Get image width, height. We'll use the entire image.
size_t pixelsWide = CGImageGetWidth(inImage);
size_t pixelsHigh = CGImageGetHeight(inImage);
// Declare the number of bytes per row. Each pixel in the bitmap in this
// example is represented by 4 bytes; 8 bits each of red, green, blue, and
// alpha.
bitmapBytesPerRow = (pixelsWide * 4);
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
// Use the generic RGB color space.
colorSpace = CGColorSpaceCreateDeviceRGB();
if (colorSpace == NULL)
{
fprintf(stderr, "Error allocating color space\n");
return NULL;
}
// Allocate memory for image data. This is the destination in memory
// where any drawing to the bitmap context will be rendered.
bitmapData = malloc( bitmapByteCount );
if (bitmapData == NULL)
{
fprintf (stderr, "Memory not allocated!");
CGColorSpaceRelease( colorSpace );
return NULL;
}
// Create the bitmap context. We want pre-multiplied ARGB, 8-bits
// per component. Regardless of what the source image format is
// (CMYK, Grayscale, and so on) it will be converted over to the format
// specified here by CGBitmapContextCreate.
context = CGBitmapContextCreate (bitmapData,
pixelsWide,
pixelsHigh,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedFirst);
if (context == NULL)
{
free (bitmapData);
fprintf (stderr, "Context not created!");
}
// Make sure and release colorspace before returning
CGColorSpaceRelease( colorSpace );
return context;
}
Thanks in Advance.
Related
I am building a scratch card experience where I have a background color and a transparent png on the top.
Beneath this, I have the actual image with the content of the scratch card.
I want to combine the background color and the transparent image as one uiimage so that when i scratch this, I am able to see the below content.
I have tried putting a background color to the actual image but when I scratch it, I cannot see the content. Instead the background color starts clearing the transparent image.
I have written the following code for clearing the area of the image after i touch it:
- (UIImage *)addTouches:(NSSet *)touches {
CGSize size = CGSizeMake(self.image.size.width * self.image.scale, self.image.size.height * self.image.scale);
CGContextRef ctx = _imageContext;
CGContextSetFillColorWithColor(ctx,[UIColor clearColor].CGColor);
CGContextSetStrokeColorWithColor(ctx,[UIColor colorWithRed:0 green:0 blue:0 alpha:0].CGColor);
int tempFilled = _tilesFilled;
// process touches
for (UITouch *touch in touches) {
CGContextBeginPath(ctx);
CGPoint touchPoint = [touch locationInView:self];
touchPoint = fromUItoQuartz(touchPoint, self.bounds.size);
touchPoint = scalePoint(touchPoint, self.bounds.size, size);
if(UITouchPhaseBegan == touch.phase){
[self.touchPoints removeAllObjects];
[self.touchPoints addObject:[NSValue valueWithCGPoint:touchPoint]];
[self.touchPoints addObject:[NSValue valueWithCGPoint:touchPoint]];
// on begin, we just draw ellipse
CGRect rect = CGRectMake(touchPoint.x - _radius, touchPoint.y - _radius, _radius*2, _radius*2);
CGContextAddEllipseInRect(ctx, rect);
CGContextFillPath(ctx);
static const FillTileWithPointFunc fillTileFunc = (FillTileWithPointFunc) [self methodForSelector:#selector(fillTileWithPoint:)];
(*fillTileFunc)(self,#selector(fillTileWithPoint:),rect.origin);
} else if (UITouchPhaseMoved == touch.phase) {
[self.touchPoints addObject:[NSValue valueWithCGPoint:touchPoint]];
// then touch moved, we draw superior-width line
CGContextSetStrokeColor(ctx, CGColorGetComponents([UIColor clearColor].CGColor));
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetLineWidth(ctx, 2 * _radius);
// CGContextMoveToPoint(ctx, prevPoint.x, prevPoint.y);
// CGContextAddLineToPoint(ctx, rect.origin.x, rect.origin.y);
while(self.touchPoints.count > 3){
CGPoint bezier[4];
bezier[0] = ((NSValue*)self.touchPoints[1]).CGPointValue;
bezier[3] = ((NSValue*)self.touchPoints[2]).CGPointValue;
CGFloat k = 0.3;
CGFloat len = sqrt(pow(bezier[3].x - bezier[0].x, 2) + pow(bezier[3].y - bezier[0].y, 2));
bezier[1] = ((NSValue*)self.touchPoints[0]).CGPointValue;
bezier[1] = [self normalizeVector:CGPointMake(bezier[0].x - bezier[1].x - (bezier[0].x - bezier[3].x), bezier[0].y - bezier[1].y - (bezier[0].y - bezier[3].y) )];
bezier[1].x *= len * k;
bezier[1].y *= len * k;
bezier[1].x += bezier[0].x;
bezier[1].y += bezier[0].y;
bezier[2] = ((NSValue*)self.touchPoints[3]).CGPointValue;
bezier[2] = [self normalizeVector:CGPointMake( (bezier[3].x - bezier[2].x) - (bezier[3].x - bezier[0].x), (bezier[3].y - bezier[2].y) - (bezier[3].y - bezier[0].y) )];
bezier[2].x *= len * k;
bezier[2].y *= len * k;
bezier[2].x += bezier[3].x;
bezier[2].y += bezier[3].y;
CGContextMoveToPoint(ctx, bezier[0].x, bezier[0].y);
CGContextAddCurveToPoint(ctx, bezier[1].x, bezier[1].y, bezier[2].x, bezier[2].y, bezier[3].x, bezier[3].y);
[self.touchPoints removeObjectAtIndex:0];
}
CGContextStrokePath(ctx);
CGPoint prevPoint = [touch previousLocationInView:self];
prevPoint = fromUItoQuartz(prevPoint, self.bounds.size);
prevPoint = scalePoint(prevPoint, self.bounds.size, size);
static const FillTileWithTwoPointsFunc fillTileFunc = (FillTileWithTwoPointsFunc) [self methodForSelector:#selector(fillTileWithTwoPoints:end:)];
(*fillTileFunc)(self,#selector(fillTileWithTwoPoints:end:),touchPoint, prevPoint);
}
}
// was _tilesFilled changed?
if(tempFilled != _tilesFilled) {
[_delegate mdScratchImageView:self didChangeMaskingProgress:self.maskingProgress];
}
CGImageRef cgImage = CGBitmapContextCreateImage(ctx);
UIImage *image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
return image;
}
/*
* filling tile with one ellipse
*/
-(void)fillTileWithPoint:(CGPoint) point{
size_t x,y;
point.x = MAX( MIN(point.x, self.image.size.width - 1) , 0);
point.y = MAX( MIN(point.y, self.image.size.height - 1), 0);
x = point.x * self.maskedMatrix.max.x / self.image.size.width;
y = point.y * self.maskedMatrix.max.y / self.image.size.height;
char value = [self.maskedMatrix valueForCoordinates:x y:y];
if (!value){
[self.maskedMatrix setValue:1 forCoordinates:x y:y];
_tilesFilled++;
}
}
/*
* filling tile with line
*/
-(void)fillTileWithTwoPoints:(CGPoint)begin end:(CGPoint)end{
CGFloat incrementerForx,incrementerFory;
static const FillTileWithPointFunc fillTileFunc = (FillTileWithPointFunc) [self methodForSelector:#selector(fillTileWithPoint:)];
/* incrementers - about size of a tile */
incrementerForx = (begin.x < end.x ? 1 : -1) * self.image.size.width / _tilesX;
incrementerFory = (begin.y < end.y ? 1 : -1) * self.image.size.height / _tilesY;
// iterate on points between begin and end
CGPoint i = begin;
while(i.x <= MAX(begin.x, end.x) && i.y <= MAX(begin.y, end.y) && i.x >= MIN(begin.x, end.x) && i.y >= MIN(begin.y, end.y)){
(*fillTileFunc)(self,#selector(fillTileWithPoint:),i);
i.x += incrementerForx;
i.y += incrementerFory;
}
(*fillTileFunc)(self,#selector(fillTileWithPoint:),end);
}
What you probably want to do is use a Layer Mask.
When masking a layer, from Apple's docs:
The layer’s alpha channel determines how much of the layer’s content and background shows through. Fully or partially opaque pixels allow the underlying content to show through, but fully transparent pixels block that content.
So, you'd want to use a path to mask your dark circle.
However, to get the "scratch off" effect, you would need to draw the path with a Clear stroke... which you cannot accomplish with a CAShapeLayer.
So, we'll use a custom CALayer subclass.
MyShapeLayer.h
//
// MyShapeLayer.h
//
#import <QuartzCore/QuartzCore.h>
#interface MyShapeLayer : CALayer
#property(nonatomic) CGPathRef path;
#end
MyShapeLayer.m
//
// MyShapeLayer.m
//
#import <UIKit/UIKit.h>
#import "MyShapeLayer.h"
#implementation MyShapeLayer
- (void)drawInContext:(CGContextRef)inContext {
// fill entire layer with solid color
CGContextSetGrayFillColor(inContext, 0.0, 1.0);
CGContextFillRect(inContext, self.bounds);
// we want to "clear" the stroke
CGContextSetStrokeColorWithColor(inContext, [UIColor clearColor].CGColor);
// any color will work, as the mask uses the alpha value
CGContextSetFillColorWithColor(inContext, [UIColor whiteColor].CGColor);
// adjust drawing-line-width as desired
CGContextSetLineWidth(inContext, 60.0);
CGContextSetLineCap(inContext, kCGLineCapRound);
CGContextSetLineJoin(inContext, kCGLineJoinRound);
CGContextAddPath(inContext, self.path);
CGContextSetBlendMode(inContext, kCGBlendModeSourceIn);
CGContextDrawPath(inContext, kCGPathFillStroke);
}
#end
Now we can create a UIView subclass to draw a filled-circle path on a CAShapeLayer and mask it with our MyShapeLayer.
ScratchOffView.h
//
// ScratchOffView.h
//
#import <UIKit/UIKit.h>
#interface ScratchOffView : UIView
#property (assign, readwrite) CGFloat expandedBounds;
#end
ScratchOffView.m
//
// ScratchOffView.m
//
#import "ScratchOffView.h"
#import "MyShapeLayer.h"
#interface ScratchOffView()
#property (strong, nonatomic) UIBezierPath *maskPath;
#property (strong, nonatomic) MyShapeLayer *maskLayer;
#property (strong, nonatomic) CAShapeLayer *scratchOffShapeLayer;
#property (strong, nonatomic) CALayer *scratchOffLayer;
#end
#implementation ScratchOffView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit {
_maskPath = [UIBezierPath new];
_maskLayer = [MyShapeLayer new];
_scratchOffLayer = [CALayer new];
_scratchOffShapeLayer = [CAShapeLayer new];
// Important, otherwise you will get a black rectangle
_maskLayer.opaque = NO;
// add the layer holding the shape to "Scratch Off"
[self.layer addSublayer:_scratchOffShapeLayer];
UIColor *c = [UIColor colorWithRed:50.0 / 255.0 green:150.0 / 255.0 blue:140.0 / 255.0 alpha:1.0];
[_scratchOffShapeLayer setFillColor:c.CGColor];
// set the mask layer
[_scratchOffShapeLayer setMask:_maskLayer];
// default 0.0 == no expanded bounds for touch
_expandedBounds = 0.0;
}
- (void)layoutSubviews {
[super layoutSubviews];
[_maskLayer setFrame:[self bounds]];
[_scratchOffShapeLayer setFrame:[self bounds]];
UIBezierPath *b = [UIBezierPath bezierPathWithOvalInRect:[self bounds]];
[_scratchOffShapeLayer setPath:b.CGPath];
// triggers drawInContext
[_maskLayer setNeedsDisplay];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self];
[_maskPath moveToPoint:currentPoint];
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self];
// add line to our maskPath
[_maskPath addLineToPoint:currentPoint];
// update the mask layer path
[_maskLayer setPath:_maskPath.CGPath];
// triggers drawInContext
[_maskLayer setNeedsDisplay];
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
// accept touch if within expanded bounds
// setting _expandedBounds to a Positive number allows the
// touches to start outside the frame
CGRect r = CGRectInset([self bounds], -_expandedBounds, -_expandedBounds);
return CGRectContainsPoint(r, point);
}
#end
Note that we've added a property: expandedBounds. Since the touches will only register if they begin on this view, we can (virtually) expand the bounds of the view so the user can touch and "drag into the circle."
Here is a complete example implementation. To try and match your question, I use this image (420 x 460 pixels) as the "background" image:
and this image (284 x 284 pixels) as the "image to reveal under the scratch-off circle" (the transparent area is the size we want the circle to be):
ScratchOffTestViewController.h
//
// ScratchOffTestViewController.h
//
#import <UIKit/UIKit.h>
#interface ScratchOffTestViewController : UIViewController
#end
ScratchOffTestViewController.m
//
// ScratchOffTestViewController.m
//
#import "ScratchOffTestViewController.h"
#import "ScratchOffView.h"
#interface ScratchOffTestViewController ()
#property (strong, nonatomic) ScratchOffView *scratchOffView;
#end
#implementation ScratchOffTestViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// create the Scratch Off View
_scratchOffView = [ScratchOffView new];
// load background and giftBox image
UIImage *bkgImage = [UIImage imageNamed:#"backgroundImage"];
UIImage *giftBoxImage = [UIImage imageNamed:#"giftBox"];
if (!bkgImage || !giftBoxImage) {
NSLog(#"Could not load images!!!");
return;
}
UIImageView *bkgImageView = [UIImageView new];
UIImageView *giftImageView = [UIImageView new];
bkgImageView.image = bkgImage;
giftImageView.image = giftBoxImage;
bkgImageView.translatesAutoresizingMaskIntoConstraints = NO;
giftImageView.translatesAutoresizingMaskIntoConstraints = NO;
_scratchOffView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:bkgImageView];
[self.view addSubview:giftImageView];
[self.view addSubview:_scratchOffView];
UILayoutGuide *g = [self.view safeAreaLayoutGuide];
[NSLayoutConstraint activateConstraints:#[
// constrain background image view to background image size
[bkgImageView.widthAnchor constraintEqualToConstant:bkgImage.size.width],
[bkgImageView.heightAnchor constraintEqualToConstant:bkgImage.size.height],
// centered
[bkgImageView.centerXAnchor constraintEqualToAnchor:g.centerXAnchor],
[bkgImageView.centerYAnchor constraintEqualToAnchor:g.centerYAnchor],
// constrain giftBox image view to giftBox image size
[giftImageView.widthAnchor constraintEqualToConstant:giftBoxImage.size.width],
[giftImageView.heightAnchor constraintEqualToConstant:giftBoxImage.size.height],
// centered horizontally, and a little above vertically
[giftImageView.centerXAnchor constraintEqualToAnchor:bkgImageView.centerXAnchor],
[giftImageView.centerYAnchor constraintEqualToAnchor:bkgImageView.centerYAnchor],
// constrain Scratch Off View to giftImageView
[_scratchOffView.widthAnchor constraintEqualToAnchor:giftImageView.widthAnchor],
[_scratchOffView.heightAnchor constraintEqualToAnchor:giftImageView.widthAnchor],
[_scratchOffView.centerXAnchor constraintEqualToAnchor:giftImageView.centerXAnchor],
[_scratchOffView.centerYAnchor constraintEqualToAnchor:giftImageView.centerYAnchor],
]];
// expand the touch bounds of the Scratch Off View by 80-pts
_scratchOffView.expandedBounds = 80.0;
return;
}
#end
On start, we see this:
and after touch-drag a bit on the circle, we see this:
If we continue dragging our touch around, the dark-green circle will eventually be completely gone -- we will have "scratched it off."
To Erase I'm using kCGBlendModeClear.
e.g. I have one erased image, would like to get redraw on erased image when move on CGContext.
Image : https://www.dropbox.com/s/hsltjqa3ue2dpx8/Simulator%20Screen%20Shot%20-%20iPhone%208%20-%202020-05-09%20at%2019.56.15.png?dl=0
My erase code :
-(void)initialize {
self.userInteractionEnabled = YES;
_tilesFilled = 0;
dragVw = [[UIView alloc] init];
dragVw.backgroundColor = [UIColor redColor];
[dragVw removeFromSuperview];
[self addSubview:dragVw];
if (nil == self.image) {
_tilesX = _tilesY = 0;
self.maskedMatrix = nil;
if (NULL != _imageContext) {
CGContextRelease(_imageContext);
_imageContext = NULL;
}
if (NULL != _colorSpace) {
CGColorSpaceRelease(_colorSpace);
_colorSpace = NULL;
}
} else {
self.touchPoints = [NSMutableArray array];
// CGSize size = self.image.size;
// CGSize fromImage = [self aspectScaledImageSizeForImageView:self image:self.image];
CGSize size = /*CGSizeMake(fromImage.width * pinchScale, fromImage.height * pinchScale);*/ CGSizeMake(self.image.size.width * self.image.scale, self.image.size.height * self.image.scale);
// initalize bitmap context
if (NULL == _colorSpace) {
_colorSpace = CGColorSpaceCreateDeviceRGB();
}
if (NULL != _imageContext) {
CGContextRelease(_imageContext);
}
_imageContext = CGBitmapContextCreate(0, size.width, size.height, 8, size.width * 4 , _colorSpace, kCGImageAlphaPremultipliedLast);
CGContextDrawImage(_imageContext, CGRectMake(0, 0, size.width, size.height), self.image.CGImage);
int blendMode = kCGBlendModeClear;
CGContextSetBlendMode(_imageContext, (CGBlendMode) blendMode);
_tilesX = size.width / (2 * __radius);
_tilesY = size.height / (2 * __radius);
offset = 0;
}
-(void)moveTouches:(NSSet*)touches {
// CGSize fromImage = [self aspectScaledImageSizeForImageView:self image:self.image];
CGSize size = /*CGSizeMake(fromImage.width, fromImage.height);*/ CGSizeMake(self.image.size.width * self.image.scale, self.image.size.height * self.image.scale);
CGContextRef ctx = _imageContext;
CGContextSetFillColorWithColor(ctx,[UIColor clearColor].CGColor);
CGContextSetStrokeColorWithColor(ctx,[UIColor colorWithRed:0 green:0 blue:0 alpha:0].CGColor);
// int tempFilled = _tilesFilled;
// process touches
for (UITouch *touch in touches) {
CGContextBeginPath(ctx);
CGPoint touchPoint = CGPointMake([touch locationInView:self].x , [touch locationInView:self].y);
touchPoint = fromUItoQuartz(touchPoint, self.bounds.size);
touchPoint = scalePoint(touchPoint, self.bounds.size, size);
CGPoint dragPoint = [touch locationInView:self];
CGFloat fineRadius = __radius;
NSLog(#"__radius : %zu",__radius);
dispatch_async(dispatch_get_main_queue(), ^{
self->dragVw.frame = CGRectMake(dragPoint.x - fineRadius/2 , dragPoint.y - fineRadius/2, fineRadius, fineRadius);
self->dragVw.layer.cornerRadius = self->dragVw.frame.size.height / 2;
self->dragVw.alpha = 0;
// NSLog(#"DragVw frame : %#",NSStringFromCGRect(self->dragVw.frame));
});
if(UITouchPhaseBegan == touch.phase){
[self.touchPoints removeAllObjects];
[self.touchPoints addObject:[NSValue valueWithCGPoint:touchPoint]];
[self.touchPoints addObject:[NSValue valueWithCGPoint:touchPoint]];
// on begin, we just draw ellipse
CGRect rect = CGRectMake(touchPoint.x - __radius, touchPoint.y - __radius, __radius*2, __radius*2);
CGContextAddEllipseInRect(ctx, rect);
CGContextFillPath(ctx);
static const FillTileWithPointFunc fillTileFunc = (FillTileWithPointFunc) [self methodForSelector:#selector(fillTileWithPoint:)];
(*fillTileFunc)(self,#selector(fillTileWithPoint:),rect.origin);
} else if (UITouchPhaseMoved == touch.phase) {
[self.touchPoints addObject:[NSValue valueWithCGPoint:touchPoint]];
// then touch moved, we draw superior-width line
CGContextSetStrokeColor(ctx, CGColorGetComponents([UIColor clearColor].CGColor));
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetLineWidth(ctx, 2 * __radius);
while(self.touchPoints.count > 3){
CGPoint bezier[4];
bezier[0] = ((NSValue*)self.touchPoints[1]).CGPointValue;
bezier[3] = ((NSValue*)self.touchPoints[2]).CGPointValue;
CGFloat k = 0.3;
CGFloat len = sqrt(pow(bezier[3].x - bezier[0].x, 2) + pow(bezier[3].y - bezier[0].y, 2));
bezier[1] = ((NSValue*)self.touchPoints[0]).CGPointValue;
bezier[1] = [self normalizeVector:CGPointMake(bezier[0].x - bezier[1].x - (bezier[0].x - bezier[3].x), bezier[0].y - bezier[1].y - (bezier[0].y - bezier[3].y) )];
bezier[1].x *= len * k;
bezier[1].y *= len * k;
bezier[1].x += bezier[0].x;
bezier[1].y += bezier[0].y;
bezier[2] = ((NSValue*)self.touchPoints[3]).CGPointValue;
bezier[2] = [self normalizeVector:CGPointMake( (bezier[3].x - bezier[2].x) - (bezier[3].x - bezier[0].x), (bezier[3].y - bezier[2].y) - (bezier[3].y - bezier[0].y) )];
bezier[2].x *= len * k;
bezier[2].y *= len * k;
bezier[2].x += bezier[3].x;
bezier[2].y += bezier[3].y;
CGContextMoveToPoint(ctx, bezier[0].x , bezier[0].y + offset );
CGContextAddCurveToPoint(ctx, bezier[1].x , bezier[1].y + offset, bezier[2].x , bezier[2].y + offset, bezier[3].x , bezier[3].y + offset);
[self.touchPoints removeObjectAtIndex:0];
}
CGContextStrokePath(ctx);
CGPoint prevPoint = [touch previousLocationInView:self];
prevPoint = fromUItoQuartz(prevPoint, self.bounds.size);
prevPoint = scalePoint(prevPoint, self.bounds.size, size);
static const FillTileWithTwoPointsFunc fillTileFunc = (FillTileWithTwoPointsFunc) [self methodForSelector:#selector(fillTileWithTwoPoints:end:)];
(*fillTileFunc)(self,#selector(fillTileWithTwoPoints:end:),touchPoint, prevPoint);
}
}
}
I'm using this class to create a circular slider but i have a problem,how to not allow when user slide from 1% to 100% and vice versa? Please help me to fix this issue. Thanks in advance.
Here is code:
#interface SLCircularSlider()
#property (nonatomic) CGPoint thumbCenterPoint;
#pragma mark - Init and Setup methods
- (void)setup;
#pragma mark - Thumb management methods
- (BOOL)isPointInThumb:(CGPoint)point;
#pragma mark - Drawing methods
- (CGFloat)sliderRadius;
- (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext:(CGContextRef)context;
- (CGPoint)drawCircularTrack:(float)track atPoint:(CGPoint)point withRadius:(CGFloat)radius inContext:(CGContextRef)context;
- (CGPoint)drawPieTrack:(float)track atPoint:(CGPoint)point withRadius:(CGFloat)radius inContext:(CGContextRef)context;
#end
#pragma mark -
#implementation SLCircularSlider
#synthesize value = _value;
- (void)setValue:(float)value {
if (value != _value) {
if (value > self.maximumValue) { value = self.maximumValue; }
if (value < self.minimumValue) { value = self.minimumValue; }
_value = value;
[self setNeedsDisplay];
if (self.isContinuous) {
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
}
#synthesize minimumValue = _minimumValue;
- (void)setMinimumValue:(float)minimumValue {
if (minimumValue != _minimumValue) {
_minimumValue = minimumValue;
if (self.maximumValue < self.minimumValue) { self.maximumValue = self.minimumValue; }
if (self.value < self.minimumValue) { self.value = self.minimumValue; }
}
}
#synthesize maximumValue = _maximumValue;
- (void)setMaximumValue:(float)maximumValue {
if (maximumValue != _maximumValue) {
_maximumValue = maximumValue;
if (self.minimumValue > self.maximumValue) { self.minimumValue = self.maximumValue; }
if (self.value > self.maximumValue) { self.value = self.maximumValue; }
}
}
#synthesize minimumTrackTintColor = _minimumTrackTintColor;
- (void)setMinimumTrackTintColor:(UIColor *)minimumTrackTintColor {
if (![minimumTrackTintColor isEqual:_minimumTrackTintColor]) {
_minimumTrackTintColor = minimumTrackTintColor;
[self setNeedsDisplay];
}
}
#synthesize maximumTrackTintColor = _maximumTrackTintColor;
- (void)setMaximumTrackTintColor:(UIColor *)maximumTrackTintColor {
if (![maximumTrackTintColor isEqual:_maximumTrackTintColor]) {
_maximumTrackTintColor = maximumTrackTintColor;
[self setNeedsDisplay];
}
}
#synthesize thumbTintColor = _thumbTintColor;
- (void)setThumbTintColor:(UIColor *)thumbTintColor {
if (![thumbTintColor isEqual:_thumbTintColor]) {
_thumbTintColor = thumbTintColor;
[self setNeedsDisplay];
}
}
#synthesize continuous = _continuous;
#synthesize sliderStyle = _sliderStyle;
- (void)setSliderStyle:(UICircularSliderStyle)sliderStyle {
if (sliderStyle != _sliderStyle) {
_sliderStyle = sliderStyle;
[self setNeedsDisplay];
}
}
#synthesize thumbCenterPoint = _thumbCenterPoint;
/** #name Init and Setup methods */
#pragma mark - Init and Setup methods
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (void)awakeFromNib {
[self setup];
}
- (void)setup {
self.value = 0.0;
self.minimumValue = 0.0;
self.maximumValue = 1.0;
/*self.minimumTrackTintColor = [UIColor blueColor];
self.maximumTrackTintColor = [UIColor whiteColor];
self.thumbTintColor = [UIColor darkGrayColor];*/
self.minimumTrackTintColor = [UIColor clearColor];
self.maximumTrackTintColor = [UIColor clearColor];
self.thumbTintColor = [UIColor clearColor];
self.continuous = YES;
self.thumbCenterPoint = CGPointZero;
/**
* This tapGesture isn't used yet but will allow to jump to a specific location in the circle
*/
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGestureHappened:)];
[self addGestureRecognizer:tapGestureRecognizer];
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panGestureHappened:)];
panGestureRecognizer.maximumNumberOfTouches = panGestureRecognizer.minimumNumberOfTouches;
[self addGestureRecognizer:panGestureRecognizer];
}
/** #name Drawing methods */
#pragma mark - Drawing methods
#define kLineWidth 5.0
#define kThumbRadius 12.0
- (CGFloat)sliderRadius {
CGFloat radius = MIN(self.bounds.size.width/2, self.bounds.size.height/2);
radius -= MAX(kLineWidth, kThumbRadius);
return radius;
}
- (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext:(CGContextRef)context {
UIGraphicsPushContext(context);
CGContextBeginPath(context);
CGContextMoveToPoint(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y);
CGContextAddArc(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y, kThumbRadius, 0.0, 2*M_PI, NO);
CGContextFillPath(context);
UIGraphicsPopContext();
}
- (CGPoint)drawCircularTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius inContext:(CGContextRef)context {
UIGraphicsPushContext(context);
CGContextBeginPath(context);
float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, 2*M_PI);
CGFloat startAngle = -M_PI_2;
CGFloat endAngle = startAngle + angleFromTrack;
CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, NO);
CGPoint arcEndPoint = CGContextGetPathCurrentPoint(context);
CGContextStrokePath(context);
UIGraphicsPopContext();
return arcEndPoint;
}
- (CGPoint)drawPieTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius inContext:(CGContextRef)context {
UIGraphicsPushContext(context);
float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, 2*M_PI);
CGFloat startAngle = -M_PI_2;
CGFloat endAngle = startAngle + angleFromTrack;
CGContextMoveToPoint(context, center.x, center.y);
CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, NO);
CGPoint arcEndPoint = CGContextGetPathCurrentPoint(context);
CGContextClosePath(context);
CGContextFillPath(context);
UIGraphicsPopContext();
return arcEndPoint;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGPoint middlePoint;
middlePoint.x = self.bounds.origin.x + self.bounds.size.width/2;
middlePoint.y = self.bounds.origin.y + self.bounds.size.height/2;
CGContextSetLineWidth(context, kLineWidth);
CGFloat radius = [self sliderRadius];
switch (self.sliderStyle) {
case UICircularSliderStylePie:
[self.maximumTrackTintColor setFill];
[self drawPieTrack:self.maximumValue atPoint:middlePoint withRadius:radius inContext:context];
[self.minimumTrackTintColor setStroke];
[self drawCircularTrack:self.maximumValue atPoint:middlePoint withRadius:radius inContext:context];
[self.minimumTrackTintColor setFill];
self.thumbCenterPoint = [self drawPieTrack:self.value atPoint:middlePoint withRadius:radius inContext:context];
break;
case UICircularSliderStyleCircle:
default:
[self.maximumTrackTintColor setStroke];
[self drawCircularTrack:self.maximumValue atPoint:middlePoint withRadius:radius inContext:context];
[self.minimumTrackTintColor setStroke];
self.thumbCenterPoint = [self drawCircularTrack:self.value atPoint:middlePoint withRadius:radius inContext:context];
break;
}
[self.thumbTintColor setFill];
[self drawThumbAtPoint:self.thumbCenterPoint inContext:context];
}
/** #name Thumb management methods */
#pragma mark - Thumb management methods
- (BOOL)isPointInThumb:(CGPoint)point {
CGRect thumbTouchRect = CGRectMake(self.thumbCenterPoint.x - kThumbRadius, self.thumbCenterPoint.y - kThumbRadius, kThumbRadius*2, kThumbRadius*2);
return CGRectContainsPoint(thumbTouchRect, point);
}
/** #name UIGestureRecognizer management methods */
#pragma mark - UIGestureRecognizer management methods
- (void)panGestureHappened:(UIPanGestureRecognizer *)panGestureRecognizer {
CGPoint tapLocation = [panGestureRecognizer locationInView:self];
/* UILabel* percentlbl =(UILabel*) [self.superview viewWithTag:10];
NSLog(#"percentlbl frame %f",percentlbl.frame.origin.y
);
if (CGRectContainsPoint(percentlbl.frame, tapLocation)) {
NSLog(#"Tapped label");
}*/
switch (panGestureRecognizer.state) {
case UIGestureRecognizerStateChanged: {
CGFloat radius = [self sliderRadius];
CGPoint sliderCenter = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
CGPoint sliderStartPoint = CGPointMake(sliderCenter.x, sliderCenter.y - radius);
CGFloat angle = angleBetweenThreePoints(sliderCenter, sliderStartPoint, tapLocation);
if (angle < 0) {
angle = -angle;
}
else {
angle = 2*M_PI - angle;
}
self.value = translateValueFromSourceIntervalToDestinationInterval(angle, 0, 2*M_PI, self.minimumValue, self.maximumValue);
break;
}
case UIGestureRecognizerStateEnded:
if (!self.isContinuous) {
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
if ([self isPointInThumb:tapLocation]) {
[self sendActionsForControlEvents:UIControlEventTouchUpInside];
}
else {
[self sendActionsForControlEvents:UIControlEventTouchUpOutside];
}
break;
default:
break;
}
}
- (void)tapGestureHappened:(UITapGestureRecognizer *)tapGestureRecognizer {
if (tapGestureRecognizer.state == UIGestureRecognizerStateEnded) {
CGPoint tapLocation = [tapGestureRecognizer locationInView:self];
if ([self isPointInThumb:tapLocation]) {
}
else {
}
}
}
/** #name Touches Methods */
#pragma mark - Touches Methods
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:self];
if ([self isPointInThumb:touchLocation]) {
[self sendActionsForControlEvents:UIControlEventTouchDown];
}
}
#end
/** #name Utility Functions */
#pragma mark - Utility Functions
float translateValueFromSourceIntervalToDestinationInterval(float sourceValue, float sourceIntervalMinimum, float sourceIntervalMaximum, float destinationIntervalMinimum, float destinationIntervalMaximum) {
float a, b, destinationValue;
a = (destinationIntervalMaximum - destinationIntervalMinimum) / (sourceIntervalMaximum - sourceIntervalMinimum);
b = destinationIntervalMaximum - a*sourceIntervalMaximum;
destinationValue = a*sourceValue + b;
return destinationValue;
}
CGFloat angleBetweenThreePoints(CGPoint centerPoint, CGPoint p1, CGPoint p2) {
CGPoint v1 = CGPointMake(p1.x - centerPoint.x, p1.y - centerPoint.y);
CGPoint v2 = CGPointMake(p2.x - centerPoint.x, p2.y - centerPoint.y);
CGFloat angle = atan2f(v2.x*v1.y - v1.x*v2.y, v1.x*v2.x + v1.y*v2.y);
return angle;
}
What you need to do is add a stage in your gesture recognition where you determine if the user is increasing or decreasing the counter. If decreasing, do not respond to decrease changes once the value drops below zero (probable you want to be able to get to zero, not stop at one despite what the question says?). If increasing, don't respond to increase changes once value gets to 100%. You need to ensure of course that while at the maximum you continue to be responsive if the user changes to decrease the value, and likewise to increasing when at zero.
I am trying to pick the color from a horizontal bar with imageViews by reading their pixel values which is sort of horizontal colour picker
but I am unable to retrieve the proper colour there is some problem in sending the points of the knob because of the hierarchy
- View
- UIImageview (Red)
- UIImageview (Green)
- UIimageview (Blue)
- etc…
Below is the code of it
storyboard:I am trying to pick the color from a horizontal bar with imageViews by reading their pixel values which is sort of horizontal colour picker
but I am unable to retrieve the proper colour there is some problem in sending the points of the knob because of the hierarchy
- View
- UIImageview (Red)
- UIImageview (Green)
- UIimageview (Blue)
- etc…
Below is the code of it
storyboard:
#import "EffViewController.h"
#import "TouchMoveGestureRecognizer.h"
#interface EffViewController ()
#end
#implementation EffViewController
#synthesize colorPicker,sliders,sliderValues,picker;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
for (ColorPickerImageView *colorPickers in _colorPickerArray) {
colorPickers.pickedColorDelegate = self;
}
//colorPicker.pickedColorDelegate = self;
TouchMoveGestureRecognizer* gr = [[TouchMoveGestureRecognizer alloc] init];
[picker addGestureRecognizer: gr];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(getTheColorPickerData:) name:#"getTheColorPickerData" object:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void) pickedColor:(UIColor*)color atPoint:(CGPoint)point{
[picker setHidden:false];
self.view.backgroundColor = color;
// Convert point Positions from colorpickerview to main view
CGPoint pointInViewCoords = [self.view convertPoint:point fromView:colorPicker];
//[picker setCenter:pointInViewCoords];
CGColorRef cgcolor = [color CGColor];
int numComponents = CGColorGetNumberOfComponents(cgcolor);
if (numComponents == 4){
const CGFloat *components = CGColorGetComponents(cgcolor);
for(UISlider *slider in sliders)
[slider setValue: components[[slider tag]-1]*255.0f];
for(UITextField *text in sliderValues){
int intValue = components[[text tag]-1]*255;
[text setText:[NSString stringWithFormat:#"%d",intValue]];
}
}
}
- (void)getTheColorPickerData:(NSNotification*)notification
{
// 1
//UIEvent *event = notification.userInfo[#"events"];
NSSet *touches = notification.userInfo[#"touches"];
UITouch* t = [touches anyObject];
CGPoint Worldpoint = [t locationInView: self.colorPickerViewFrame];
//point.y = picker.frame.origin.y;
NSLog(#"World Points x:%f y:%f",Worldpoint.x,Worldpoint.y);
[picker setHidden:false];
CGPoint point = picker.frame.origin;//[[[event allTouches] anyObject] locationInView:self.view];
point.y = picker.frame.size.height/2;
UIControl *control = picker;
NSLog(#"Points x:%f y:%f",point.x,point.y);
NSLog(#"Color Picker Points x :%f y:%f w:%f h:%f",[colorPicker frame].origin.x,[colorPicker frame].origin.y,[colorPicker frame].size.width,[colorPicker frame].size.height);
for (ColorPickerImageView *colorPickers in _colorPickerArray) {
if (CGRectContainsPoint([colorPickers frame], point)) {
// control.center = point;
//[colorPicker touchesEnded:[event allTouches] withEvent:event];
[colorPicker imageSelectionEnded:point];
}
}
}
TouchMoveGesture
#import "TouchMoveGestureRecognizer.h"
#implementation TouchMoveGestureRecognizer
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* t = [touches anyObject];
_ptOffset = [t locationInView: self.view];
// if(![self isInsideTheBoundary:_ptOffset]){
// return;
// }
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* t = [touches anyObject];
CGPoint pt = [t locationInView: self.view.superview];
float ptYtemp;
// if(![self isInsideTheBoundary:pt]){
// return;
// }
if ((pt.x < 295.0f && pt.x > 25.0f) == NO) {
return;
}
pt.x -= _ptOffset.x;
ptYtemp = pt.y;
pt.y -= _ptOffset.y;
if ((pt.y < 26.0f && pt.y > 24.0f) == NO) {
pt.y=ptYtemp;
}
CGRect r = self.view.frame;
r.origin.x = pt.x;
self.view.frame = r;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
_ptOffset = CGPointMake(0, 0);
[[NSNotificationCenter defaultCenter] postNotificationName:#"getTheColorPickerData"
object:self
userInfo:#{#"touches":touches,
#"events":event}];
}
-(BOOL) isInsideTheBoundary :(CGPoint) location {
BOOL isInsideBoundary = NO;
// NSLog(#"Location x:%f y:%f",location.x,location.y);
if (location.x < 320.0f && location.x > 0.0f) {
if (location.y < 31.0f && location.y > 26.0f) {
return YES;
}
}
return NO;
}
#end
ColorPickerImageView
#import "ColorPickerImageView.h"
#import "EffViewController.h"
#import <CoreGraphics/CoreGraphics.h>
#import <QuartzCore/CoreAnimation.h>
#implementation ColorPickerImageView
#synthesize lastColor;
#synthesize pickedColorDelegate;
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
if (self.hidden==YES) {
//color wheel is hidden, so don't handle this as a color wheel event.
[[self nextResponder] touchesEnded:touches withEvent:event];
return;
}
UITouch* touch = [touches anyObject];
CGPoint point = [touch locationInView:self]; //where image was tapped
}
-(void)imageSelectionEnded:(CGPoint)point {
if (self.hidden==YES) {
//color wheel is hidden, so don't handle this as a color wheel event.
// [[self nextResponder] touchesEnded:touches withEvent:event];
return;
}
self.lastColor = [self getPixelColorAtLocation:point];
NSLog(#"color %#",lastColor);
[pickedColorDelegate pickedColor:(UIColor*)self.lastColor atPoint:point];
}
- (UIColor*) getPixelColorAtLocation:(CGPoint)point {
UIColor* color = nil;
CGImageRef inImage = self.image.CGImage;
// Create off screen bitmap context to draw the image into. Format ARGB is 4 bytes for each pixel: Alpa, Red, Green, Blue
CGContextRef cgctx = [self createARGBBitmapContextFromImage:inImage];
if (cgctx == NULL) { return nil; /* error */ }
size_t w = CGImageGetWidth(inImage);
size_t h = CGImageGetHeight(inImage);
CGRect rect = {{0,0},{w,h}};
/** Extra Added code for Resized Images ****/
float xscale = w / self.frame.size.width;
float yscale = h / self.frame.size.height;
point.x = point.x * xscale;
point.y = point.y * yscale;
/** ****************************************/
/** Extra Code Added for Resolution ***********/
CGFloat x = 1.0;
if ([self.image respondsToSelector:#selector(scale)]) x = self.image.scale;
/*********************************************/
// Draw the image to the bitmap context. Once we draw, the memory
// allocated for the context for rendering will then contain the
// raw image data in the specified color space.
CGContextDrawImage(cgctx, rect, inImage);
// Now we can get a pointer to the image data associated with the bitmap
// context.
unsigned char* data = CGBitmapContextGetData (cgctx);
if (data != NULL) {
//offset locates the pixel in the data from x,y.
//4 for 4 bytes of data per pixel, w is width of one row of data.
// int offset = 4*((w*round(point.y))+round(point.x));
int offset = 4*((w*round(point.y))+round(point.x))*x; //Replacement for Resolution
int alpha = data[offset];
int red = data[offset+1];
int green = data[offset+2];
int blue = data[offset+3];
NSLog(#"offset: %i colors: RGB A %i %i %i %i",offset,red,green,blue,alpha);
color = [UIColor colorWithRed:(red/255.0f) green:(green/255.0f) blue:(blue/255.0f) alpha:(alpha/255.0f)];
}
// When finished, release the context
CGContextRelease(cgctx);
// Free image data memory for the context
if (data) { free(data); }
return color;
}
- (CGContextRef) createARGBBitmapContextFromImage:(CGImageRef) inImage {
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void * bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
// Get image width, height. We'll use the entire image.
size_t pixelsWide = CGImageGetWidth(inImage);
size_t pixelsHigh = CGImageGetHeight(inImage);
// Declare the number of bytes per row. Each pixel in the bitmap in this
// example is represented by 4 bytes; 8 bits each of red, green, blue, and
// alpha.
bitmapBytesPerRow = (pixelsWide * 4);
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
// Use the generic RGB color space.
colorSpace = CGColorSpaceCreateDeviceRGB();
if (colorSpace == NULL)
{
fprintf(stderr, "Error allocating color space\n");
return NULL;
}
// Allocate memory for image data. This is the destination in memory
// where any drawing to the bitmap context will be rendered.
bitmapData = malloc( bitmapByteCount );
if (bitmapData == NULL)
{
fprintf (stderr, "Memory not allocated!");
CGColorSpaceRelease( colorSpace );
return NULL;
}
// Create the bitmap context. We want pre-multiplied ARGB, 8-bits
// per component. Regardless of what the source image format is
// (CMYK, Grayscale, and so on) it will be converted over to the format
// specified here by CGBitmapContextCreate.
context = CGBitmapContextCreate (bitmapData,
pixelsWide,
pixelsHigh,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedFirst);
if (context == NULL)
{
free (bitmapData);
fprintf (stderr, "Context not created!");
}
// Make sure and release colorspace before returning
CGColorSpaceRelease( colorSpace );
return context;
}
#end
The following is my code, it's working properly, it's just that the "animation" of a moving line in the line if (guidelineOn) is not smooth. The drawRect is being called every 0.01 seconds.
if (guideLineOn)
{
[self drawGuidanceLineAtPoint:CGPointMake((65+guideLineOffset)*scalingF, 98*scalingF) withAlpha:paramAlpha];
}
It's kind of laggy and sometimes interferes with the user input. How do I resolve this? New to this so open to suggestions.
Thanks in advance for your help.
Pier.
- (void)drawRect:(CGRect)rect
{
scalingF = 1.0; // default : iPhone
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
scalingF = IPAD_SCALING_FACTOR;
}
if (guideLineOn)
{
[self drawGuidanceLineAtPoint:CGPointMake((65+guideLineOffset)*scalingF, 98*scalingF) withAlpha:paramAlpha];
}
// draw staff line
[self drawStaffLineFrom:CGPointMake(65*scalingF, 98*scalingF) toPoint:CGPointMake(420*scalingF, 98*scalingF)];
[self drawStaffLineFrom:CGPointMake(420*scalingF,108*scalingF) toPoint:CGPointMake(420*scalingF, 50*scalingF)];
[self drawStaffLineFrom:CGPointMake(415*scalingF,108*scalingF) toPoint:CGPointMake(415*scalingF, 50*scalingF)];
// cycle through all the static images to draw tbd
float offSet = 0.0;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
offSet += 50.0; // to adjust the metronome down
}
if (metronomeImage)
{
[metronomeImage drawInRect:CGRectMake(410.0f*scalingF, 195.0f*scalingF+offSet, metronomeImage.size.width*scalingF, metronomeImage.size.height*scalingF)];
}
if (circlesImage)
{
[circlesImage drawInRect:CGRectMake(400.0f*scalingF, 255.0f*scalingF+offSet, circlesImage.size.width*scalingF, circlesImage.size.height*scalingF)];
}
for (Note * v in notesArray)
{
UIImage * noteImage = [UIImage imageNamed: v.imageName];
[noteImage drawInRect:CGRectMake(v.notePlacement.x*scalingF, v.notePlacement.y*scalingF, noteImage.size.width*scalingF, noteImage.size.height*scalingF)];
}
if (!clearDisplay)
{
// draw the arrows
for (int i=0; i < arrowsToDrawArray.count; i++)
{
Arrow * arrowObj = [arrowsToDrawArray objectAtIndex:i];
UIColor * colourOfArrow = [arrowObj colourOfArrow]; // colour determines whether right or wrong
CGPoint p = [arrowObj arrowPlacement];
p.x = p.x*scalingF;
p.y = p.y*scalingF;
// CGPoint p = [val CGPointValue];
[self drawRooftopAtTopPointof:p colour:colourOfArrow lineJoin:kCGLineJoinMiter];
}
// draw the ties
for (int j=0; j < tiePointsArray.count; j++)
{
CGPointPair * tiePairToDraw = [tiePointsArray objectAtIndex:j];
CGPoint firstPt = [tiePairToDraw firstPoint];
firstPt.x = firstPt.x *scalingF;
firstPt.y = firstPt.y *scalingF;
CGPoint secondPt = [tiePairToDraw secondPoint];
secondPt.x = secondPt.x *scalingF;
secondPt.y = secondPt.y *scalingF;
[self drawPseudoCurveFromPoint:firstPt toPoint:secondPt];
}
// bool perfect = true;
// int noOfNotesHit = 0;
// draw the tick/cross/arrow
for (int k=0; k < answerObjArray.count; k++)
{
Answer * ansObj = [answerObjArray objectAtIndex:k];
if (ansObj.hit)
{
// noOfNotesHit++;
if (ansObj.earlyOrLate == -1) // early, draw right pointing arrow
{
UIImage * arrowImage = [UIImage imageNamed: #"arrowright.png"];
[arrowImage drawInRect:CGRectMake((ansObj.xPosition-(0.5*arrowImage.size.width))*scalingF, 125*scalingF, arrowImage.size.width*scalingF, arrowImage.size.height*scalingF)];
// perfect = false;
}
else if (ansObj.earlyOrLate == 1) // late, draw left pointing arrow
{
UIImage * arrowImage = [UIImage imageNamed: #"arrowleft.png"];
[arrowImage drawInRect:CGRectMake((ansObj.xPosition-(0.5*arrowImage.size.width))*scalingF, 125*scalingF, arrowImage.size.width*scalingF, arrowImage.size.height*scalingF)];
//perfect = false;
}
else // perfect!
{
// draw a tick
UIImage * tickImage = [UIImage imageNamed: #"tick.png"];
[tickImage drawInRect:CGRectMake((ansObj.xPosition-(0.5*tickImage.size.width))*scalingF, 125*scalingF, tickImage.size.width*scalingF, tickImage.size.height*scalingF)];
}
}
else
{
// draw a cross
UIImage * crossImage = [UIImage imageNamed: #"cross.png"];
[crossImage drawInRect:CGRectMake((ansObj.xPosition-(0.5*crossImage.size.width))*scalingF, 125*scalingF, crossImage.size.width*scalingF, crossImage.size.height*scalingF)];
//perfect = false;
}
}
}
else
{
// draw nothing
clearDisplay = false;
}
}
Here's my Core Animation solution for those interested.
- (void) animateRedLine: (float) alphaParam
{
[self.view addSubview:self.redlineImageView];
/* Start from top left corner */
[self.redlineImageView setFrame:CGRectMake(65.0f*scalingF,
50.0f*scalingF,
self.redlineImageView.image.size.width*scalingF,
self.redlineImageView.image.size.height*scalingF*2)];
[self.redlineImageView setAlpha:alphaParam];
[UIView beginAnimations:#"redLineAnimation"
context:( void *)self.redlineImageView];
/* 3 seconds animation */
[UIView setAnimationDuration:noOfBeatsInBar*timeInSecondsForOneBeat];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
/* Receive animation delegates */
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:
#selector(imageViewDidStop:finished:context:)];
/* End at the bottom right corner */
[self.redlineImageView setFrame:CGRectMake((65.0f+LENGTH_OF_BAR)*scalingF,
50.0f*scalingF,
self.redlineImageView.image.size.width*scalingF,
self.redlineImageView.image.size.height*scalingF*2)];
//[self.redlineImageView setAlpha:0.0f];
[UIView commitAnimations];
}