Adapt drawing shape in 3.5inch and 4inch screen - ios

I am a new ios developer and here's my problem.I draw a shape with this code :
-(void)drawFirstShape:(float)xcoord :(float)ycoord :(CGContextRef)c {
float cote = 70.0f;
float x = 60.0f;
float y = 35.0f;
CGFloat strokecolor[4] = {1.0f, 1.0f, 1.0f, 1.0f};
CGContextSetStrokeColor(c, strokecolor);
CGContextSetLineWidth(c, 4.0f);
CGContextBeginPath(c);
CGContextMoveToPoint(c, xcoord, ycoord);
CGContextAddLineToPoint(c, xcoord+x, ycoord+y);
CGContextAddLineToPoint(c, xcoord+x, ycoord+cote+y);
CGContextAddLineToPoint(c, xcoord, ycoord+cote+2*y);
CGContextClosePath(c);
CGContextDrawPath(c, kCGPathStroke);
}
- (void)drawRect:(CGRect)rect
{
CGContextRef c = UIGraphicsGetCurrentContext();
[self drawFirstShape:160.0f :100.0f:c];
}
I want to create relative coordinates instead of absolute ones.
The aim is to stabilize this code to use it with a 3.5inch and 4inch screen. Can someone help me please?

If you want to use relative coordinates then use relative coordinates :)
All positions and width/height should be defined as float or double from 0 to 1.
Then to get real coordinate - just multiply these relative coordinates to frame sizes (or to rect in your case) and you will get absolute coordinates or sizes.
Example (I just used your code with small changes. keep in mind that I changed sizes to relative and you will need to change them to be correct ones):
-(void)drawFirstShape:(float)xcoord :(float)ycoord :(CGContextRef)c inRect:(CGRect)rect{
xcoord = xcoord*rect.size.width;
ycoord = ycoord*rect.size.height;
float cote = 0.07f*rect.size.width;
float x = 0.05f*rect.size.width;
float y = 0.025f*rect.size.height;
CGFloat strokecolor[4] = {1.0f, 1.0f, 1.0f, 1.0f};
CGContextSetStrokeColor(c, strokecolor);
CGContextSetLineWidth(c, 4.0f);
CGContextBeginPath(c);
CGContextMoveToPoint(c, xcoord, ycoord);
CGContextAddLineToPoint(c, xcoord+x, ycoord+y);
CGContextAddLineToPoint(c, xcoord+x, ycoord+cote+y);
CGContextAddLineToPoint(c, xcoord, ycoord+cote+2*y);
CGContextClosePath(c);
CGContextDrawPath(c, kCGPathStroke);
}
- (void)drawRect:(CGRect)rect {
CGContextRef c = UIGraphicsGetCurrentContext();
[self drawFirstShape:0.2 :0.1 :c inRect:rect];
}

Related

Harsh colour banding in radial CGGradient

CGContextDrawRadialGradient produces a very visible ‘cross’ at the centre of the gradient:
Code (reduced):
- (void)drawRect:(NSRect)dirtyRect {
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
size_t numberOfGradientLocations = 2;
CGFloat startRadius = 0.0f;
CGFloat endRadius = 30.0f;
CGPoint centre = CGPointMake(floorf(self.bounds.size.width / 2), floorf(self.bounds.size.height / 2));
CGFloat gradientColours[8] = {0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f};
CGFloat gradientLocations[2] = {0.0f, 1.0f};
CGColorSpaceRef colourspace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(colourspace, gradientColours, gradientLocations, numberOfGradientLocations);
CGContextDrawRadialGradient(context, gradient, centre, startRadius, centre, endRadius, kCGGradientDrawsBeforeStartLocation);
}
This happens on both macOS and iOS. Meanwhile, the same kind of gradient renders perfectly in WebKit with CSS (so it’s not some ‘bad display’ issue).
What am I doing wrong? Is there a known way around this?
Here's what I get on iOS; I used the iPhone 7 Plus simulator at 100% so as to make every pixel visible, and it seems completely smooth:
EDIT After some experimentation, I suspect you're seeing a moiré effect caused by misalignment between points and pixels.

iOS CGContext Edge isn't antialiased

