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.
Related
how can I create a label below like this image in iOS (Objective-c)?
My problem is not all corners, because I can round top-left & bottom-right corners by writing codes like this:
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = [UIBezierPath bezierPathWithRoundedRect:self.customView.bounds
byRoundingCorners:UIRectCornerBottomRight | UIRectCornerTopLeft
cornerRadii:(CGSize){7.0, 7.0}].CGPath;
self.customLabel.layer.mask = maskLayer;
and the result is this:
Now how can I write codes for top-right & bottom-left to reach the goal?
Update:
I didn't use the height as a variable in my equations it may affect the results.
Better results
- (void)drawRect:(CGRect)rect
{
CGFloat curveWidth = 40;
CGFloat width = self.frame.size.width;
CGFloat height = self.frame.size.height;
// UIEdgeInsets insets = {0, curveWidth, 0, curveWidth};
// [super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
UIBezierPath *path = [UIBezierPath bezierPath];
UIColor *fillColor = [UIColor orangeColor];
[fillColor setFill];
[path moveToPoint:CGPointMake(0, height)];
[path addCurveToPoint:CGPointMake(curveWidth*2, 0) controlPoint1:CGPointMake(curveWidth*0.75, height) controlPoint2:CGPointMake(curveWidth*0.25, 0)];
[path addLineToPoint:CGPointMake(width, 0)];
[path addCurveToPoint:CGPointMake(width-curveWidth*2, height) controlPoint1:CGPointMake(width-curveWidth*0.75, 0) controlPoint2:CGPointMake(width-curveWidth*0.25, height)];
[path closePath];
[path fill];
[super drawRect: rect];
}
Original Answer:
I wrote the code for you. follow the steps:
Create a custom UILabel by subclassing it. (code below)
Add a UILabel to your view using interface builder.
Add your constraints.
In the Identifier inspector tab, change class to your.
--
#implementation CurvedLabel
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGFloat curveWidth = 30;
CGFloat width = self.frame.size.width;
CGFloat height = self.frame.size.height;
// UIEdgeInsets insets = {0, curveWidth, 0, curveWidth};
// [super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
UIBezierPath *path = [UIBezierPath bezierPath];
UIColor *fillColor = [UIColor orangeColor];
[fillColor setFill];
[path moveToPoint:CGPointMake(0, height)];
[path addCurveToPoint:CGPointMake(curveWidth*2, 0) controlPoint1:CGPointMake(curveWidth, height) controlPoint2:CGPointMake(0, 0)];
[path addLineToPoint:CGPointMake(width, 0)];
[path addCurveToPoint:CGPointMake(width-curveWidth*2, height) controlPoint1:CGPointMake(width-curveWidth, 0) controlPoint2:CGPointMake(width, height)];
[path closePath];
[path fill];
[super drawRect: rect];
}
#end
I don't believe there is any simple way of doing that, you could use a png file, if that is not a option you should look in to UIBezierPath, check this link out: ios drawing concepts
Sure you can do it with a UIBezierPath.
I have made a pretty simple setup you can use.
// This is the parallelogram's corner structure
// 1------2
// / /
// / /
// 4------3
int xOffset = 150; // x Offset on the parent view - here self.view
int yOffset = 50; // y Offset on the parent view - here self.view
int flatten = 50; // The difference from center of curve to spike point (try to adjust it and see what it does)
int width = 200;
int height = 100;
UIBezierPath * maskLayer = [UIBezierPath bezierPath];
// Start at corner 1
[maskLayer moveToPoint: CGPointMake(xOffset+flatten, yOffset)];
// Go to corner 2
[maskLayer addLineToPoint: CGPointMake(xOffset+width+flatten, yOffset)];
// Curve it down to corner 3
[maskLayer addCurveToPoint: CGPointMake(xOffset+width-flatten, height+yOffset) controlPoint1: CGPointMake(xOffset+width, yOffset) controlPoint2:CGPointMake(xOffset+width, height+yOffset)];
// Go straight to corner 4
[maskLayer addLineToPoint: CGPointMake(xOffset, height+yOffset)];
// Curve it back to corner 1
[maskLayer addCurveToPoint: CGPointMake(xOffset+(2*flatten), yOffset) controlPoint1:CGPointMake(xOffset+flatten, yOffset+height) controlPoint2: CGPointMake(xOffset+flatten, yOffset)];
// Create the shape and fill it with a color
CAShapeLayer *maskShapeLayer = [CAShapeLayer layer];
maskShapeLayer.path = maskLayer.CGPath;
maskShapeLayer.fillColor = [UIColor redColor].CGColor;
// add the layer to a view's layer (here self.view.layer)
[self.view.layer addSublayer:maskShapeLayer];
If you wanna make the two ´spikes´ more rounded, you can make the y-value in the addCurveToPoint points closer to the parallelogram's y-center-axis (height/2).
You will have to useaddCurveToPoint of UIBezierPath to make it curvy by giving it controlPoint1 and controlPoint2
For reference, you can look at Apple's Documentation
Also, this is a helpful reference too.
You will have to play around with UIBezierPath's properties
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.
I am applying corner radius to a UIView i.e. UIRectCornerTopLeft and UIRectCornerTopRight. When I apply this, the border is gone at the corners. How to avoid this?
This is how I apply border to UIView:
[self.middleView addRoundedCorners:UIRectCornerTopLeft|UIRectCornerTopRight withRadii:CGSizeMake(4, 4)];
self.middleView.layer.borderWidth = 0.5f;
self.middleView.layer.borderColor = [[UIColor colorWith8BitRed:0 green:0 blue:0 alpha:0.25]
And this is a category I am using for applying optional rounded corners:
#import "UIView+Roundify.h"
#implementation UIView (Roundify)
- (void)addRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii {
CALayer *tMaskLayer = [self maskForRoundedCorners:corners withRadii:radii];
self.layer.mask = tMaskLayer;
}
- (CALayer*)maskForRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii {
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = self.bounds;
UIBezierPath *roundedPath = [UIBezierPath bezierPathWithRoundedRect:
maskLayer.bounds byRoundingCorners:corners cornerRadii:radii];
maskLayer.fillColor = [[UIColor whiteColor] CGColor];
maskLayer.backgroundColor = [[UIColor clearColor] CGColor];
maskLayer.path = [roundedPath CGPath];
return maskLayer;
}
Try below code it work
Your view which you want to rounded TopLeft and TopRight
UIView *view1 = [[UIView alloc]initWithFrame:CGRectMake(50, 100, 100, 100)];
[view1 setBackgroundColor:[UIColor grayColor]];
[self.view addSubview:view1];
Set Corner as below code
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:view1.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(5.0, 5.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.view.bounds;
maskLayer.path = maskPath.CGPath;
view1.layer.mask = maskLayer;
OUTPUT IS:
Found this piece of code. Have not actually tried it, but seems like it is what you need.
- (void)drawDashedBorderAroundView:(UIView *)v {
//border definitions
CGFloat cornerRadius = 10;
CGFloat borderWidth = 2;
NSInteger dashPattern1 = 8;
NSInteger dashPattern2 = 8;
UIColor *lineColor = [UIColor orangeColor];
//drawing
CGRect frame = v.bounds;
CAShapeLayer *_shapeLayer = [CAShapeLayer layer];
//creating a path
CGMutablePathRef path = CGPathCreateMutable();
//drawing a border around a view
CGPathMoveToPoint(path, NULL, 0, frame.size.height - cornerRadius);
CGPathAddLineToPoint(path, NULL, 0, cornerRadius);
CGPathAddArc(path, NULL, cornerRadius, cornerRadius, cornerRadius, M_PI, -M_PI_2, NO);
CGPathAddLineToPoint(path, NULL, frame.size.width - cornerRadius, 0);
CGPathAddArc(path, NULL, frame.size.width - cornerRadius, cornerRadius, cornerRadius, -M_PI_2, 0, NO);
CGPathAddLineToPoint(path, NULL, frame.size.width, frame.size.height - cornerRadius);
CGPathAddArc(path, NULL, frame.size.width - cornerRadius, frame.size.height - cornerRadius, cornerRadius, 0, M_PI_2, NO);
CGPathAddLineToPoint(path, NULL, cornerRadius, frame.size.height);
CGPathAddArc(path, NULL, cornerRadius, frame.size.height - cornerRadius, cornerRadius, M_PI_2, M_PI, NO);
//path is set as the _shapeLayer object's path
_shapeLayer.path = path;
CGPathRelease(path);
_shapeLayer.backgroundColor = [[UIColor clearColor] CGColor];
_shapeLayer.frame = frame;
_shapeLayer.masksToBounds = NO;
[_shapeLayer setValue:[NSNumber numberWithBool:NO] forKey:#"isCircle"];
_shapeLayer.fillColor = [[UIColor clearColor] CGColor];
_shapeLayer.strokeColor = [lineColor CGColor];
_shapeLayer.lineWidth = borderWidth;
_shapeLayer.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:dashPattern1], [NSNumber numberWithInt:dashPattern2], nil];
_shapeLayer.lineCap = kCALineCapRound;
//_shapeLayer is added as a sublayer of the view, the border is visible
[v.layer addSublayer:_shapeLayer];
v.layer.cornerRadius = cornerRadius;
}
This piece of code adds a dashed line, but you can modify that by _shapeLayer.lineDashPattern.
Unless there is some specific requirement which we're not aware of, the bezier path and mask are unnecessary if all you're trying to do is round the corners and have a border. I would normally just do this:
myView.layer.borderWidth=2;
myView.layer.cornerRadius=5;
Is it that you only want the top corners rounded that you need to not use the layer rounding? If so, why not use that and then overlay a thin view to cover the bottom bit? A bit fiddly, but I find that the more you can rely on the standard controls to draw themselves rather than having to step into core graphics, the better.
Edit: ok, given that it needs to have the bottom corners not rounded, how about if you had a category on UIView with 2 subviews: 1 with 4 rounded corners and another layed over the top (self bringSubviewToFront) which simply covers the rounded view's "footer" with a non-rounded strip, ie a view with equal width and tiny height which is equal to the rounded corner radius. If you have a solid color background then just make both subviews the same; if you have some texture or image background, make them both transparent and put the texture/image on the super view (the parent view who is using your category's specific layout method). Then finally, put the border on that same superview. Should work, let me know what you think.
I am trying to add inner shadow on few UITextField and UITextView. I have set cornerRadius property for these controls to 1.0. and I am using the code which is mentioned below to create the inner shadow. But problem is that the shadow appears with the steep rectangular corners while the text view and the text field are with rounded corners.
I am calling following method in my viewcontroller class to add the shadow on my text field.
[self addInnerShadow:myTextField.frame];
- (void)addInnerShadow:(CGRect)frame
{
CAShapeLayer* shadowLayer = [CAShapeLayer layer];
[shadowLayer setFrame:frame];
[shadowLayer setShadowColor:[[UIColor blackColor] CGColor]];
[shadowLayer setShadowOffset:CGSizeMake(0.0f, 0.0f)];
[shadowLayer setShadowOpacity:1.0f];
[shadowLayer setShadowRadius:5];
[shadowLayer setFillRule:kCAFillRuleEvenOdd];
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectInset(CGRectMake(0, 0, frame.size.width, frame.size.height),-5, -5));
CGMutablePathRef someInnerPath = CGPathCreateMutable();
CGPathAddRect(someInnerPath, NULL, CGRectInset(CGRectMake(0, 0, frame.size.width, frame.size.height), 0, 0));
CGPathAddPath(path, NULL, someInnerPath);
CGPathCloseSubpath(path);
[shadowLayer setPath:path];
CGPathRelease(path);
CAShapeLayer* maskLayer = [CAShapeLayer layer];
[maskLayer setPath:someInnerPath];
[shadowLayer setMask:maskLayer];
[[self.view layer] addSublayer:shadowLayer];
}
This
CGContextRef context = UIGraphicsGetCurrentContext();
// Shadow
UIColor* shadow4 = [[UIColor blackColor] colorWithAlphaComponent: 0.81];
CGSize shadow4Offset = CGSizeMake(0.1, 2.1);
CGFloat shadow4BlurRadius = 5;
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(20.5, 26.5, 200, 40) cornerRadius: 4];
[[UIColor whiteColor] setFill];
[rectanglePath fill];
CGRect rectangleBorderRect = CGRectInset([rectanglePath bounds], -shadow4BlurRadius, - shadow4BlurRadius);
rectangleBorderRect = CGRectOffset(rectangleBorderRect, -shadow4Offset.width, - shadow4Offset.height);
rectangleBorderRect = CGRectInset(CGRectUnion(rectangleBorderRect, [rectanglePath bounds]), -1, -1);
UIBezierPath* rectangleNegativePath = [UIBezierPath bezierPathWithRect: rectangleBorderRect];
[rectangleNegativePath appendPath: rectanglePath];
rectangleNegativePath.usesEvenOddFillRule = YES;
CGContextSaveGState(context);
{
CGFloat xOffset = shadow4Offset.width + round(rectangleBorderRect.size.width);
CGFloat yOffset = shadow4Offset.height;
CGContextSetShadowWithColor(context,
CGSizeMake(xOffset + copysign(0.1, xOffset), yOffset + copysign(0.1, yOffset)),
shadow4BlurRadius,
shadow4.CGColor);
[rectanglePath addClip];
CGAffineTransform transform = CGAffineTransformMakeTranslation(-round(rectangleBorderRect.size.width), 0);
[rectangleNegativePath applyTransform: transform];
[[UIColor grayColor] setFill];
[rectangleNegativePath fill];
}
CGContextRestoreGState(context);
[[UIColor blackColor] setStroke];
rectanglePath.lineWidth = 0.5;
[rectanglePath stroke];
Will produce this. You can manipulate this to get your desired effect.
Create path with UIBezierPath
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:frame
byRoundingCorners:UIRectCornerAllCorners
cornerRadii:CGSizeMake(5.0, 5.0)];
And than use it with Layer.
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);
}