Im trying to make a UIView draw a circle with a point obviously i can get it to circle like so
view.layer.cornerRadius = 50.0f;
view.layer.masksToBounds = YES;
but I am wanting to make a point like so
this must be possible but I am unable to figure it out. may just be tired :/
The content will hold dynamic images or changing colors so designing an image isnt really possible as i need the contents of the view conform to the shape
There are a bunch of ways to accomplish this. Here is an example. Just put this into your drawRect: method.
CGContextRef context = UIGraphicsGetCurrentContext();
// Oval
UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(0, 0, 100, 100)];
[UIColor.grayColor setFill];
[ovalPath fill];
// Rectangle
CGContextSaveGState(context);
CGContextTranslateCTM(context, 79, 50);
CGContextRotateCTM(context, -45 * M_PI / 180);
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(0, 0, 25, 25)];
[UIColor.grayColor setFill];
[rectanglePath fill];
CGContextRestoreGState(context);
You may want to look into getting an app like PaintCode to help you with stuff like this. It handles these sorts of drawings with ease and can help you learn how to create them yourself.
Related
I have the following piece of code to round only specific corners of a view:
- (void)roundOnlySpecifiedCornersInView:(UIView *)view corners:(UIRectCorner)corners
{
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:(corners) cornerRadii:CGSizeMake(4.0, 4.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.path = maskPath.CGPath;
view.layer.mask = maskLayer;
}
This works perfectly in isolation. Now I also want shadow in my view, but I specifically want to apply shadow in different cases:
on all sides
all sides except bottom
all sides except top
left/right sides only
All techniques I encountered work by creating an inset of the view. The problem with this, is that, say you want to only keep shadow on left/right sides, you offset bottom and top. Since the Rect is now less high, the shadow at the left and right does not cover the full height of the view. Also, the mask layer used for rounding corners causes the shadow to no longer appear.
Example code for this:
innerView.layer.shadowColor = [[UIColor colorWithWhite:0.0f alpha:0.1f] CGColor];
innerView.layer.shadowOpacity = 1.0f;
innerView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
innerView.layer.shadowRadius = 6.0f;
CGRect shadowFrame = UIEdgeInsetsInsetRect(innerView.bounds, UIEdgeInsetsMake(9, 0, 9, 0));
CGPathRef shadowPath = [UIBezierPath bezierPathWithRect:shadowFrame].CGPath;
innerView.layer.shadowPath = shadowPath;
How can I round specific corners in a view and at the same time show shadow only at specified sides?
Answers in Swift are appreciated too!
Screenshot of what I want (this one is easy since all corners need to be rounded so I can use .layer.cornerRadius and it has shadow at all sides):
Now I just want to round only 2 of the corners (top left and top right, bottom left and bottom right) and add shadow to only some sides.
I'm not sure if it meet your demand. The code create an image with top and bottom shadow, and all rounding corner, you can modify the code to achieve what you need. You can use the image as the background of your cell(It's seems that it is UITableViewCell)
Let me know if it don't work for you.
The image:
// create a shadow image
CGSize size = CGSizeMake(ScreenWidth, ScreenWidth);
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor *backgroundColor = [UIColor colorWithRed:246.0/255.0 green:246.0/255.0 blue:246.0/255.0 alpha:1.0];
UIColor *fillColor = [UIColor whiteColor];
CGRect rect = CGRectMake(10, 10, 100, 44);
// re-draw the background
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));
// set top and bottom shadow
CGRect rectTop = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, 5);
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0, -5), 5, [UIColor colorWithRed:0 green:0 blue:0 alpha:0.1].CGColor);
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rectTop);
CGContextRestoreGState(context);
CGRect rectBottom = CGRectMake(rect.origin.x, rect.origin.y+rect.size.height-5, rect.size.width, 5);
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0, 5), 5, [UIColor colorWithRed:0 green:0 blue:0 alpha:0.1].CGColor);
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rectBottom);
CGContextRestoreGState(context);
// re-draw the background
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
CGContextFillRect(context, rect);
CGContextSaveGState(context);
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(4.0, 4.0)];
[maskPath addClip];
CGContextSetFillColorWithColor(context, fillColor.CGColor);
CGContextFillRect(context, rect);
CGContextRestoreGState(context);
You can modify the code to get a top left shadow:
// create a shadow image
CGSize size = CGSizeMake(ScreenWidth, ScreenWidth);
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor *backgroundColor = [UIColor colorWithRed:246.0/255.0 green:246.0/255.0 blue:246.0/255.0 alpha:1.0];
UIColor *fillColor = [UIColor whiteColor];
CGRect rect = CGRectMake(10, 10, 100, 44);
// re-draw the background
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));
// set top and left shadow
CGRect rectTop = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, 5);
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0, -5), 5, [UIColor colorWithRed:0 green:0 blue:0 alpha:0.1].CGColor);
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rectTop);
CGContextRestoreGState(context);
CGRect rectLeft = CGRectMake(rect.origin.x, rect.origin.y, 5, rect.size.height);
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(-5, 0), 5, [UIColor colorWithRed:0 green:0 blue:0 alpha:0.1].CGColor);
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rectLeft);
CGContextRestoreGState(context);
// re-draw the background
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
CGContextFillRect(context, rect);
CGContextSaveGState(context);
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(4.0, 4.0)];
[maskPath addClip];
CGContextSetFillColorWithColor(context, fillColor.CGColor);
CGContextFillRect(context, rect);
CGContextRestoreGState(context);
HTH
In many, many cases you would want to achieve this with drawing the shadow, of course. However, you may consider the following approach: using a stretchable image with the shadow just on the sides that you need, e.g. something similar to (this one has shadow on all sides though):
Just before you say: "Ewwwww! He's using an image for a simple shadow!" let me emphasise that this will work much better in terms of performance. For instance, if you have a lot of cells in a UICollectionView and each one is re-drawing its shadow it could significantly slow down your app during scrolling, whereas with an image-based shadow it's going to be essentially the same.
I would go even further and suggest that you could actually use a stretchable image for masking the view with rounded corners, too! Now, this may look like going a bit too far, but if you notice a drastic decrease in performance I would give it a shot. What you essentially need to do is prepare another stretchable image with a transparent "middle" part and rounded corners of the same colour as your background. Something like:
And again, before you downvote this bizarre way of doing something that can be easily achieved with two lines of code...
view.layer.cornerRadius = 20
view.layer.masksToBounds = true
...Let me point out that this will work ridiculously faster in cases when you have to display a bunch of those masked views simultaneously: for instance, masking UICollectionViewCells. Basically, if you set a mask on a UIView's layer, this mask is going to be re-calculated and applied in real time, which would cost you a lot in terms of performance during frequent redrawing of the UIView contents — when scrolling a UITableView or UICollectionView, for example.
Obviously this wouldn't work if your background is not of a solid colour, and has a gradient for instance. However, in many other cases this may help you achieve much smoother UI performance. Another advantage in your particular case is that you can easily control which corners to mask and where the shadow should drop.
Again, I'm not implying that this is the way to go in each and every case. What I'm saying is that there are a lot of cases when this could help increase the performance significantly: for example, if you are masking far too many views that are rendered on the screen at the same time, and the layout is expected to be redrawn frequently.
for specific corner set corner radius
refer from : how to set cornerRadius for only top-left and top-right corner of a UIView?
UIBezierPath *maskPath;
maskPath = [UIBezierPathbezierPathWithRoundedRect:_backgroundImageView.bounds
byRoundingCorners:(UIRectCornerBottomLeft|UIRectCornerBottomRight)
cornerRadii:CGSizeMake(3.0, 3.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
_imageView.layer.mask = maskLayer;
[maskLayer release];
I have a UIView that I want to be resizable via a UISlider. My UISlider range is set at 1.0-2.0. Currently I am resizing the view using a CGAffineTransform.
Screenshot:
My done in the custom UIView:
- (void)drawRect:(CGRect)rect {
NSLog(#"Draw rect called");
//// Square Drawing
UIBezierPath* squarePath = [UIBezierPath bezierPathWithRect: CGRectMake(8, 8, 50, 50)];
[UIColor.whiteColor setFill];
[squarePath fill];
//// Right top Drawing
UIBezierPath* rightTopPath = [UIBezierPath bezierPathWithRect: CGRectMake(57, 13, 9, 10)];
[UIColor.whiteColor setStroke];
rightTopPath.lineWidth = 1;
[rightTopPath stroke];
//// Top left Drawing
UIBezierPath* topLeftPath = [UIBezierPath bezierPathWithRect: CGRectMake(13, 0, 17.5, 9)];
[UIColor.whiteColor setStroke];
topLeftPath.lineWidth = 1;
[topLeftPath stroke];
//// Top right Drawing
UIBezierPath* topRightPath = [UIBezierPath bezierPathWithRect: CGRectMake(35, 0, 17.5, 9)];
[UIColor.whiteColor setStroke];
topRightPath.lineWidth = 1;
[topRightPath stroke];
//// Right middle Drawing
UIBezierPath* rightMiddlePath = [UIBezierPath bezierPathWithRect: CGRectMake(57, 28, 9, 10)];
[UIColor.whiteColor setStroke];
rightMiddlePath.lineWidth = 1;
[rightMiddlePath stroke];
//// Right bottom Drawing
UIBezierPath* rightBottomPath = [UIBezierPath bezierPathWithRect: CGRectMake(57, 43, 9, 10)];
[UIColor.whiteColor setStroke];
rightBottomPath.lineWidth = 1;
[rightBottomPath stroke];
}
Code for the slider:
- (IBAction)changeValue:(id)sender {
CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformIdentity, self.slider.value, self.slider.value);
self.square.transform = transform;
}
This works okay however there is quality loss in the drawing as I resize because I'm not actually dynamically changing the size of the drawing. What I would actually like to do is change the size of the drawing dynamically. How would I achieve this? Do I need to first resize the frame and then call [self.square setNeedsDisplay] passing in the multiplication factor?
EDIT:
So I tired this method, now when I move the slider I have:
- (IBAction)changeValue:(id)sender {
CGRect newFrame = CGRectMake(20, 20, 72*self.slider2.value, 72*self.slider2.value);
self.square.frame = newFrame;
[self.square resizeAt:self.slider2.value];
}
So I am just changing the frame size now, then passing the slider value into the view and calling:
-(void)resizeAt: (float)multiply{
self.size=multiply;
self.transform = CGAffineTransformScale(CGAffineTransformIdentity, self.size, self.size);
[self setNeedsDisplay];
}
an example of what is happening in drawRect:
UIBezierPath* squarePath = [UIBezierPath bezierPathWithRect: CGRectMake(11, 11, 50, 50)];
[squarePath applyTransform:self.transform];
[UIColor.whiteColor setFill];
[squarePath fill];
However the image doesn't look sharp or "redrawn"
Consider creating your bezier paths with points in the range 0.0 to 1.0 and then applying your transform directly to each bezier path. The scale of your transform would obviously change because you're scaling the paths to the full view size, not to a relative size.
Because you scale the path rather than the already rendered view the path render will still be clean and accurate.
I'm attempting to build a UIImage in code using CoreGraphics (via PaintCode) that has a border and a corner radius. I'm finding that the image has a noticeably thicker border around the corner. This seems like it would either be an iOS bug or something that I'm completely missing. Please advise.
Code:
CGRect rect = CGRectMake(0, 0, 53, 100);
//// UIGraphics for UIImage context
CGContextRef context = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(context);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
//// Rectangle Drawing
UIBezierPath *rectanglePath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius];
[backgroundColor setFill];
[rectanglePath fill];
[borderColor setStroke];
rectanglePath.lineWidth = 1.4;
[rectanglePath stroke];
//// UIBezierPath to Image
CGContextAddPath(context, rectanglePath.CGPath);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsPopContext();
UIGraphicsEndImageContext();
return image;
Image:
Here's what a lineWidth of 1 and width of 60 looks like, it still seems a little thick:
For those of you following along at home:
Thanks to the helpful #PixelCutCompany I need to inset the bounds for the UIBezierPath so that the border doesn't exceed the frame of the image I am drawing.
Modified the code as follows for a lineWidth of 1.
CGRect boundedRectForRadius = CGRectMake(1, 1, 58, 98);
UIBezierPath *rectanglePath = [UIBezierPath bezierPathWithRoundedRect:boundedRectForRadius cornerRadius:cornerRadius];
Without this, it was cropping the image as so:
The problem also can be in shift real screen pixels coordinates.
Please check this discussion for more details:
Core Graphics is not drawing a line at the correct width
This question already has answers here:
Drawing rounded rect in core graphics
(2 answers)
Closed 8 years ago.
I'm using following code inside UIView drawRect to draw rectangle with round corners:
CGContextRef context = UIGraphicsGetCurrentContext();
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:5];
CGContextSetFillColorWithColor(context, _fillColor.CGColor);
[path fill];
CGContextSetLineWidth(context, 1);
CGContextSetStrokeColorWithColor(context, _strikeColor.CGColor);
[path stroke];
For some reason I'm getting artefact in stroke: bold corners.
Looks not good. I also tried to have independent path for stroke - same problem.
Think that the corners are right, but stroke goes out of view. Try:
rect = CGRectInset(self.bounds, 0.5, 0.5)
Before creating path.
//// Rectangle Drawing
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(57, 48, 136, 31) byRoundingCorners: UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii: CGSizeMake(5, 5)];
[rectanglePath closePath];
[UIColor.grayColor setFill];
[rectanglePath fill];
I have a question about Quartz and clipping area:
I would like to have a rectangle A
inside this rectangle I would like to have a rectangle B
The filling of B dveve also cut in A: I would like that A was pierced by B. What is the best way to do this in quartz? I did not really understand how the clipping
If I understand you correctly, you want to draw a smaller rectangle inside a larger rectangle so that the inner rectangle is transparent. You can achieve this by drawing a CAShapeLayer with a path that contains both rectangles as subpaths. Don't forget to set the layer's fill rule to kCAFillRuleEvenOdd.
Try something like this:
CGRect rectA = CGRectMake(100, 100, 200, 200);
CGRect rectB = CGRectMake(150, 150, 100, 100);
UIBezierPath *path=[[UIBezierPath alloc] init];
// Add sub-path for rectA
[path moveToPoint:CGPointMake(rectA.origin.x, rectA.origin.y)];
[path addLineToPoint:CGPointMake(rectA.origin.x+rectA.size.width, rectA.origin.y)];
[path addLineToPoint:CGPointMake(rectA.origin.x+rectA.size.width, rectA.origin.y+rectA.size.height)];
[path addLineToPoint:CGPointMake(rectA.origin.x, rectA.origin.y+rectA.size.height)];
[path closePath];
// Add sub-path for rectB
[path moveToPoint:CGPointMake(rectB.origin.x, rectB.origin.y)];
[path addLineToPoint:CGPointMake(rectB.origin.x+rectB.size.width, rectB.origin.y)];
[path addLineToPoint:CGPointMake(rectB.origin.x+rectB.size.width, rectB.origin.y+rectB.size.height)];
[path addLineToPoint:CGPointMake(rectB.origin.x, rectB.origin.y+rectB.size.height)];
[path closePath];
// Create CAShapeLayer with this path
CAShapeLayer *pathLayer = [CAShapeLayer layer];
[pathLayer setFillRule:kCAFillRuleEvenOdd]; /* <- IMPORTANT! */
[pathLayer setPath:path.CGPath];
[pathLayer setFillColor:[UIColor blackColor].CGColor];
// Add the CAShapeLayer to a view
[someView.layer addSublayer:pathLayer];
I solved with this simple way:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, 0);
CGContextFillRect(context,self.bounds);
CGContextAddRect(context, self.bounds);
//Add cropped rectangle:
CGContextAddRect(context, _croppedRegion);
//Clip:
CGContextEOClip(context);
CGContextSetRGBFillColor(context, 255.0, 255.0, 255.0, 0.5);
CGContextFillRect(context, self.bounds);
}