I'm trying to draw a rectangle that has a 15 degree angle splitting two colors down the center but the line between them is pixelated. I've tried different blend options and can't seem to figure out how to make this look clean.
This is what the header looks like now
Does anyone know how I can make the line between them look clean?
if (!_backgroundImageView.image) {
CGFloat width = CGRectGetWidth(self.bounds);
CGFloat height = CGRectGetHeight(self.bounds);
CGFloat tWidth = 0.26794919243f/*tan(15 degrees)*/ * height;
UIGraphicsBeginImageContext(CGSizeMake(width, height));
CGContextRef context = UIGraphicsGetCurrentContext();
if (_color1) {
CGContextSetFillColorWithColor(context, _color1.CGColor);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, 1.0f + RoundPixelValue((width + tWidth) / 2.0f), 0);
CGContextAddLineToPoint(context, 1.0f + RoundPixelValue((width - tWidth) / 2.0f), height);
CGContextAddLineToPoint(context, 0, height);
CGContextFillPath(context);
}
if (_color2) {
CGContextSetFillColorWithColor(context, _color2.CGColor);
CGContextMoveToPoint(context, width, 0);
CGContextAddLineToPoint(context, RoundPixelValue((width + tWidth) / 2.0f) - 1.0f, 0);
CGContextAddLineToPoint(context, RoundPixelValue((width - tWidth) / 2.0f) - 1.0f, height);
CGContextAddLineToPoint(context, width, height);
CGContextFillPath(context);
}
_backgroundImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
The main issue here is that you're not creating the context with the correct scale.
Instead of using UIGraphicsBeginImageContext use UIGraphicsBeginImageContextWithOptions like so:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), YES, 0);
Specifying a scale of 0 sets it to the scale factor of the device screen.
This should eliminated most of the aliasing artifacts.

CoreGraphics rotate and image around a point in iOS

