Related
I am playing with Brad Larsen's adaption of the trackball app.
I have two views at a 60 degree angle to each other and was wondering how I get the rotation to be in the center of this (non-closed) rectangle?
In the images below I would have liked the rotation to take place all within the blue lines.
Code (modified to only rotate around x axis):
#import "MyView.h"
//=====================================================
// Defines
//=====================================================
#define DEGREES_TO_RADIANS(degrees) \
(degrees * (M_PI / 180.0f))
//=====================================================
// Public Interface
//=====================================================
#implementation MyView
- (void)awakeFromNib
{
transformed = [CALayer layer];
transformed.anchorPoint = CGPointMake(0.5f, 0.5f);
transformed.frame = self.bounds;
[self.layer addSublayer:transformed];
CALayer *imageLayer = [CALayer layer];
imageLayer.frame = CGRectMake(10.0f, 4.0f, self.bounds.size.width / 2.0f, self.bounds.size.height / 2.0f);
imageLayer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(60.0f), 1.0f, 0.0f, 0.0f);
imageLayer.contents = (id)[[UIImage imageNamed:#"IMG_0051.png"] CGImage];
imageLayer.borderColor = [UIColor yellowColor].CGColor;
imageLayer.borderWidth = 2.0f;
[transformed addSublayer:imageLayer];
imageLayer = [CALayer layer];
imageLayer.frame = CGRectMake(10.0f, 120.0f, self.bounds.size.width / 2.0f, self.bounds.size.height / 2.0f);
imageLayer.transform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(-60.0f), 1.0f, 0.0f, 0.0f);
imageLayer.contents = (id)[[UIImage imageNamed:#"IMG_0089.png"] CGImage];
imageLayer.borderColor = [UIColor greenColor].CGColor;
imageLayer.borderWidth = 2.0f;
transformed.borderColor = [UIColor whiteColor].CGColor;
transformed.borderWidth = 2.0f;
[transformed addSublayer:imageLayer];
UIView *line = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height / 2.0f, self.bounds.size.width, 2)];
[line setBackgroundColor:[UIColor redColor]];
[self addSubview:line];
line = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height * (1.0f / 4.0f), self.bounds.size.width, 2)];
[line setBackgroundColor:[UIColor blueColor]];
[self addSubview:line];
line = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height * (3.0f / 4.0f), self.bounds.size.width, 2)];
[line setBackgroundColor:[UIColor blueColor]];
[self addSubview:line];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
previousLocation = [[touches anyObject] locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint location = [[touches anyObject] locationInView:self];
//location = CGPointMake(previousLocation.x, location.y);
CATransform3D currentTransform = transformed.sublayerTransform;
//CGFloat displacementInX = location.x - previousLocation.x;
CGFloat displacementInX = previousLocation.x - location.x;
CGFloat displacementInY = previousLocation.y - location.y;
CGFloat totalRotation = sqrt((displacementInX * displacementInX) + (displacementInY * displacementInY));
CGFloat angle = DEGREES_TO_RADIANS(totalRotation);
CGFloat x = ((displacementInX / totalRotation) * currentTransform.m12 + (displacementInY/totalRotation) * currentTransform.m11);
CATransform3D rotationalTransform = CATransform3DRotate(currentTransform, angle, x, 0, 0);
previousLocation = location;
transformed.sublayerTransform = rotationalTransform;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)dealloc {
[super dealloc];
}
#end
You need to set the imageLayer.zPosition of each of the triangle sides to the distance from the center of the triangle (which is an equilateral triangle in your case).
If sideHeight = self.bounds.size.height / 2.0f;
Then distanceFromCenter = ((sideHeight / 2.0f) / sqrt(3));
Also, when setting the rotation on the sides, you need to move them into their required positions (in your code they are hardcoded).
Updated Code
- (void)awakeFromNib
{
transformed = [CALayer layer];
transformed.anchorPoint = CGPointMake(0.5f, 0.5f);
transformed.frame = self.bounds;
[self.layer addSublayer:transformed];
CGFloat sideHeight = self.bounds.size.height / 2.0f;
CGFloat distanceFromCenter = ((sideHeight / 2.0f) / sqrt(3));
CGFloat sideC = sideHeight / 2.0f;
CGFloat sideA = sideC / 2.0f;
CGFloat sideB = (sqrt(3) * sideA);
CALayer *imageLayer = [CALayer layer];
imageLayer.frame = CGRectMake(10.0f, (self.bounds.size.height / 2.0f) - (sideHeight / 2.0f), self.bounds.size.width / 2.0f, sideHeight);
imageLayer.transform = CATransform3DConcat(CATransform3DMakeRotation(-DEGREES_TO_RADIANS(60.0f), 1.0f, 0.0f, 0.0f),
CATransform3DMakeTranslation(0, -sideA, -sideB));
imageLayer.contents = (id)[[UIImage imageNamed:#"IMG_0051.png"] CGImage];
imageLayer.borderColor = [UIColor yellowColor].CGColor;
imageLayer.borderWidth = 2.0f;
imageLayer.zPosition = distanceFromCenter;
[transformed addSublayer:imageLayer];
imageLayer = [CALayer layer];
imageLayer.frame = CGRectMake(10.0f, (self.bounds.size.height / 2.0f) - (sideHeight / 2.0f), self.bounds.size.width / 2.0f, sideHeight);
imageLayer.transform = CATransform3DConcat(CATransform3DMakeRotation(DEGREES_TO_RADIANS(60.0f), 1.0f, 0.0f, 0.0f),
CATransform3DMakeTranslation(0, sideA, -sideB));
imageLayer.contents = (id)[[UIImage imageNamed:#"IMG_0089.png"] CGImage];
imageLayer.borderColor = [UIColor greenColor].CGColor;
imageLayer.zPosition = distanceFromCenter;
imageLayer.borderWidth = 2.0f;
transformed.borderColor = [UIColor whiteColor].CGColor;
transformed.borderWidth = 2.0f;
[transformed addSublayer:imageLayer];
UIView *line = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height / 2.0f, self.bounds.size.width, 2)];
[line setBackgroundColor:[UIColor redColor]];
[self addSubview:line];
line = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height * (1.0f / 4.0f), self.bounds.size.width, 2)];
[line setBackgroundColor:[UIColor blueColor]];
[self addSubview:line];
line = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height * (3.0f / 4.0f), self.bounds.size.width, 2)];
[line setBackgroundColor:[UIColor blueColor]];
[self addSubview:line];
}
Use also other transformation (I think you'll need translation) and concatenate them with rotation. This example even scales the image, so you get a scaled, shifted and rotated layer:
CATransform3D translate = CATransform3DMakeTranslation(yourShiftByX, yourShiftByY, 0);
CATransform3D scale = CATransform3DMakeScale(yourRateX, yourRateY, 1);
CATransform3D rotate = CATransform3DMakeRotation(DEGREES_TO_RADIANS(60.0f), 1.0, 0.0, 0.0);
CATransform3D transform = CATransform3DConcat(CATransform3DConcat(rotate, scale), translate);
// the order is important: FIRST we rotate, THEN we scale and AT LAST we position the object
// now apply 'transform' to your layer
you should make your imageView superView to show the perspective :
CATransform3D tranfrom = CATransform3DIdentity;
tranfrom.m34 = -1.0 / z;
imageView.superview.layer.transform = transfrom;
I am trying to implement a custom slider as shown in figure below.
what I have done so far looks something like this
please help me out for drawing the arc with such effect. my code is as below, what I am doing is drawing the arc using CGContextAddArc with line width kLineWidth.
- (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext: (CGContextRef)context {
UIGraphicsPushContext(context);
CGContextBeginPath(context);
CGContextMoveToPoint(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y);
CGImageRef imageRef = [UIImage imageNamed:#"circle25.png"].CGImage;
CGRect rect = CGRectMake(sliderButtonCenterPoint.x - kThumbRadius, sliderButtonCenterPoint.y - kThumbRadius, kThumbRadius*2, kThumbRadius*2);
CGContextDrawImage(context, rect, imageRef);
//CGContextAddArc(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y, kThumbRadius, 0.0, 2*M_PI, NO);
CGContextFillPath(context);
UIGraphicsPopContext();
}
- (CGPoint)drawArcTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius inContext:(CGContextRef)context {
UIGraphicsPushContext(context);
CGContextBeginPath(context);
float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, M_PI/3);// 2*M_PI
CGFloat startAngle = (4*M_PI)/3;
CGFloat endAngle = startAngle + angleFromTrack;
CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, NO);
CGPoint arcEndPoint = CGContextGetPathCurrentPoint(context);
CGContextStrokePath(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.width;
CGContextSetLineWidth(context, kLineWidth);
CGFloat radius = [self sliderRadius];
[self.maximumTrackTintColor setStroke];
[self drawArcTrack:self.maximumValue atPoint:middlePoint withRadius:radius inContext:context];
[self.minimumTrackTintColor setStroke];
self.thumbCenterPoint = [self drawArcTrack:self.value atPoint:middlePoint withRadius:radius inContext:context];
[self.thumbTintColor setFill];
[self drawThumbAtPoint:self.thumbCenterPoint inContext:context];
}
Unless you are going to be changing the shape dynamically, you would probably be better off just creating the image in an image editor. I know it's easy to create that effect in Photoshop, Illustrator, or Fireworks.
That said, drawing an inner shadow like that with Core Graphics requires several steps:
Clip to the shape (using e.g. CGContextClip or CGContextClipToMask).
Make a path or mask of everything but the shape.
Set your shadow parameters (using CGContextSetShadowWithColor).
Fill the path or mask from step 2. This casts a shadow inside the shape, and only the shadow is drawn because you clipped to the shape in step 1.
If you do all of that correctly, you can get a nice result like this:
Here's the code I wrote to draw that. I wrote it in the drawRect: of a custom view subclass, but you can easily use this code to draw into any graphics context.
- (void)drawRect:(CGRect)rect {
CGContextRef gc = UIGraphicsGetCurrentContext();
First, I create a path that's just an arc:
static CGFloat const kArcThickness = 20.0f;
CGRect arcBounds = CGRectInset(self.bounds, 10.0f, 10.0f);
CGPoint arcCenter = CGPointMake(CGRectGetMidX(arcBounds), CGRectGetMidY(arcBounds));
CGFloat arcRadius = 0.5f * (MIN(arcBounds.size.width, arcBounds.size.height) - kArcThickness);
UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:-M_PI / 3.0 endAngle:-2.0 * M_PI / 3.0 clockwise:NO];
Next, I ask Core Graphics to make a new path that is the outline of the arc path. Note how I ask it for a stroke width of kArcThickness and round line caps:
CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
I also need the inverse of that path: a path that includes every point except the points in shape. It so happens (although I don't think it's documented) that CGContextCreateCopyByStrokingPath and CGPathAddRect draw in opposite directions. So if I copy shape and draw an enormous rectangle around it, the non-zero winding rule means that the new path will be the inverse of shape:
CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
Now I can actually start drawing. First, I'll fill in the shape with a light gray color:
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
CGContextFillPath(gc);
Next I actually perform the four steps I listed above. I have to save the graphics state so I can undo the clipping and shadow parameters when I'm done.
CGContextSaveGState(gc); {
Step 1: clip to the shape:
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextClip(gc);
Step 2: Well, I did this step already when I created shapeInverse.
Step 3: I set the shadow parameters:
CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
Step 4: I fill the inverse shape from step 2:
CGContextBeginPath(gc);
CGContextAddPath(gc, shapeInverse);
CGContextFillPath(gc);
Now I restore the graphics state, which specifically restores the clipping path and unsets the shadow parameters.
} CGContextRestoreGState(gc);
Finally, I'll stroke shape with a light gray to make the edge crisper:
CGContextSetStrokeColorWithColor(gc, [UIColor colorWithWhite:.75 alpha:1].CGColor);
CGContextSetLineWidth(gc, 1);
CGContextSetLineJoin(gc, kCGLineCapRound);
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextStrokePath(gc);
Of course I clean up when I'm done:
CGPathRelease(shape);
CGPathRelease(shapeInverse);
}
For more complex shapes, you can look at my answer here and my answer here.
Here's all the code together for easy copying:
- (void)drawRect:(CGRect)rect {
CGContextRef gc = UIGraphicsGetCurrentContext();
static CGFloat const kArcThickness = 20.0f;
CGRect arcBounds = CGRectInset(self.bounds, 10.0f, 10.0f);
CGPoint arcCenter = CGPointMake(CGRectGetMidX(arcBounds), CGRectGetMidY(arcBounds));
CGFloat arcRadius = 0.5f * (MIN(arcBounds.size.width, arcBounds.size.height) - kArcThickness);
UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:-M_PI / 3.0 endAngle:-2.0 * M_PI / 3.0 clockwise:NO];
CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
CGContextFillPath(gc);
CGContextSaveGState(gc); {
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextClip(gc);
CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
CGContextBeginPath(gc);
CGContextAddPath(gc, shapeInverse);
CGContextFillPath(gc);
} CGContextRestoreGState(gc);
CGContextSetStrokeColorWithColor(gc, [UIColor colorWithWhite:.75 alpha:1].CGColor);
CGContextSetLineWidth(gc, 1);
CGContextSetLineJoin(gc, kCGLineCapRound);
CGContextBeginPath(gc);
CGContextAddPath(gc, shape);
CGContextStrokePath(gc);
CGPathRelease(shape);
CGPathRelease(shapeInverse);
}
check the below code for fully functional version of the above component, its working (may be its bit messy).
#import "UIArcSlider.h"
#interface UIArcSlider()
#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)drawTheArcTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius AndColor:(Boolean) isWhite inContext:(CGContextRef)context;
- (CGPoint)drawTheLineTrack:(float)track withColor:(Boolean) isWhite inContext:(CGContextRef)context;
#end
#pragma mark -
#implementation UIArcSlider
#synthesize sliderStyle = _sliderStyle;
- (void)setSliderStyle:(UISliderStyle)sliderStyle {
if (sliderStyle != _sliderStyle) {
_sliderStyle = sliderStyle;
[self setNeedsDisplay];
}
}
#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];
[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 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 = 100.0;
self.thumbCenterPoint = CGPointZero;
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 22.0
#define kThumbRadius 20.0
#define kPadding 20.0
- (CGFloat)sliderRadius {
CGFloat radius = self.bounds.size.width;
radius -= MAX(kLineWidth + kPadding, kThumbRadius + kPadding);
return radius;
}
-(CGFloat)sliderWidth {
return self.bounds.size.width - kThumbRadius*2;
}
- (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext:(CGContextRef)context {
UIGraphicsPushContext(context);
CGContextBeginPath(context);
CGContextMoveToPoint(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y);
CGRect rect = CGRectMake(sliderButtonCenterPoint.x - kThumbRadius, sliderButtonCenterPoint.y - kThumbRadius, kThumbRadius*2, kThumbRadius*2);
CGImageRef imageRef = [UIImage imageNamed:#"circle25.png"].CGImage;
CGContextDrawImage(context, rect, imageRef);
UIGraphicsPopContext();
}
- (CGPoint)drawTheArcTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius AndColor:(Boolean) isWhite inContext:(CGContextRef)context
{
static CGFloat const kArcThickness = kLineWidth;
CGPoint arcCenter = center;
CGFloat arcRadius = radius;
float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, M_PI/3);// 2*M_PI
CGFloat startAngle = (4*M_PI)/3;
CGFloat endAngle = startAngle + angleFromTrack;
UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:startAngle endAngle:endAngle clockwise:YES];
CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
CGPoint arcEndPoint = [arc currentPoint];
CGContextBeginPath(context);
CGContextAddPath(context, shape);
if (isWhite) {
CGContextSetFillColorWithColor(context, [UIColor colorWithWhite:.9 alpha:1].CGColor);
} else {//70,172, 220
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:70/255.0 green:172/255.0 blue:220/255.0 alpha:1].CGColor);
}
CGContextFillPath(context);
CGContextSaveGState(context); {
CGContextBeginPath(context);
CGContextAddPath(context, shape);
CGContextClip(context);
if (isWhite) {
CGContextSetShadowWithColor(context, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
} else {
CGContextSetShadowWithColor(context, CGSizeZero, 7, [UIColor colorWithRed:85/255.0 green:183/255.0 blue:230/255.0 alpha:.25].CGColor);
}
CGContextBeginPath(context);
CGContextAddPath(context, shapeInverse);
CGContextFillPath(context);
} CGContextRestoreGState(context);
if (isWhite) {
CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:.75 alpha:1].CGColor);
} else {
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:62/255.0 green:135/255.0 blue:169/255.0 alpha:1].CGColor);
}
CGContextSetLineWidth(context, 1);
CGContextSetLineJoin(context, kCGLineCapRound);
CGContextBeginPath(context);
CGContextAddPath(context, shape);
CGContextStrokePath(context);
CGPathRelease(shape);
CGPathRelease(shapeInverse);
return arcEndPoint;
}
- (CGPoint)drawTheLineTrack:(float)track withColor:(Boolean) isWhite inContext:(CGContextRef)context
{
CGFloat sliderWidth = [self sliderWidth];
CGFloat xStart = self.bounds.origin.x + (self.bounds.size.width - sliderWidth)/2;
CGFloat lineRectX = (self.bounds.size.width - sliderWidth)/2;
CGFloat lineThickness = kLineWidth/2;
CGFloat lineRectY = (self.bounds.size.height - lineThickness)/2;
CGFloat xPoint = translateValueToPoint(track, xStart, self.maximumValue, sliderWidth);
sliderWidth = xPoint;
CGRect lineRect = CGRectMake(lineRectX, lineRectY, sliderWidth, lineThickness);
UIBezierPath *line = [UIBezierPath bezierPathWithRoundedRect:lineRect cornerRadius:lineThickness/2];
CGPathRef shape = CGPathCreateCopyByStrokingPath(line.CGPath, NULL, lineThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
CGPathAddRect(shapeInverse, NULL, CGRectInfinite);
CGPoint arcEndPoint = CGPointMake(lineRectX + xPoint, lineRectY + (lineThickness/2));
CGContextBeginPath(context);
CGContextAddPath(context, shape);
if (isWhite) {
CGContextSetFillColorWithColor(context, [UIColor colorWithWhite:.9 alpha:1].CGColor);
} else {//70,172, 220
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:70/255.0 green:172/255.0 blue:220/255.0 alpha:1].CGColor);
}
CGContextFillPath(context);
CGContextSaveGState(context); {
CGContextBeginPath(context);
CGContextAddPath(context, shape);
CGContextClip(context);
if (isWhite) {
CGContextSetShadowWithColor(context, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
} else {
CGContextSetShadowWithColor(context, CGSizeZero, 7, [UIColor colorWithRed:85/255.0 green:183/255.0 blue:230/255.0 alpha:.25].CGColor);
}
CGContextBeginPath(context);
CGContextAddPath(context, shapeInverse);
CGContextFillPath(context);
} CGContextRestoreGState(context);
if (isWhite) {
CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:.75 alpha:1].CGColor);
} else {
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:62/255.0 green:135/255.0 blue:169/255.0 alpha:1].CGColor);
}
CGRect outlineRect = CGRectMake(lineRectX - (lineThickness/2), lineRectY - (lineThickness/2), sliderWidth + lineThickness, lineThickness * 2);
UIBezierPath *outline = [UIBezierPath bezierPathWithRoundedRect:outlineRect cornerRadius:lineThickness];
CGContextSetLineWidth(context, 1);
CGContextSetLineJoin(context, kCGLineCapSquare);
CGContextBeginPath(context);
CGContextAddPath(context, outline.CGPath);
CGContextStrokePath(context);
CGPathRelease(shape);
CGPathRelease(shapeInverse);
return arcEndPoint;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGPoint middlePoint;
switch (self.sliderStyle) {
case UISliderStyleLine:
[self drawTheLineTrack:self.maximumValue withColor:YES inContext:context];
self.thumbCenterPoint = [self drawTheLineTrack:self.value withColor:NO inContext:context];
[self drawThumbAtPoint:self.thumbCenterPoint inContext:context];
break;
case UISliderStyleArc:
default:
middlePoint.x = self.bounds.origin.x + self.bounds.size.width/2;
middlePoint.y = self.bounds.origin.y + self.bounds.size.width;
CGFloat radius = [self sliderRadius];
[self drawTheArcTrack:self.maximumValue atPoint:middlePoint withRadius:radius AndColor:YES inContext:context];
self.thumbCenterPoint = [self drawTheArcTrack:self.value atPoint:middlePoint withRadius:radius AndColor:NO inContext:context];
[self drawThumbAtPoint:self.thumbCenterPoint inContext:context];
break;
}
}
/** #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];
switch (panGestureRecognizer.state) {
case UIGestureRecognizerStateChanged: {
CGFloat radius;
CGPoint sliderCenter;
CGPoint sliderStartPoint;
CGFloat angle;
CGFloat xStart;
CGFloat maximumValue;
CGFloat sliderWidth;
CGFloat point;
switch (self.sliderStyle) {
case UISliderStyleLine:
sliderWidth = [self sliderWidth];
xStart = self.bounds.origin.x + (self.bounds.size.width - sliderWidth)/2;
maximumValue = self.maximumValue;
point = tapLocation.x;
self.value = translatePointToValue(point, xStart, maximumValue, sliderWidth);
break;
case UISliderStyleArc:
default:
radius = [self sliderRadius];
sliderCenter = CGPointMake(self.bounds.size.width/2, self.bounds.size.width);
sliderStartPoint = CGPointMake(sliderCenter.x - (radius * 1.15), sliderCenter.y - (radius * 2.1) );
angle = angleBetweenThreePoints(sliderCenter, sliderStartPoint, tapLocation);
if (angle < 0) {
angle = -angle;
}
else {
angle = 0.0;
//angle = M_PI/3 - angle;
}
self.value = translateValueFromSourceIntervalToDestinationInterval(angle, 0, M_PI/3, self.minimumValue, self.maximumValue);
break;
}
}
default:
break;
}
}
- (void)tapGestureHappened:(UITapGestureRecognizer *)tapGestureRecognizer {
if (tapGestureRecognizer.state == UIGestureRecognizerStateEnded) {
CGPoint tapLocation = [tapGestureRecognizer locationInView:self];
if ([self isPointInThumb:tapLocation]) {
// do something on tap
}
else {
}
}
}
#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;
}
float translateValueToPoint(float value, float xStart, float maximum, float width)
{
float point = (width * value) / maximum;
//
// point = xStart + point;
//
return point;
}
float translatePointToValue(float point, float xStart, float maximum, float width)
{
float value = (point) * maximum;
value = value / width;
return value;
}
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;
}
You'll require the thumb image as well
I have an application where I am showing an alert message using UIAlertView. By default the color of the dialog box is blue. Can anyone tell me, how to change the color of that? Can it be changed?
Use Like This
[CustomAlert setBackgroundColor:[UIColor blueColor]
withStrokeColor:[UIColor greenColor]];
.h and .m File's Content
CustomAlert.h
#import <UIKit/UIKit.h>
#interface CustomAlert : UIAlertView
{
}
+ (void) setBackgroundColor:(UIColor *) background
withStrokeColor:(UIColor *) stroke;
#end
CustomAlert.m
#import "CustomAlert.h"
#interface CustomAlert (Private)
- (void) drawRoundedRect:(CGRect) rect inContext:(CGContextRef)
context withRadius:(CGFloat) radius;
#end
static UIColor *fillColor = nil;
static UIColor *borderColor = nil;
#implementation CustomAlert
+ (void) setBackgroundColor:(UIColor *) background
withStrokeColor:(UIColor *) stroke
{
if(fillColor != nil)
{
[fillColor release];
[borderColor release];
}
fillColor = [background retain];
borderColor = [stroke retain];
}
- (id)initWithFrame:(CGRect)frame
{
if((self = [super initWithFrame:frame]))
{
if(fillColor == nil)
{
fillColor = [[UIColor blackColor] retain];
borderColor = [[UIColor colorWithHue:0.625
saturation:0.0 brightness:0.8 alpha:0.8]
retain];
}
}
return self;
}
- (void)layoutSubviews
{
for (UIView *sub in [self subviews])
{
if([sub class] == [UIImageView class] && sub.tag == 0)
{
// The alert background UIImageView tag is 0,
// if you are adding your own UIImageView's
// make sure your tags != 0 or this fix
// will remove your UIImageView's as well!
[sub removeFromSuperview];
break;
}
}
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
CGContextSetAllowsAntialiasing(context, true);
CGContextSetLineWidth(context, 0.0);
CGContextSetAlpha(context, 0.8);
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [borderColor CGColor]);
CGContextSetFillColorWithColor(context, [fillColor CGColor]);
// Draw background
CGFloat backOffset = 2;
CGRect backRect = CGRectMake(rect.origin.x + backOffset,
rect.origin.y + backOffset,
rect.size.width - backOffset*2,
rect.size.height - backOffset*2);
[self drawRoundedRect:backRect inContext:context withRadius:8];
CGContextDrawPath(context, kCGPathFillStroke);
// Clip Context
CGRect clipRect = CGRectMake(backRect.origin.x + backOffset-1,
backRect.origin.y + backOffset-1,
backRect.size.width - (backOffset-1)*2,
backRect.size.height - (backOffset-1)*2);
[self drawRoundedRect:clipRect inContext:context withRadius:8];
CGContextClip (context);
//Draw highlight
CGGradientRef glossGradient;
CGColorSpaceRef rgbColorspace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 1.0, 1.0, 1.0, 0.35, 1.0, 1.0, 1.0, 0.06 };
rgbColorspace = CGColorSpaceCreateDeviceRGB();
glossGradient = CGGradientCreateWithColorComponents(rgbColorspace,
components, locations, num_locations);
CGRect ovalRect = CGRectMake(-130, -115, (rect.size.width*2),
rect.size.width/2);
CGPoint start = CGPointMake(rect.origin.x, rect.origin.y);
CGPoint end = CGPointMake(rect.origin.x, rect.size.height/5);
CGContextSetAlpha(context, 1.0);
CGContextAddEllipseInRect(context, ovalRect);
CGContextClip (context);
CGContextDrawLinearGradient(context, glossGradient, start, end, 0);
CGGradientRelease(glossGradient);
CGColorSpaceRelease(rgbColorspace);
}
- (void) drawRoundedRect:(CGRect) rrect inContext:(CGContextRef) context
withRadius:(CGFloat) radius
{
CGContextBeginPath (context);
CGFloat minx = CGRectGetMinX(rrect), midx = CGRectGetMidX(rrect),
maxx = CGRectGetMaxX(rrect);
CGFloat miny = CGRectGetMinY(rrect), midy = CGRectGetMidY(rrect),
maxy = CGRectGetMaxY(rrect);
CGContextMoveToPoint(context, minx, midy);
CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);
CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius);
CGContextClosePath(context);
}
- (void)dealloc
{
[super dealloc];
}
#end
From the UIAlertView Class Reference:
The UIAlertView class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.
You have to implement your UIAlertView. You can't change the default blue color.
use a uiview to show alert instead of using UIAlertView then change the color of that view accordingly
I have been trying desperately to draw some images into a view. The view should be inside a scrollview. For that I subclassed UIScrollview and override the drawRect method in it. And added this as my UIView's subview.
#interface DrawAnotherViewClass : UIScrollView<UIScrollViewDelegate> {
}
#end
#implementation DrawAnotherViewClass
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
CGRect fullScreenRect=[[UIScreen mainScreen] applicationFrame];
self.frame = fullScreenRect;
self.contentSize = CGSizeMake(600, 600);
self.showsHorizontalScrollIndicator = YES;
self.showsVerticalScrollIndicator = NO;
self.pagingEnabled = YES;
}
return self;
}
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextMoveToPoint(context, 10.0f, 50.0f);
CGContextAddLineToPoint(context, 10.0f, 200.0f);
CGContextStrokePath(context);
CGContextMoveToPoint(context, 8.0f, 77.0f);
CGContextAddLineToPoint(context, 300.0f, 77.0f);
CGContextStrokePath(context);
CGContextSetRGBFillColor(context, 0, 0, 255, 0.1);
CGContextSetRGBStrokeColor(context, 0, 0, 255, 1);
CGContextStrokeEllipseInRect(context, CGRectMake(65.0, 33.5, 25, 25));
UIImage *image1 = [UIImage imageNamed:#"PinDown1.png"];
UIImage *image2 = [UIImage imageNamed:#"pinGreen_v1.png"];
CGPoint drawPoint = CGPointMake(0.0f, 10.0f);
[image2 drawAtPoint:drawPoint];
for(int i =1; i<20; i++){
CGPoint drawPointOne = CGPointMake(40.0f * i, 40.0f);
[image1 drawAtPoint:drawPointOne];
}
}
Am I missing something here. Is this the right way to go.
If the view that should perform the drawing resides in that UIScrollView, you have to put the - (void)drawRect:(CGRect)rect method into that view's class method and not into the UIScrollView subclass.
I am trying to draw grid on a UIView on a UIScrollView.
The width of UIView is about 1000 pixels. However, I can draw lines only
about first 340 pixels of the UIView. Can anyone help me on this?
The drawGrid function is
- (void)drawRect:(CGRect)rect
{
NSInteger width = 25 * 5 * 10;
NSInteger height = (10 * 2 + 10) * 12;
NSInteger i;
//Get the CGContext from this view
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw rectangle with a blue stroke color and white fill color
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 1.0, 1.0);
CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
CGContextSetLineWidth(context, 2.0);
CGContextAddRect(context, CGRectMake(0.0, 0.0, width, height));
CGContextFillPath(context);
// Draw grid with red color
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
// Set the width of the pen mark
CGContextSetLineWidth(context, 0.2);
for (i = 0; i < height; i += 5)
{
if (i % 25 == 0)
{
CGContextSetLineWidth(context, 0.4);
// Draw a line
CGContextMoveToPoint(context, i, 0.0);
CGContextAddLineToPoint(context, i, width);
CGContextStrokePath(context);
CGContextSetLineWidth(context, 0.2);
}
else
{
// Draw a line
CGContextMoveToPoint(context, i, 0.0);
CGContextAddLineToPoint(context, i, width);
CGContextStrokePath(context);
}
}
for (i = 0; i < width; i += 5)
{
if (i % 25 == 0)
{
CGContextSetLineWidth(context, 0.4);
// Draw a line
CGContextMoveToPoint(context, 0.0, i);
CGContextAddLineToPoint(context, height, i);
CGContextStrokePath(context);
CGContextSetLineWidth(context, 0.2);
}
else
{
// Draw a line
CGContextMoveToPoint(context, 0.0, i);
CGContextAddLineToPoint(context, height, i);
CGContextStrokePath(context);
}
}
}
I am also setting up the UIScrollView and UIView as follows:
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
waveScrollView = [[WaveScrollView alloc] initWithFrame:frame];
CGRect waveframe = CGRectMake(0, 0, 25 * 5 * 10, (10 * 2 + 10) * 12);
EKGWaveView *ekgWaveView = [[EKGWaveView alloc] initWithFrame:waveframe];
waveScrollView.contentSize = CGSizeMake(waveframe.size.width, waveframe.size.height);
waveScrollView.maximumZoomScale = 4.0;
waveScrollView.minimumZoomScale = 0.75;
waveScrollView.clipsToBounds = YES;
waveScrollView.aView = ekgWaveView;
[waveScrollView addSubview:ekgWaveView];
waveScrollView.delegate = self;
[self.view addSubview:waveScrollView];
}
I appreciate any help. Thanks.
I'm pretty sure you've got some of your variables mixed up.
You're looping i from 0 to height but then you're using i as your x parameter when creating lines, and using width as your y parameter.