How to add label or text in to CAShapeLayer - ios

Here is my class and it'll draw a circle, it looks like this:
class OvalLayer: CAShapeLayer {
let animationDuration: CFTimeInterval = 0.3
override init() {
super.init()
fillColor = Colors.green.CGColor
path = ovalPathSmall.CGPath
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var ovalPathStart: UIBezierPath {
let path = UIBezierPath(ovalInRect: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
return path
}
}
Now I need to add a text to middle of this circle, I tried to find it on google but nothing that works fine. I am not sure if it's possible or not, can anyone help me if it's possible?

I guess you should add CATextLayer as a sublayer to CALayer... That works fine that way: try adding CAShapeLayer first, and then CATextLayer (to same CALayer parent layer), for example in following order...
// assume self - is UIView instance
self.layer.addSublayer(shapedLayer) // shapedLayer - CAShapeLayer instance
self.layer.addSublayer(textLayer) // textLayer - CATextLayer instance

Swift 3.0
let label = UILabel()
label.font = UIFont(name: "Helvetica-Bold", size: 12)
label.frame = CGRect(x: OvalLayer.frame.origin.x + (circleWidth/2), y: OvalLayer.frame.origin.y, width: OvalLayer.bounds.width, height: OvalLayer.bounds.height)
label.text = "Hello"
label.textColor = UIColor.red
label.isHidden = false
OvalLayer.addSublayer(label.layer)

You just need to get the center of your UIBezierPath and add a label or a CATextLayer to your current layer.
let center : CGPoint = CGPoint(x: CGRectGetMidX(ovalPathStart.bounds), y: CGRectGetMidX(ovalPathStart.bounds))
Now, create a UILabel or CATextLayer and set the center.

Text on CAShapeLayer Using CATextLayer
Here i am create CAShapeLayer object and i am adding CATextLayer to CAShapeLayer
Here numberOfArcsCount means some 8
CAShapeLayer *progressLayer = [[CAShapeLayer alloc] init];
[progressLayer setPath:bezierPath.CGPath];
CATextLayer* text = [CATextLayer new];
for (int i = 1; i <= numberOfArcs.count; i++) {
text.string = [NSString stringWithFormat:#"%i", i];
text.font = (__bridge CFTypeRef _Nullable)([UIFont fontWithName:#"akaChen" size:42]);
text.font = (__bridge CFTypeRef _Nullable)([UIFont boldSystemFontOfSize:15]);
text.fontSize=25;
text.frame = CGRectMake(0,0,40,40);
text.position = CGPointMake(CGRectGetMidX(progressLayer.frame) ,CGRectGetMidY(progressLayer.frame) );
CGFloat vert = CGRectGetMidY(progressLayer.frame) / CGRectGetHeight(text.frame);
text.anchorPoint = CGPointMake(0.5, vert );
text.alignmentMode = kCAAlignmentCenter;
text.foregroundColor = [[UIColor whiteColor] CGColor];
[progressLayer addSublayer:text];
}

This way to add label to layer :
labelFrame - CGRect
someString - String
layer - your layer
let label = CATextLayer()
label.frame = labelFarme
label.string = someString
layer.addSublayer(label)

Related

Mask of layer mask. Core animation

I deep into the Core Animation and have some problem with layer masking
I try to make a complex UI layer with transparent things for study reasons
This view a have and put into the ViewController
public class TestView : UIView
{
public TestView()
{
Layer.Delegate = this;
Frame = new CGRect(100f,200f, 100f, 100f);
}
public override void LayoutSublayersOfLayer(CALayer layer)
{
base.LayoutSublayersOfLayer(layer);
var bounds = Bounds;
var circleFrame = new CGRect(20, 20, 60, 60);
var imageFrame = new CGRect(30, 30, 40, 40);
Layer.BorderWidth = 5;
Layer.BorderColor = UIColor.Red.CGColor;
Layer.CornerRadius = 4;
var gradientLayer = new CAGradientLayer();
gradientLayer.Colors = new[] { UIColor.Green.CGColor, UIColor.Red.CGColor };
gradientLayer.Frame = bounds;
Layer.AddSublayer(gradientLayer);
var maskOfGradientLayer = new CAShapeLayer();
var path = UIBezierPath.FromRoundedRect(circleFrame, 30);
path.UsesEvenOddFillRule = true;
maskOfGradientLayer.Path = path.CGPath;
maskOfGradientLayer.FillRule = CAShapeLayer.FillRuleEvenOdd;
maskOfGradientLayer.FillColor = UIColor.Black.CGColor;
maskOfGradientLayer.Frame = bounds;
gradientLayer.Mask = maskOfGradientLayer;
var maskOfMaskLayer = new CAShapeLayer();
maskOfMaskLayer.Frame = imageFrame;
maskOfMaskLayer.FillColor = UIColor.Black.CGColor;
maskOfMaskLayer.Contents = Bundles.ImageOff.CGImage;
Layer.AddSublayer(maskOfMaskLayer);
}
}
And that I have and this is exactly what I wanted.
But I also want can to make transparent in circle instead black color.
I tried to make like this maskOfGradientLayer.Mask = maskOfMaskLayer.
But this way maskOfMaskLayer do nothing! It's not working like mask must do.
What I should to do?
But I also want can to make transparent in circle instead black color.
Solution Exploratory Version:
You can also use UIBezierPath to draw what you want, and set to maskOfMaskLayer:
var maskOfMaskLayer = new CAShapeLayer();
maskOfMaskLayer.Frame = imageFrame;
maskOfMaskLayer.FillColor = UIColor.Black.CGColor;
maskOfMaskLayer.Contents = Bundles.ImageOff.CGImage;
Layer.AddSublayer(maskOfMaskLayer);
Up code repalced with follow:
UIBezierPath offCirclePath = new UIBezierPath();
// draw an arc
offCirclePath.AddArc(new CGPoint(50, 50), circleFrame.Width / 3, (nfloat)(1.75 * Math.PI), (nfloat)(1.25 * Math.PI), true);
//draw a line
offCirclePath.MoveTo(new CGPoint(50, 30));
offCirclePath.AddLineTo(new CGPoint(50, 45));
//set to maskOfMaskLayer
var maskOfMaskLayer = new CAShapeLayer();
maskOfMaskLayer.FillColor = UIColor.Clear.CGColor;
maskOfMaskLayer.Path = offCirclePath.CGPath;
maskOfMaskLayer.LineWidth = 5;
maskOfMaskLayer.StrokeColor = UIColor.White.CGColor;
maskOfMaskLayer.LineCap = new NSString("round");
Layer.AddSublayer(maskOfMaskLayer);
Other code not changed.
But I from starts thought about setting any custom image.
Solution Two:(Wrong direction)
I have tested that CAShapeLayer can not have this function to change color from image. CAShapeLayer Class
However , I found UIImageView Can do that.There is a TintColor propert of it.This property is from UiView .UIView.TintColor Property.
var maskOfMaskLayer = new CAShapeLayer();
maskOfMaskLayer.Frame = imageFrame;
maskOfMaskLayer.FillColor = UIColor.Black.CGColor;
maskOfMaskLayer.Contents = Bundles.ImageOff.CGImage;
Layer.AddSublayer(maskOfMaskLayer);
Change to:
UIImageView imageView = new UIImageView();
imageView.Frame = imageFrame;
imageView.Image = Bundles.ImageOff;
imageView.TintColor = UIColor.White;
AddSubview(imageView);
Right Solution :
Sorry for misunderstanding the real want effect,I update as follow:
Layer.Mask = maskOfMaskLayer;
If you use AddSublayer just overwrite your image on the Layer, and Layer.Mask is the opposite effect, it only shows the background that covers the part, and the rest of the area will be white.CALayer.Mask
All needed to do it is reverse image colors via Core Graphics, any color to transparent, transparent to any color. In this case, black color is enough
public static UIImage GetRevertImage(UIImage image, CGRect rect, CGRect cropFrame)
{
UIGraphics.BeginImageContextWithOptions(rect.Size, false, 1);
UIColor.Black.SetColor();
UIGraphics.RectFill(rect);
image.Draw(cropFrame, CGBlendMode.DestinationOut, 1);
var newImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return newImage;
}
And set mask of mask layer to parent layer
Layer.Mask = maskOfMaskLayer;
And final version of code:
public class TestView : UIView
{
public TestView()
{
Layer.Delegate = this;
Frame = new CGRect(50f, 50f, 100f, 100f);
}
public override void LayoutSublayersOfLayer(CALayer layer)
{
base.LayoutSublayersOfLayer(layer);
var bounds = Bounds;
var circleFrame = new CGRect(20, 20, 60, 60);
var imageFrame = new CGRect(30, 30, 40, 40);
Layer.BorderWidth = 5;
Layer.BorderColor = UIColor.Red.CGColor;
Layer.CornerRadius = 4;
Layer.MasksToBounds = true;
var gradientLayer = new CAGradientLayer();
gradientLayer.Colors = new[] {UIColor.Green.CGColor, UIColor.Red.CGColor};
gradientLayer.Frame = bounds;
Layer.AddSublayer(gradientLayer);
var maskOfGradientLayer = new CAShapeLayer();
var path = UIBezierPath.FromRoundedRect(circleFrame, 30);
path.UsesEvenOddFillRule = true;
maskOfGradientLayer.Path = path.CGPath;
maskOfGradientLayer.FillRule = CAShapeLayer.FillRuleEvenOdd;
maskOfGradientLayer.FillColor = UIColor.Black.CGColor;
maskOfGradientLayer.Frame = bounds;
gradientLayer.Mask = maskOfGradientLayer;
var maskOfMaskLayer = new CALayer();
maskOfMaskLayer.Frame = bounds;
var image = UIImage.FromBundle("Turn");
var reversedImage = GetReverseImage(image, bounds, imageFrame);
maskOfMaskLayer.Contents = reversedImage.CGImage;
Layer.Mask = maskOfMaskLayer;
}
private static UIImage GetReverseImage(UIImage image, CGRect rect, CGRect cropFrame)
{
UIGraphics.BeginImageContextWithOptions(rect.Size, false, 1);
UIColor.Black.SetColor();
UIGraphics.RectFill(rect);
image.Draw(cropFrame, CGBlendMode.DestinationOut, 1);
var newImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return newImage;
}
}

Apply Gradient to dashed UIBezierPath

This gradient is what I'd like to achieve on dashed UIBezierPath.
Tried this code with no success.
let gradient = CAGradientLayer()
gradient.frame = outerPath.bounds
gradient.colors = [UIColor.red.cgColor, UIColor.yellow.cgColor]
// mask
let shapeMask = CAShapeLayer()
shapeMask.path = outerPath.cgPath
self.insertSublayer(gradient, at: 0)
Let the CAShapeLayer make a dashed line mask.
class
TheView: UIView {
required init?( coder aDecoder: NSCoder ) {
super.init( coder: aDecoder )
let outerPath = UIBezierPath( ovalIn: bounds.insetBy( dx: 20, dy: 20 ) )
let gradient = CAGradientLayer()
gradient.frame = bounds
gradient.colors = [ UIColor.red.cgColor, UIColor.yellow.cgColor ]
let shapeMask = CAShapeLayer()
shapeMask.path = outerPath.cgPath
shapeMask.lineWidth = 8
shapeMask.lineDashPhase = 0
shapeMask.lineDashPattern = [ 6, 2 ]
shapeMask.lineCap = .butt
shapeMask.lineJoin = .bevel
shapeMask.strokeColor = UIColor.black.cgColor // Any color
shapeMask.fillColor = nil
gradient.mask = shapeMask
layer.addSublayer( gradient )
}
}
I think the best way is filling every square's color by yourself.In this case, the start color is green, end color is red, so how to calculate the internal color?
You can see this demo:
typedef struct{
float red;
float green;
float blue;
} TFColor;
- (void)viewDidLoad {
[super viewDidLoad];
TFColor start = {0,1,0}; //green
TFColor end = {1,0,0}; //red
for (int i = 0; i<20; i++) {
UIView *square = [[UIView alloc] initWithFrame:CGRectMake(150, 20*i, 75, 20)];
//the lerp rate changes from 1 to 0.
TFColor lerpColor = [self lerp:(19-i)/19.0f first:start second:end];
square.backgroundColor = [UIColor colorWithRed:lerpColor.red green:lerpColor.green blue:lerpColor.blue alpha:1];
[self.view addSubview:square];
}
}
#define lerpNum(a,b,r) (a*r+(1.0f-r)*b)
-(TFColor)lerp:(CGFloat)rate first:(TFColor)firstColor second:(TFColor)secondColor{
TFColor result;
result.red = lerpNum(firstColor.red, secondColor.red, rate);
result.green = lerpNum(firstColor.green, secondColor.green, rate);
result.blue = lerpNum(firstColor.blue, secondColor.blue, rate);
return result;
}
You can use the angle of every square to get smooth lerp rate.

Add UILabel below the thumb of UISlider [duplicate]

How can i put an UILabel over the thumb of UISlider...so that when i move the thumb....UILabel will also move....as it is on the thumb...
Any idea??
Try this
yourLabel = [[UILabel alloc]initWithFrame:....];
//Call this method on Slider value change event
-(void)sliderValueChanged{
CGRect trackRect = [self.slider trackRectForBounds:self.slider.bounds];
CGRect thumbRect = [self.slider thumbRectForBounds:self.slider.bounds
trackRect:trackRect
value:self.slider.value];
yourLabel.center = CGPointMake(thumbRect.origin.x + self.slider.frame.origin.x, self.slider.frame.origin.y - 20);
}
I could get most accurate value by using this snippet.
The "knob" isn't available per public API, so bad chances for hooking it up - if it is a subview at all and not just drawn directly.
So you should add you label to the same view as the slider (make sure you add it later so that appears over it). You can then listen for the value change events and place your label accordingly. It is linear scaling between the endpoints that you need to figure out at first, but it shouldn't be too difficult.
Edit with code:
yourLabel = [[UILabel alloc]initWithFrame:....];
// .. configure label
[[yourSlider superview] addSubview:yourLabel];
[yourSlider addTarget:self action:#selector(adjustLabelForSlider:) forControlEvents:UIControlEventValueChanged];
-(void)adjustLabelForSlider:(id)slider
{
float value = slider.value;
float min = slider.minimumValue;
float max = slider.maximumValue;
CGFloat newX = ...; // Calculate based on yourSlider.frame and value, min, and max
CGFloat newY = ...;
[yourLabel setCenter:CGPointMake(newX,newY)];
}
Note: untested code ;-)
Same answer with swift3:
let trackRect: CGRect = slider.trackRect(forBounds: slider.bounds)
let thumbRect: CGRect = slider.thumbRect(forBounds: slider.bounds , trackRect: trackRect, value: slider.value)
let x = thumbRect.origin.x + slider.frame.origin.x
let y = slider.frame.origin.y - 20
sliderLabel.center = CGPoint(x: x, y: y)
extension UIImage {
class func imageWithLabel(_ label: UILabel) -> UIImage {
UIGraphicsBeginImageContextWithOptions(label.bounds.size, false, 0)
defer { UIGraphicsEndImageContext() }
label.layer.render(in: UIGraphicsGetCurrentContext()!)
return UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
}
}
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate, UITextViewDelegate, UIPickerViewDelegate, UIPickerViewDataSource, UIScrollViewDelegate
{
func maskRoundedImage(image: UIImage, radius: CGFloat) -> UIImage {
let imageView: UIImageView = UIImageView(image: image)
let layer = imageView.layer
layer.masksToBounds = true
layer.cornerRadius = radius
UIGraphicsBeginImageContext(imageView.bounds.size)
layer.render(in: UIGraphicsGetCurrentContext()!)
let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return roundedImage!
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 28, height: 28))
label.backgroundColor = .black
label.textAlignment = .center
label.font = label.font.withSize(12)
label.text = String(Int(round( backlightSlider.value * 100 )))
label.textColor = .white
var image = UIImage.imageWithLabel(label)
image = maskRoundedImage(image: image, radius: 14.0)
backlightSlider.setThumbImage(image, for: .normal)
}
Just add an imageview on the thumb of slider add a label on imageview
- (IBAction)valueChangedSlider:(id)sender {
handleView = [_slider.subviews lastObject];
label = [[UILabel alloc] initWithFrame:handleView.bounds];
label = (UILabel*)[handleView viewWithTag:1000];
if (label==nil) {
label = [[UILabel alloc] initWithFrame:handleView.bounds];
label.tag = 1000;
[label setFont:[UIFont systemFontOfSize:12]];
label.textColor = [UIColor redColor];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
[handleView addSubview:label];
}
label.text = [NSString stringWithFormat:#"%0.2f", self.slider.value];
}
If anybody is looking for answer in Swift, please take a look of my answer here:- Put Label over UISlider Thumb
It'll work like a charm :)
This could be very helpful...
How to get the center of the thumb image of UISlider

Create gradient-based UICollectionViewCell background colour

I want to create custom bar for displaying some progress, that must look something like this:
So, as you can see each bar has it's own gradient background. I want to use UICollectionView for this. But problem is to create gradient background for each individual cell. So is it possible somehow create ONE basic gradient for whole UICollectionView, and then mask it, to be visible inside UICollectionViewCell?
Okay, here is the example, background is the gradient layer with different colours are spread and above you can create a mask layer to appear only on the rectangular shapes, which gives the illusion of the different rectangular layers having different gradient, you can play with gradient to get your desired effect,
First you subclass the UIView or you can create directly, better you can create a subclass as I am doing in the example
obj-c
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
[self createGreadient];
}
return self;
}
//this create a grenadine with the rectangular mask layer
- (void)createGreadient
{
UIColor *theColor = [UIColor redColor];//[[UIColor alloc] initWithRed:146.0/255.0 green:146.0/255.0 blue:146.0/255.0 alpha:1];
CAGradientLayer *gradientLayer = [[CAGradientLayer alloc] init];
gradientLayer.frame = self.bounds;
//u can add your own colours to get your requirement
gradientLayer.colors = [NSArray arrayWithObjects:
(id)[theColor CGColor],
(id)[[theColor colorWithAlphaComponent:0.7f] CGColor],
(id)[[theColor colorWithAlphaComponent:0.4f] CGColor],
(id)[[theColor colorWithAlphaComponent:0.3f] CGColor],
(id)[[theColor colorWithAlphaComponent:0.2f] CGColor],
(id)[[theColor colorWithAlphaComponent:0.1f] CGColor],
(id)[[UIColor clearColor] CGColor],nil];
[self.layer addSublayer:gradientLayer];
CAShapeLayer *layerMask = [[CAShapeLayer alloc] init];
layerMask.frame = self.bounds;
UIBezierPath *rectangularMaskPath = [self getRectangularMaskPathForCount:5];
layerMask.path = rectangularMaskPath.CGPath;
gradientLayer.mask = layerMask;
}
//this creates rectangular path, u can adjust the rectangular and u can
//pass different number of count as the progress proceeds
- (UIBezierPath *)getRectangularMaskPathForCount:(NSInteger)rectCount
{
UIBezierPath *path = [[UIBezierPath alloc] init];
int i = 0;
for(;i <= rectCount; i++)
{
UIBezierPath *aPath;
CGRect aRect = CGRectMake(20+ (i * 20), 20, 10, 100); //set the rectangular position to your requirement
aPath = [UIBezierPath bezierPathWithRect:aRect];
[path appendPath:aPath];
}
return path;
}
swift version
subclass the UIView and past the below code,
override init (frame : CGRect) {
super.init(frame : frame)
createGreadient()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createGreadient()
{
let color:UIColor = UIColor.red
let greadetLayer = CAGradientLayer.init()
greadetLayer.frame = self.bounds
greadetLayer.colors = [color.withAlphaComponent(0.8).cgColor,color.withAlphaComponent(0.7),color.withAlphaComponent(0.4).cgColor,color.withAlphaComponent(0.3).cgColor,color.withAlphaComponent(0.2),color.withAlphaComponent(0.1).cgColor]
self.layer .addSublayer(greadetLayer)
let rectangularMaskPath = self.creatrRectangularPathWithCount(countRect: 5)
let maskLayer:CAShapeLayer = CAShapeLayer()
maskLayer.frame = self.bounds
maskLayer.path = rectangularMaskPath.cgPath
greadetLayer.mask = maskLayer
}
func creatrRectangularPathWithCount(countRect:NSInteger) -> UIBezierPath {
let path : UIBezierPath = UIBezierPath()
for i in 0..<countRect {
let aPath = UIBezierPath.init(rect: CGRect(x: 20 + (i * 20), y:20, width: 10, height: 100))
path.append(aPath)
}
return path
}
You can use the above view like below,
in ViewController
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let customView:CustomView = CustomView(frame: CGRect(x: 20, y: 20, width: 300, height: 300))
self.view.addSubview(customView)
}

How to apply text shadow to UITextView?

Actually I love UILabel. They're sweet. Now I had to go to UITextView because UILabel is not aligning text vertically to the top. Damn. One thing I really need is a text shadow. UILabel has it. UITextView seems to not have it. But I guess that guy just uses the same underlying UIKit NSString additions?? Maybe someone already has a solution for that problem? What would I overwrite?
text.layer.shadowColor = [[UIColor whiteColor] CGColor];
text.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
text.layer.shadowOpacity = 1.0f;
text.layer.shadowRadius = 1.0f;
And don't forget to add up top:
#import <QuartzCore/QuartzCore.h>
The answer with
text.layer.shadowColor
adds shadow to whole textView, perhaps it works, but it adds shadow not only to text.
The correct answer is:
CALayer *textLayer = ((CALayer *)[textView.layer.sublayers objectAtIndex:0]);
textLayer.shadowColor = [UIColor whiteColor].CGColor;
textLayer.shadowOffset = CGSizeMake(0.0f, 1.0f);
textLayer.shadowOpacity = 1.0f;
textLayer.shadowRadius = 1.0f;
Swift 3
let textView = UITextView(frame: view.frame)
textView.font = UIFont(name: "Helvetica", size: 64.0)
textView.textColor = .red
textView.text = "Hello World"
textView.layer.shadowColor = UIColor.black.cgColor
textView.layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
textView.layer.shadowOpacity = 1.0
textView.layer.shadowRadius = 2.0
textView.layer.backgroundColor = UIColor.clear.cgColor
Swift 2.3
let textView = UITextView(frame: view.frame)
textView.font = UIFont(name: "Helvetica", size: 64.0)
textView.textColor = UIColor.redColor()
textView.text = "Hello World"
textView.layer.shadowColor = UIColor.blackColor().CGColor
textView.layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
textView.layer.shadowOpacity = 1.0
textView.layer.shadowRadius = 2.0
textView.layer.backgroundColor = UIColor.clearColor().CGColor
This Swift example uses the attributed string method of adding shadow to text. See this answer for more about attributed strings in Swift. This method (as opposed to using the layer method) gives you the flexibility to set the shadow on a range of text if you want to.
// Create a string
let myString = "Shadow"
// Create a shadow
let myShadow = NSShadow()
myShadow.shadowBlurRadius = 3
myShadow.shadowOffset = CGSize(width: 3, height: 3)
myShadow.shadowColor = UIColor.gray
// Create an attribute from the shadow
let myAttribute = [ NSAttributedStringKey.shadow: myShadow ]
// Add the attribute to the string
let myAttrString = NSAttributedString(string: myString, attributes: myAttribute)
// set the attributed text on a label
myLabel.attributedText = myAttrString // can also use with UITextView
Updated for Swift 4
In iOS 6+ use attributed text
NSShadow * shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor blackColor];
shadow.shadowOffset = CGSizeMake(2, 2);
NSDictionary * textAttributes =
#{ NSForegroundColorAttributeName : [UIColor blueColor],
NSShadowAttributeName : shadow,
NSFontAttributeName : [UIFont boldSystemFontOfSize:20] };
textView.attributedText = [[NSAttributedString alloc] initWithString:#"Hello"
attributes:textAttributes];
It depends on the version of iOS you are using. Beginning with iOS 6 there is a single shadow supported, you set that as an attribute on an NSAttributedString that you then set onthe label.
swift 4, swift 4.2, swift 5 and above
Simple and elegant solution , can use from interface builder easily
extension UIView {
/* The color of the shadow. Defaults to opaque black. Colors created
* from patterns are currently NOT supported. Animatable. */
#IBInspectable var shadowColor: UIColor? {
set {
layer.shadowColor = newValue!.cgColor
}
get {
if let color = layer.shadowColor {
return UIColor(cgColor: color)
}
else {
return nil
}
}
}
/* The opacity of the shadow. Defaults to 0.4 Specifying a value outside the
* [0,1] range will give undefined results. Animatable. */
#IBInspectable var shadowOpacity: Float {
set {
layer.shadowOpacity = newValue
}
get {
return layer.shadowOpacity
}
}
/* The shadow offset. Defaults to (1, 2). Animatable. */
#IBInspectable var shadowOffset: CGPoint {
set {
layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
}
get {
return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
}
}
/* The blur radius used to create the shadow. Defaults to 3. Animatable. */
#IBInspectable var shadowRadius: CGFloat {
set {
layer.shadowRadius = newValue
}
get {
return layer.shadowRadius
}
}
}

Resources