CAShapeLayer shadow - ios

Given the following CAShapeLayer is it possible to add a drop shadow like the following image at the tip?
I'm using a UIBezierPath to draw the bars.
- (CAShapeLayer *)gaugeCircleLayer {
if (_gaugeCircleLayer == nil) {
_gaugeCircleLayer = [CAShapeLayer layer];
_gaugeCircleLayer.lineWidth = self.gaugeWidth;
_gaugeCircleLayer.fillColor = [UIColor clearColor].CGColor;
_gaugeCircleLayer.strokeColor = self.gaugeTintColor.CGColor;
_gaugeCircleLayer.strokeStart = 0.0f;
_gaugeCircleLayer.strokeEnd = self.value;
_gaugeCircleLayer.lineCap = kCALineCapRound;
_gaugeCircleLayer.masksToBounds = NO;
_gaugeCircleLayer.cornerRadius = 8.0;
_gaugeCircleLayer.shadowRadius = 8.0;
_gaugeCircleLayer.shadowColor = [UIColor blackColor].CGColor;
_gaugeCircleLayer.shadowOpacity = 0.5;
_gaugeCircleLayer.shadowOffset = CGSizeMake(0.0, 0.0);
_gaugeCircleLayer.path = [self circlPathForCurrentGaugeStyle].CGPath;
}
return _gaugeCircleLayer;
}
It will need to be applied to this UIBezierPath:
- (UIBezierPath *)insideCirclePath {
CGPoint arcCenter = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:arcCenter
radius:CGRectGetWidth(self.bounds) / 2.0f
startAngle:(3.0f * M_PI_2)
endAngle:(3.0f * M_PI_2) + (2.0f * M_PI)
clockwise:YES];
_titleTextLabel.textColor = self.gaugeTintColor;
return path;
}

Very roughly:
let arc1 = CAShapeLayer()
arc1.lineWidth = 20.0
arc1.path = UIBezierPath(ovalInRect: CGRectMake(10, 10, 80, 80)).CGPath
arc1.strokeStart = 0
arc1.strokeEnd = 0.5
arc1.strokeColor = UIColor.grayColor().CGColor
arc1.fillColor = UIColor.clearColor().CGColor
layer.addSublayer(arc1)
let cap = CAShapeLayer()
cap.shadowColor = UIColor.blackColor().CGColor
cap.shadowRadius = 8.0
cap.shadowOpacity = 0.9
cap.shadowOffset = CGSize(width: 0, height: 0)
cap.path = UIBezierPath(ovalInRect: CGRectMake(0, 40, 20, 20)).CGPath
cap.fillColor = UIColor.grayColor().CGColor
layer.addSublayer(cap)
let arc2 = CAShapeLayer()
arc2.lineWidth = 20.0
arc2.path = UIBezierPath(ovalInRect: CGRectMake(10, 10, 80, 80)).CGPath
arc2.strokeStart = 0.5
arc2.strokeEnd = 1.0
arc2.strokeColor = UIColor.grayColor().CGColor
arc2.fillColor = UIColor.clearColor().CGColor
layer.addSublayer(arc2)
I think you need two arcs, and one circle for cap with shadow.

Related

How to create multicolor border in UIImageView?