I have tried everything I can think of, but none of its working. In an iOS UIView drawRect, I want a function that takes an image, a point on the image, scale, and an angle, and draws the image rotated around the given point at the center of the view. So if I want to rotate around the origin (0,0) the edge of my image will appear in the center of the view.
Here's my first version of the code:
-(void)drawImage:(UIImage*)image atAngle:(float)theta atScale:(float)scale whereCenter:(JBPoint*)center inContext:(CGContextRef)context
{
UIGraphicsPushContext(context);
//Rotate
CGContextTranslateCTM (context, -center.x, -center.y);
CGContextRotateCTM (context, theta);
CGContextTranslateCTM (context, center.x, center.y);
//Scale
CGContextScaleCTM(context, scale, scale);
//Draw
GContextDrawImage(context, self.frame, image.CGImage);
UIGraphicsPopContext();
}
However, this skewed the image such that it was not an affine transformation even though I only performed affine transformations on it.
After fiddling with it for a time, I just decided to do it by hand, and calculate the locations of the four corner points and draw it then:
-(void)drawImage:(UIImage*)image atAngle:(float)theta atScale:(float)scale whereCenter:(JBPoint*)center inContext:(CGContextRef)context
{
UIGraphicsPushContext(context);
CGContextRotateCTM (context, theta);
float ux = cos(theta);
float uy = sin(theta);
float vx = sin(theta);
float vy = -cos(theta);
CGPoint p1 = CGPointMake(self.frame.size.width/2.0f-ux*scale*center.x-vx*scale*(image.size.height - center.y), self.frame.size.height/2.0f-uy*scale*center.x-vy*scale*(image.size.height - center.y));
CGPoint p2 = CGPointMake(p1.x+ux*image.size.width*scale, p1.y+uy*image.size.width*scale);
CGPoint p3 = CGPointMake(p2.x+vx*image.size.height*scale, p2.y+vy*image.size.height*scale);
CGPoint p4 = CGPointMake(p1.x+vx*image.size.height*scale, p1.y+vy*image.size.height*scale);
//Sanity check. These all should be the same since I want to center at the center of the frame
NSLog(#"Center: %f, %f", (p1.x+p3.x)/2.0f, (p1.y+p3.y)/2.0f);
NSLog(#"Center: %f, %f", (p2.x+p4.x)/2.0f, (p2.y+p4.y)/2.0f);
NSLog(#"Center: %f, %f", self.frame.size.width/2.0f, self.frame.size.height/2.0f);
float minX = MIN(MIN(p1.x, p2.x), MIN(p3.x, p4.x));
float minY = MIN(MIN(p1.y, p2.y), MIN(p3.y, p4.y));
float maxX = MAX(MAX(p1.x, p2.x), MAX(p3.x, p4.x));
float maxY = MAX(MAX(p1.y, p2.y), MAX(p3.y, p4.y));
CGContextSetAlpha(context, alpha);
CGContextScaleCTM(context, 1, -1);
CGContextDrawImage(context, CGRectMake(minX, -minY, (maxX-minX), -(maxY-minY)), image.CGImage);
UIGraphicsPopContext();
}
This works for angles of 0, but as soon as the angle becomes 90 degrees (pi/2) the image is extremely skewed, which doesn't make any sense.
This seems to be such a simple problem. Why is it so hard? Does anyone see what I'm doing wrong?

Core graphics rotate rectangle

With this formula I got angle
double rotateAngle = atan2(y,x)
with this code I can draw a rectangle
CGRect rect = CGRectMake(x,y , width ,height);
CGContextAddRect(context, rect);
CGContextStrokePath(context);
How can I rotate the rectangle around the angle ?
Here's how you'd do that:
CGContextSaveGState(context);
CGFloat halfWidth = width / 2.0;
CGFloat halfHeight = height / 2.0;
CGPoint center = CGPointMake(x + halfWidth, y + halfHeight);
// Move to the center of the rectangle:
CGContextTranslateCTM(context, center.x, center.y);
// Rotate:
CGContextRotateCTM(context, rotateAngle);
// Draw the rectangle centered about the center:
CGRect rect = CGRectMake(-halfWidth, -halfHeight, width, height);
CGContextAddRect(context, rect);
CGContextStrokePath(context);
CGContextRestoreGState(context);

Simple way to display the borders of a CGRect?

I'm trying to find a way to display the borders of some CGRects in my iOS program for debugging purposes. Is there a fairly simple way to do this? I just need to see where the program is creating these rectangles, so I can track down some odd touch behaviors (or lack thereof).
My class init method:
// Initialize with points and a line number, then draw a rectangle
// in the shape of the line
-(id)initWithPoint:(CGPoint)sP :(int)w :(int)h :(int)lN :(int)t {
if ((self = [super init])) {
startP = sP;
lineNum = lN;
width = w;
height = h;
int type = t;
self.gameObjectType = kPathType;
// Draw the path sprite
path = [CCSprite spriteWithFile: #"line.png" rect:CGRectMake(0, 0, 5, height)];
ccTexParams params = {GL_LINEAR,GL_LINEAR,GL_REPEAT,GL_REPEAT};
[path.texture setTexParameters:&params];
if(type == 1) {
path.position = ccp(startP.x, startP.y);
} else {
path.rotation = 90;
path.anchorPoint = ccp(0, 0);
path.position = ccp(startP.x, startP.y-2);
}
[self addChild:path];
// Draw the "bounding" box
pathBox = CGRectMake(path.position.x - (path.contentSize.width/2), path.position.y - (path.contentSize.height/2), path.contentSize.width * 10, path.contentSize.height);
}
return self;
}
pathBox is the rect in question.
This can be handled within drawRect:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect aRect=[myPath bounds];
CGContextSetLineWidth(context, 2);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor ].CGColor);
CGContextStrokeRect(context, aRect);
}
I'm going to take a stab and assume this is an iOS project, since that's what I know.
If these rectangles are being used for UIView or a CALayer then you can set the border for them.
Add #import <QuartzCore/QuartzCore.h> in your file and use view.layer.borderColor, view.layer.borderWidth add what you want.
If it's just a layer remove the view part of it. 
I figured out more-or-less how to do it: just extended the draw method in my class like so:
-(void) draw {
glColor4f(0, 1.0, 0, 1.0);
glLineWidth(2.0f);
[super draw];
CGRect pathBox = CGRectMake(path.position.x - (path.contentSize.width/2), path.position.y - (path.contentSize.height/2), path.contentSize.width * 10, path.contentSize.height);
CGPoint verts[4] = {
ccp(pathBox.origin.x, pathBox.origin.y),
ccp(pathBox.origin.x + pathBox.size.width, pathBox.origin.y),
ccp(pathBox.origin.x + pathBox.size.width, pathBox.origin.y + pathBox.size.height),
ccp(pathBox.origin.x, pathBox.origin.y + pathBox.size.height)
};
ccDrawPoly(verts, 4, YES);
}
Thanks to Blue Ether over at the Cocos2D site for the heads-up:
http://www.cocos2d-iphone.org/forum/topic/21718?replies=5#post-120691
You can try something simple like this method here. This uses an actual CGRect structure as oppose to a UIView and CLayer.
-(void) drawBorderForRect:(CGRect)rect usingColor:(UIColor*)uiColor{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, uiColor.CGColor);
CGContextSetLineWidth(context, 1.0f);
CGFloat x = rect.origin.x;
CGFloat y = rect.origin.y;
CGFloat width = abs(rect.size.width);
CGFloat height = abs(rect.size.height);
// ---
CGContextMoveToPoint(context, x, y);
CGContextAddLineToPoint(context, x + width, y);
// |
CGContextMoveToPoint(context, x + width, y);
CGContextAddLineToPoint(context, x + width, y - height);
// ---
CGContextMoveToPoint(context, x + width, y - height);
CGContextAddLineToPoint(context, x - width, y - height);
// |
CGContextMoveToPoint(context, x, y - height);
CGContextAddLineToPoint(context, x, y);
CGContextStrokePath(context);
}
In swift, it's easy. You can construct a BezierPath from a CGRect:
let dp = UIBezierPath.init(rect: myRect)
dp.stroke()

Resources