I want to achieve Imageview like-
So far I am using:
roundedImage.layer.cornerRadius = roundedImage.frame.width/2
roundedImage.layer.masksToBounds = true
roundedImage.layer.borderWidth = 2.0
roundedImage.layer.borderColor = UIColor.red.cgColor
But it's giving me
,
How to accomplish multi-color rounded border ?
The easiest way that comes in mind is creating a rounded background view colored with a gradient and put your imageView on top with a 1px white border
let gradient = CAGradientLayer()
gradient.frame = CGRect(origin: CGPoint.zero, size: self.roundedImage.frame.size)
gradient.colors = [UIColor.blue.cgColor, UIColor.green.cgColor]
let shape = CAShapeLayer()
shape.lineWidth = 2
shape.path = UIBezierPath(rect: roundedImage.bounds).cgPath
shape.strokeColor = UIColor.black.cgColor
shape.fillColor = UIColor.clear.cgColor
gradient.mask = shape
roundedImage.layer.addSublayer(gradient)
Try creating extension to CALayer.
It will work across app at other places too.
I have set color as red and pink. Please adjust other values as per your need
Objective C :
#implementation CALayer(Border)
-(void) addGradientBorderColor {
CAGradientLayer *objgradientLayer = [[CAGradientLayer alloc] init];
objgradientLayer.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
objgradientLayer.startPoint = CGPointMake(0.0, 0.5);
objgradientLayer.endPoint = CGPointMake(1.0, 0.5);
objgradientLayer.colors = #[(id)[UIColor redColor].CGColor, (id)
[UIColor colorWithRed:(255/255.0) green:(192/255.0) blue:(203/255.0) alpha:1].CGColor];
CAShapeLayer *objShapeLayer =[[CAShapeLayer alloc] init];
objShapeLayer.lineWidth = 0.5;
objShapeLayer.path = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;
objShapeLayer.fillColor = nil;
objShapeLayer.strokeColor = [UIColor whiteColor].CGColor;
objgradientLayer.mask = objShapeLayer;
[self addSublayer : objgradientLayer];
}
#end
Swift :
extension CALayer {
func addGradienBorder(colors:[UIColor] = [UIColor.redColor().CGColor,UIColor(red: 255/255, green: 192/255, blue: 203/255, alpha: 1).CGColor],width:CGFloat = 1) {
let objgradientLayer = CAGradientLayer()
objgradientLayer = CGRect(origin: CGPointZero, size: self.bounds.size)
objgradientLayer = CGPointMake(0.0, 0.5)
objgradientLayer = CGPointMake(1.0, 0.5)
objgradientLayer = colors.map({$0.CGColor})
let objShapeLayer = CAShapeLayer()
objShapeLayer.lineWidth = 1
objShapeLayer.path = UIBezierPath(rect: self.bounds).CGPath
objShapeLayer.fillColor = UIColor.clear.cgColor
objShapeLayer.strokeColor = UIColor.blackColor().CGColor
objgradientLayer.mask = objShapeLayer
self.addSublayer(objgradientLayer)
}
}
Hope that helps !

Drop shadow in ios

How to drop an object shadow in iOS?
My object is UIImageView and i want to drop an elliptical shadow.Please refer image for reference.
Better you use another image for showing shadow. Use blur image or change alpha of the imageview.
Or if you want to do it programmatically, try it:
Obj c:
//create elliptical shadow for image through UIBezierPath
CGRect ovalRect = CGRectMake(0.0f, _imageView.frame.size.height + 10, _imageView.frame.size.width, 15);
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:ovalRect];
//applying shadow to path
_imageView.layer.shadowColor = kShadowColor.CGColor;
_imageView.layer.shadowOffset = CGSizeMake(0.0, 0.0);
_imageView.layer.shadowOpacity = 1.0;
_imageView.layer.shadowRadius = 3.0;
_imageView.layer.shadowPath = path.CGPath;
Swift :
//create elliptical shdow forimage through UIBezierPath
var ovalRect = CGRectMake(0.0, imageView.frame.size.height + 10, imageView.frame.size.width, 15)
var path = UIBezierPath(ovalInRect: ovalRect)
//applying shadow to path
imageView.layer.shadowColor = UIColor(white: 0.0, alpha: 0.5).CGColor
imageView.layer.shadowOffset = CGSizeMake(0.0, 0.0)
imageView.layer.shadowOpacity = 1.0
imageView.layer.shadowRadius = 3.0
imageView.layer.shadowPath = path.CGPath
Output:
Taken from http://www.innofied.com/implementing-shadow-ios/ and also have a look to know more: UIView with rounded corners and drop shadow?
You can use CAShapeLayer like this:
Objective-C:
// init CAShapeLayer
CAShapeLayer *shadowLayer = [CAShapeLayer layer];
shadowLayer.frame = CGRectMake(CGRectGetMinX(_imageView.frame), CGRectGetMaxY(_imageView.frame), _imageView.frame.size.width, _imageView.frame.size.height);
shadowLayer.path = [UIBezierPath bezierPathWithOvalInRect:shadowLayer.bounds].CGPath;
shadowLayer.fillColor = [UIColor colorWithWhite:0 alpha:0.02].CGColor;
shadowLayer.lineWidth = 0;
[self.view.layer addSublayer: shadowLayer];
Swift 3
let shadowLayer = CAShapeLayer()
shadowLayer.frame = CGRect(x: imageView.frame.minX, y: imageView.frame.maxY, width: imageView.frame.width, height: imageView.frame.height * 0.25)
shadowLayer.path = UIBezierPath(ovalIn: shadowLayer.bounds).cgPath
shadowLayer.fillColor = UIColor(white: 0, alpha: 0.02).cgColor
shadowLayer.lineWidth = 0
view.layer.addSublayer(shadowLayer)
Output:

how to control strokeEnd animation Line Speed

I want to create an atomic motion animation in iOS, but there are some trouble could not solve.
here is my animation result
CAKeyframeAnimation position animation along bezierPath does not match the CABasicAnimation strokeEnd animation(particles animation does not match their wake animation).
2.The wake animation loop is incoherent, CABasicAnimation seems can't represent the Curving segment that strokeStart is positive and strokeEnd is negative.
my code:
let atom = CAShapeLayer()
var rect = CGRect(x: 0,y: 0,width: 5,height: 5)
var path = UIBezierPath(roundedRect: rect,cornerRadius: 5.0)
atom.path = path.CGPath;
atom.strokeColor = UIColor.greenColor().CGColor
atom.bounds = rect;
rect = CGRect(x: 0,y: 0,width: 300,height: 50);
path = UIBezierPath(ovalInRect:rect);
let orbit = CAShapeLayer()
orbit.path = path.CGPath;
orbit.strokeColor = UIColor.whiteColor().CGColor
orbit.lineWidth = 1;
orbit.fillColor = UIColor.clearColor().CGColor
orbit.bounds = CGPathGetPathBoundingBox(path.CGPath);
orbit.position = CGPoint(x:150,y:25)
rect = CGRect(x: 0,y: 0,width: 300,height: 50);
path = UIBezierPath(ovalInRect:rect);
let keyani = CAKeyframeAnimation(keyPath: "position");
keyani.path = path.CGPath
keyani.duration = 3.0;
keyani.repeatCount = HUGE;
keyani.additive = true
keyani.calculationMode = kCAAnimationPaced
keyani.rotationMode = kCAAnimationRotateAuto
keyani.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionLinear);
atom.addAnimation(keyani, forKey: "")
let orbitAni = CABasicAnimation(keyPath: "strokeStart")
orbitAni.byValue = 1.0
orbitAni.cumulative = true
let orbitAni1 = CABasicAnimation(keyPath: "strokeEnd")
orbitAni1.fromValue = 0.0
orbitAni1.toValue = 1.0
let animationGroup = CAAnimationGroup()
animationGroup.animations = [orbitAni,orbitAni1]
animationGroup.duration = 3.0
animationGroup.repeatCount = HUGE
orbit.strokeStart = -0.2
orbit.addAnimation(animationGroup, forKey: "");
orbit.shadowRadius = 5.0;
orbit.shadowColor = UIColor.greenColor().CGColor
orbit.shadowOpacity = 0.5
orbit.shadowOffset = CGSizeZero
var trans = CGAffineTransformIdentity
trans = CGAffineTransformRotate(trans, CGFloat(M_PI_4))
repeator.instanceTransform = CATransform3DMakeAffineTransform (trans)
repeator.instanceCount = 4;
repeator.instanceDelay = 0;
repeator.addSublayer(orbit)
repeator.addSublayer(atom)
repeator.bounds = CGRect(x: 0,y: 0,width: 300,height: 50)
repeator.position = CGPoint(x:200,y:200)
repeator.anchorPoint = CGPoint(x:0.5,y:0.5)
repeator.instanceAlphaOffset = 1.0

Swift: add custom view inside CALayer

I'm trying to add a custom view inside a CALayer.
http://i.imgur.com/sYzQ4kX.png
And I want to put inside some buttons and labels, but I'm afraid I'm not able to do it. I make the CALabel like this:
func addRectangleToLayer(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat, layer: CALayer, index: UInt32 ) {
var sublayer = CALayer()
sublayer.backgroundColor = UIColor.whiteColor().CGColor
sublayer.shadowOffset = CGSizeMake(0, 3);
sublayer.shadowRadius = 5.0;
sublayer.shadowColor = UIColor.blackColor().CGColor
sublayer.shadowOpacity = 0.8;
sublayer.cornerRadius = 12.0;
sublayer.frame = CGRectMake(x, y, width, height);
sublayer.borderColor = UIColor.blackColor().CGColor;
sublayer.borderWidth = 0.5;
//An example
let label = UILabel()
label.text = "LOREN"
sublayer.contents = label
layer.insertSublayer(sublayer, atIndex: index)
}
Is it possible to do what I want to?
Thanks a lot and excuse me for my english level
If you create a UIView, you'll have access to its layer property for this purpose:
let label = UILabel()
label.text = "LOREN"
var sublayer = label.layer;
// .. the rest of your layer initialization
sublayer.backgroundColor = UIColor.whiteColor().CGColor
sublayer.shadowOffset = CGSizeMake(0, 3);
sublayer.shadowRadius = 5.0;
sublayer.shadowColor = UIColor.blackColor().CGColor
sublayer.shadowOpacity = 0.8;
sublayer.cornerRadius = 12.0;
sublayer.frame = CGRectMake(x, y, width, height);
sublayer.borderColor = UIColor.blackColor().CGColor;
sublayer.borderWidth = 0.5;
// .. ended original source initialization
layer.insertSublayer(sublayer, atIndex: index)

Masking CALayer shadow to outside of rect only

How would I mask CALayer with shadow, so that the shadow is only outside the path? I don't want shadow behind a transparent view.
shadowLayer.shadowOffset = CGSizeMake(x, y);
shadowLayer.shadowRadius = radius;
shadowLayer.shadowOpacity = opacity;
shadowLayer.shadowColor = color.CGColor;
shadowLayer.shadowPath = [UIBezierPath bezierPathWithRect:view.bounds].CGPath;
Thank you.
I will answer my own question. Adding a new shadow layer for view. This should work for any shadowPath, if set correctly.
float radius = 8;
float opacity = 0.5f;
float x = 4;
float y = 6;
UIColor *color = [UIColor blackColor];
// Shadow layer
CALayer *shadowLayer = [CALayer layer];
shadowLayer.shadowOffset = CGSizeMake(x, y);
shadowLayer.shadowRadius = radius;
shadowLayer.shadowOpacity = opacity;
shadowLayer.shadowColor = color.CGColor;
shadowLayer.shadowPath = [UIBezierPath bezierPathWithRect:view.frame].CGPath; // Or any other path
// Shadow mask frame
CGRect frame = CGRectInset(view.layer.frame, -2*radius, -2*radius);
frame = CGRectOffset(frame, x, y);
// Translate shadowLayer shadow path to mask layer's coordinate system
CGAffineTransform trans = CGAffineTransformMakeTranslation(-view.frame.origin.x-x+2*radius,
-view.frame.origin.y-y+2*radius);
// Mask path
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, nil, (CGRect){.origin={0,0}, .size=frame.size});
CGPathAddPath(path, &trans, shadowLayer.shadowPath);
CGPathCloseSubpath(path);
// Mask layer
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = frame;
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.path = path;
shadowLayer.mask = maskLayer;
[view.layer.superlayer insertSublayer:shadowLayer below:view.layer];
ssteinberg's answer helped me in iOS 10.2 and Swift 3. I was able to use his answer with a UIVisualEffectView to create a beautiful blur but also with a shadow. Converted to Swift 3 here:
let shadowLayer = CALayer()
let mutablePath = CGMutablePath()
let maskLayer = CAShapeLayer()
let xOffset = CGFloat(4)
let yOffset = CGFloat(6)
let shadowOffset = CGSize(width: xOffset, height: yOffset)
let shadowOpacity = Float(0.5)
let shadowRadius = CGFloat(8)
let shadowPath = UIBezierPath(rect: frame).cgPath
let shadowColor = UIColor.black
let shadowFrame = frame.insetBy(dx: -2 * shadowRadius, dy: -2 * shadowRadius).offsetBy(dx: xOffset, dy: yOffset)
let shadowRect = CGRect(origin: .zero, size: shadowFrame.size)
let shadowTransform = CGAffineTransform(translationX: -frame.origin.x - xOffset + 2 * shadowRadius, y: -frame.origin.y - yOffset + 2 * shadowRadius)
shadowLayer.shadowOffset = shadowOffset
shadowLayer.shadowOpacity = shadowOpacity
shadowLayer.shadowRadius = shadowRadius
shadowLayer.shadowPath = shadowPath
shadowLayer.shadowColor = shadowColor.cgColor
mutablePath.addRect(shadowRect)
mutablePath.addPath(shadowLayer.shadowPath!, transform: shadowTransform)
mutablePath.closeSubpath()
maskLayer.frame = shadowFrame
maskLayer.fillRule = kCAFillRuleEvenOdd
maskLayer.path = mutablePath
shadowLayer.mask = maskLayer
layer.superlayer?.insertSublayer(shadowLayer, above: layer)
Just an advance: u can use CAShapeLayer to set shadow
let shapeLayer = CAShapeLayer()
shapeLayer.lineWidth = 2
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.purple.cgColor
shapeLayer.shadowColor = UIColor.black.cgColor
shapeLayer.shadowOffset = CGSize(width: 0, height: 3)
shapeLayer.shadowOpacity = 1
view.layer.addSublayer(shapeLayer)

Resources