Why masksToBounds = YES prevents CALayer shadow? - ios

With the following snippet, I'm adding a drop shadow effect to one my UIView. Which works pretty well. But as soon as I set the view's masksToBounds property to YES. The drop shadow effect isn't rendered any more.
self.myView.layer.shadowColor = [[UIColor blackColor] CGColor];
self.myView.layer.shadowOpacity = 1.0;
self.myView.layer.shadowRadius = 10.0;
self.myView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
self.myView.layer.cornerRadius = 5.0;
self.myView.layer.masksToBounds = YES; // <-- This is causing the Drop shadow to not be rendered
UIBezierPath *path = [UIBezierPath bezierPathWithCurvedShadowForRect:self.myView.bounds];
self.myView.layer.shadowPath = path.CGPath;
self.myView.layer.shouldRasterize = YES;
Do you have any ideas on this?

Because shadow is an effect done outside the View, and that masksToBounds set to YES will tell the UIView not to draw anything that is outside itself.
If you want a roundedCorner view with shadow I suggest you do it with 2 views:
UIView *view1 = [[UIView alloc] init];
UIView *view2 = [[UIView alloc] init];
view1.layer.cornerRadius = 5.0;
view1.layer.masksToBounds = YES;
view2.layer.cornerRadius = 5.0;
view2.layer.shadowColor = [[UIColor blackColor] CGColor];
view2.layer.shadowOpacity = 1.0;
view2.layer.shadowRadius = 10.0;
view2.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
[view2 addSubview:view1];
[view1 release];

It's iOS 6 now, things might have changed. TheSquad's answer don't work for me until I managed to add one more line view2.layer.masksToBounds = NO;, otherwise shadow doesn't show. Although documentation says masksToBounds is NO by default, my code shows the opposite.
Here is how I make a rounded corner button with shadow, which is among the most commonly used code snippet in my app.
button.layer.masksToBounds = YES;
button.layer.cornerRadius = 10.0f;
view.layer.masksToBounds = NO; // critical to add this line
view.layer.cornerRadius = 10.0f;
view.layer.shadowOpacity = 1.0f;
// set shadow path to prevent horrible performance
view.layer.shadowPath =
[UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;
[view addSubview:button];
EDIT
If views need to be animated or scrolled, masksToBounds = YES tax performance significantly, which means animation will probably get stuttered. To get rounded corner and shadow AND smooth animation or scrolling, use following code instead:
button.backgroundColor = [UIColor clearColor];
button.layer.backgroundColor = [UIColor redColor].CGColor;
button.layer.masksToBounds = NO;
button.layer.cornerRadius = 10.0f;
view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;
view.layer.shadowOffset = CGSizeMake(0.0f, 4.0f);
view.layer.shadowRadius = 2.0f;
view.layer.masksToBounds = NO;
view.layer.cornerRadius = 10.0f;
[view addSubview:button];

Swift 3.0 version with StoryBoard
The same idea with #TheSquad. Create a new view under the actual view and add shadow to the lower view.
1. Create a view under the actual view
Drag a UIView to StoryBoard with same constraint as your target view. Check clip to bound for the target view. Also make sure the new view is listed before the target view so that the target view will cover the new view.
2. Now link the new view to your code add add shadow on it
This is just a sample. You can do whatever way you want here
shadowView.layer.masksToBounds = false
shadowView.layer.shadowColor = UIColor.red.cgColor
shadowView.layer.shadowOpacity = 0.5
shadowView.layer.shadowOffset = CGSize(width: -1, height: 1)
shadowView.layer.shadowRadius = 3
shadowView.layer.shadowPath = UIBezierPath(rect: coverImage.bounds).cgPath
shadowView.layer.shouldRasterize = true

This is the Swift 3 and IBDesignable version of the answer posted by #TheSquad.
I used the same concept while making changes in the storyboard file. First I moved my targetView (the one which requires corner radius and shadow) inside a new containerView. Then I added the following lines of code (Reference: https://stackoverflow.com/a/35372901/419192) to add some IBDesignable attributes for UIView Class:
#IBDesignable 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. 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 (0, -3). 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
}
}
/* The corner radius of the view. */
#IBInspectable var cornerRadius: CGFloat {
set {
layer.cornerRadius = newValue
}
get {
return layer.cornerRadius
}
}
After adding this code, I went back to the storyboard and on selecting my containerView I could now find a new set of attributes in the attributes inspector:
Other than adding values for these attributes as per my choice, I also added a corner radius to my targetView and set the masksToBounds property as true.
I hope this helps :)

I also had drastic performance issues with shadows and rounded corners. Instead of using the shadowPath part, I used the following lines which perfectly solved the performance hit:
self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = UIScreen.mainScreen.scale;

Here is one of the solutions:
#IBOutlet private weak var blockView: UIView! {
didSet {
blockView.backgroundColor = UIColor.white
blockView.layer.shadowColor = UIColor.black.cgColor
blockView.layer.shadowOpacity = 0.5
blockView.layer.shadowOffset = CGSize.zero
blockView.layer.cornerRadius = 10
}
}
#IBOutlet private weak var imageView: UIImageView! {
didSet {
imageView.layer.cornerRadius = 10
imageView.layer.masksToBounds = true
imageView.layer.shouldRasterize = true
}
}

Related

Shadow inside table view cell not working

The shadow in my cell is not working at all.
This is one of the view that I want to add shadow but it's not working. I added this code inside my custom cell class.
super.layoutSubviews()
UIview1.layer.cornerRadius = 7
UIview1.layer.borderWidth = 1.0
UIview1.layer.borderColor = HexColor.hexStringToUIColor(hex: "FA2537").cgColor
UIview1.layer.masksToBounds = true
UIview1.layer.shadowColor = HexColor.hexStringToUIColor(hex: "01A4B7").cgColor
UIview1.layer.shadowOpacity = 0.5
UIview1.layer.shadowOffset = CGSize.zero
UIview1.layer.shadowRadius = 5
}
For showing shadow on a view, you need to set its layer's masksToBounds property false.
or you can try this.
You can make a method like this and can use:
extension UIView {
func setShadowWith(color: UIColor = UIColor.black, shadowOpacity: Float = 0.2, radius: Float = 1.0, shadowOffSet: CGSize = CGSize(width: 0, height: 1)) {
self.layer.shadowColor = color.cgColor
self.layer.shadowOpacity = shadowOpacity
self.layer.shadowOffset = shadowOffSet
self.layer.shadowRadius = CGFloat(radius)
}
}
and can use function like:
yourContainerView.setShadowWith()
Here parameters used in functions are taking default values. you can change accordingly.
Set maskToBounds to false instead of true:
UIview1.layer.masksToBounds = false

IOS: How to make a shadow for UIView on 4 side (top,right,bottom and left)

I am using the code below to make the shadow for my ImageView
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:self.avatarImageView.bounds];
self.avatarImageView.layer.masksToBounds = NO;
self.avatarImageView.layer.shadowColor = [UIColor blackColor].CGColor;
self.avatarImageView.layer.shadowOffset = CGSizeMake(5.0f, 5.0f);
self.avatarImageView.layer.shadowOpacity = 0.8f;
self.avatarImageView.layer.shadowPath = shadowPath.CGPath;
It will drop a shadow in the right and bottom like this image.
Now I want to make my ImageView also have a shadow in top and left.
What should I change in code?
Is possible to make the view contains shadow in top,right,bottom,left by config in code only or I need to create other layout view for shadow? Any help would be great appreciated.
Here is what I want to achieve
Update
Thank #Dipen Panchasara for give a simple solution. Follow #Dipen Panchasara (with the shadow color is black) I will have the shadow image like this
Only following code will do the job for your requirement, You don't need to create UIBezierPath for shadow path.
// *** Set masks bounds to NO to display shadow visible ***
self.avatarImageView.layer.masksToBounds = NO;
// *** Set light gray color as shown in sample ***
self.avatarImageView.layer.shadowColor = [UIColor lightGrayColor].CGColor;
// *** *** Use following to add Shadow top, left ***
self.avatarImageView.layer.shadowOffset = CGSizeMake(-5.0f, -5.0f);
// *** Use following to add Shadow bottom, right ***
//self.avatarImageView.layer.shadowOffset = CGSizeMake(5.0f, 5.0f);
// *** Use following to add Shadow top, left, bottom, right ***
// avatarImageView.layer.shadowOffset = CGSizeZero;
// avatarImageView.layer.shadowRadius = 5.0f;
// *** Set shadowOpacity to full (1) ***
self.avatarImageView.layer.shadowOpacity = 1.0f;
Like this:
float shadowSize = 10.0f;
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:CGRectMake(self.avatarImageView.frame.origin.x - shadowSize / 2,
self.avatarImageView.frame.origin.y - shadowSize / 2,
self.avatarImageView.frame.size.width + shadowSize,
self.avatarImageView.frame.size.height + shadowSize)];
self.avatarImageView.layer.masksToBounds = NO;
self.avatarImageView.layer.shadowColor = [UIColor blackColor].CGColor;
self.avatarImageView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
self.avatarImageView.layer.shadowOpacity = 0.8f;
self.avatarImageView.layer.shadowPath = shadowPath.CGPath;
Swift 3 version:
let shadowSize : CGFloat = 5.0
let shadowPath = UIBezierPath(rect: CGRect(x: -shadowSize / 2,
y: -shadowSize / 2,
width: self.avatarImageView.frame.size.width + shadowSize,
height: self.avatarImageView.frame.size.height + shadowSize))
self.avatarImageView.layer.masksToBounds = false
self.avatarImageView.layer.shadowColor = UIColor.black.cgColor
self.avatarImageView.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
self.avatarImageView.layer.shadowOpacity = 0.5
self.avatarImageView.layer.shadowPath = shadowPath.cgPath
Little less code for swift 3:
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOpacity = 0.7
view.layer.shadowOffset = CGSize.zero
view.layer.shadowRadius = 4
view.layer.shadowPath = UIBezierPath(rect: planView.bounds).cgPath
Without using UIBezierPath, CGSize.zero is the key here
yourView.layer.masksToBounds = false
yourView?.layer.shadowColor = UIColor.red.cgColor
yourView?.layer.shadowOffset = CGSize.zero
yourView?.layer.shadowOpacity = 0.5
yourView?.layer.shadowRadius = 4
The best solution for shadow with a rounded corner on the same view and no need to do clipsToBounds or maskToBounds
func addShadow(cornerRadius: CGFloat, maskedCorners: CACornerMask, color: UIColor, offset: CGSize, opacity: Float, shadowRadius: CGFloat) {
self.layer.cornerRadius = cornerRadius
self.layer.maskedCorners = maskedCorners
self.layer.shadowColor = color.cgColor
self.layer.shadowOffset = offset
self.layer.shadowOpacity = opacity
self.layer.shadowRadius = shadowRadius
}
For UIView and Adding shadow, remember to set background color to the UIView.
If the background color is clearColor, no shadow appears.
If you are still not getting proper shadow, the problem might be the place you added the code.
You should call this in viewDidLayoutSubviews when you use UIBezierPath. If you call in ViewDidLoad, you may get wrong result since the views layout process might be unfinished.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
//Shadow code here
}
//If you’ve tried this before, you know exactly what happens. The corners will be rounded, but the shadow will be missing. If you set masksToBounds to false, the shadow will appear, but the corners will not be rounded.
//to get Shadow with corner radius
Add super view for container view with clear color and apply shadow for super view ,Apply corner radius for container View. try it.
//view to apply shadow and corner radius
containerView.layer.cornerRadius = 3
containerView.clipsToBounds = true
//superview of container View for to apply shadow
shadowView.layer.shadowOpacity = 0.1
shadowView.layer.shadowRadius = 2.0
shadowView.layer.masksToBounds = false
shadowView.layer.shadowOffset = CGSize.zero
shadowView.layer.shadowColor = UIColor.Black.cgColor
shadowView.layer.shadowPath = UIBezierPath(roundedRect:containerView.bounds, cornerRadius: containerView.layer.cornerRadius).cgPath
shadowView.layer.shouldRasterize = true
CGRectInset(self.avatarImageView.bounds, -10.0, -10.0)
**in swift 4**
yourView.clipsToBounds = true
yourView.layer.cornerRadius = 20
yourView.layer.shadowPath = UIBezierPath(roundedRect: self.yourView.bounds,
cornerRadius: self.DeletConversation.layer.cornerRadius).cgPath
yourView.layer.shadowColor = UIColor(hexString: "color")?.cgColor
DeletConversation.layer.shadowOpacity = 1
DeletConversation.layer.shadowOffset = CGSize(width: 0, height: 1.0)
DeletConversation.layer.shadowRadius = 1
DeletConversation.layer.masksToBounds = false
There is a very detailed explanation about this here: https://www.hackingwithswift.com/example-code/uikit/how-to-add-a-shadow-to-a-uiview.
If someone is straggling with no top shadow in a collection view then this may help:
I know that this may be obvious for some people, but if you have a CollectionView with header and cell, make sure that you have space between the header and the cell, otherwise, the top shadow of the cell will be blocked by the header.
To add space, just use the insetsForSectionAt section.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 5, left: 0, bottom: 5, right: 0)
}

What's the best way to add a drop shadow to my UIView

I am trying to add a drop shadow to views that are layered on top of one another, the views collapse allowing content in other views to be seen, in this vein i want to keep view.clipsToBounds ON so that when the views collapse their content is clipped.
This seems to have made it difficult for me to add a drop shadow to the layers as when i turn clipsToBounds ON the shadows are clipped also.
I have been trying to manipulate view.frame and view.bounds in order to add a drop shadow to the frame but allow the bounds to be large enough to encompass it, however I have had no luck with this.
Here is the code I am using to add a Shadow (this only works with clipsToBounds OFF as shown)
view.clipsToBounds = NO;
view.layer.shadowColor = [[UIColor blackColor] CGColor];
view.layer.shadowOffset = CGSizeMake(0,5);
view.layer.shadowOpacity = 0.5;
Here is a screenshot of the shadow being applied to the top lightest grey layer. Hopefully this gives an idea of how my content will overlap if clipsToBounds is OFF.
How can I add a shadow to my UIView and keep my content clipped?
Edit: Just wanted to add that I have also played around with using background images with shadows on, which does work well, however I would still like to know the best coded solution for this.
Try this:
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:view.bounds];
view.layer.masksToBounds = NO;
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = shadowPath.CGPath;
First of all: The UIBezierPath used as shadowPath is crucial. If you don't use it, you might not notice a difference at first, but the keen eye will observe a certain lag occurring during events like rotating the device and/or similar. It's an important performance tweak.
Regarding your issue specifically: The important line is view.layer.masksToBounds = NO. It disables the clipping of the view's layer's sublayers that extend further than the view's bounds.
For those wondering what the difference between masksToBounds (on the layer) and the view's own clipToBounds property is: There isn't really any. Toggling one will have an effect on the other. Just a different level of abstraction.
Swift 2.2:
override func layoutSubviews()
{
super.layoutSubviews()
let shadowPath = UIBezierPath(rect: bounds)
layer.masksToBounds = false
layer.shadowColor = UIColor.blackColor().CGColor
layer.shadowOffset = CGSizeMake(0.0, 5.0)
layer.shadowOpacity = 0.5
layer.shadowPath = shadowPath.CGPath
}
Swift 3:
override func layoutSubviews()
{
super.layoutSubviews()
let shadowPath = UIBezierPath(rect: bounds)
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0.0, height: 5.0)
layer.shadowOpacity = 0.5
layer.shadowPath = shadowPath.cgPath
}
Wasabii's answer in Swift 2.3:
let shadowPath = UIBezierPath(rect: view.bounds)
view.layer.masksToBounds = false
view.layer.shadowColor = UIColor.blackColor().CGColor
view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
view.layer.shadowOpacity = 0.2
view.layer.shadowPath = shadowPath.CGPath
And in Swift 3/4/5:
let shadowPath = UIBezierPath(rect: view.bounds)
view.layer.masksToBounds = false
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
view.layer.shadowOpacity = 0.2
view.layer.shadowPath = shadowPath.cgPath
Put this code in layoutSubviews() if you're using AutoLayout.
In SwiftUI, this is all much easier:
Color.yellow // or whatever your view
.shadow(radius: 3)
.frame(width: 200, height: 100)
The trick is defining the masksToBounds property of your view's layer properly:
view.layer.masksToBounds = NO;
and it should work.
(Source)
You can create an extension for UIView to access these values in the design editor
extension UIView{
#IBInspectable var shadowOffset: CGSize{
get{
return self.layer.shadowOffset
}
set{
self.layer.shadowOffset = newValue
}
}
#IBInspectable var shadowColor: UIColor{
get{
return UIColor(cgColor: self.layer.shadowColor!)
}
set{
self.layer.shadowColor = newValue.cgColor
}
}
#IBInspectable var shadowRadius: CGFloat{
get{
return self.layer.shadowRadius
}
set{
self.layer.shadowRadius = newValue
}
}
#IBInspectable var shadowOpacity: Float{
get{
return self.layer.shadowOpacity
}
set{
self.layer.shadowOpacity = newValue
}
}
}
You can set shadow to your view from storyboard also
On viewWillLayoutSubviews:
override func viewWillLayoutSubviews() {
sampleView.layer.masksToBounds = false
sampleView.layer.shadowColor = UIColor.darkGrayColor().CGColor;
sampleView.layer.shadowOffset = CGSizeMake(2.0, 2.0)
sampleView.layer.shadowOpacity = 1.0
}
Using Extension of UIView:
extension UIView {
func addDropShadowToView(targetView:UIView? ){
targetView!.layer.masksToBounds = false
targetView!.layer.shadowColor = UIColor.darkGrayColor().CGColor;
targetView!.layer.shadowOffset = CGSizeMake(2.0, 2.0)
targetView!.layer.shadowOpacity = 1.0
}
}
Usage:
sampleView.addDropShadowToView(sampleView)
So yes, you should prefer the shadowPath property for performance, but also:
From the header file of CALayer.shadowPath
Specifying the path explicitly using this property will usually
* improve rendering performance, as will sharing the same path
* reference across multiple layers
A lesser known trick is sharing the same reference across multiple layers. Of course they have to use the same shape, but this is common with table/collection view cells.
I don't know why it gets faster if you share instances, i'm guessing it caches the rendering of the shadow and can reuse it for other instances in the view. I wonder if this is even faster with

How to create border in UIButton?

I use custom button in my app named "addButton" and I want to border it with white color how can i get the white color border around my custom button?
You can set the border properties on the CALayer by accessing the layer property of the button.
First, add Quartz
#import <QuartzCore/QuartzCore.h>
Set properties:
myButton.layer.borderWidth = 2.0f;
myButton.layer.borderColor = [UIColor greenColor].CGColor;
See:
https://developer.apple.com/documentation/quartzcore/calayer#//apple_ref/occ/cl/CALayer
The CALayer in the link above allows you to set other properties like corner radius, maskToBounds etc...
Also, a good article on button fun:
https://web.archive.org/web/20161221132308/http://www.apptite.be/tutorial_custom_uibuttons.php
Its very simple, just add the quartzCore header in your file(for that you have to add the quartz framework to your project)
and then do this
[[button layer] setCornerRadius:8.0f];
[[button layer] setMasksToBounds:YES];
[[button layer] setBorderWidth:1.0f];
you can change the float values as required.
enjoy.
Here's some typical modern code ...
self.buttonTag.layer.borderWidth = 1.0f;
self.buttonCancel.layer.borderWidth = 1.0f;
self.buttonTag.layer.borderColor = [UIColor blueColor].CGColor;
self.buttonCancel.layer.borderColor = [UIColor blueColor].CGColor;
self.buttonTag.layer.cornerRadius = 4.0f;
self.buttonCancel.layer.cornerRadius = 4.0f;
that's a similar look to segmented controls.
UPDATE for Swift:
No need to add "QuartzCore"
Just do:
button.layer.cornerRadius = 8.0
button.layer.borderWidth = 1.0
button.layer.borderColor = UIColor.black.cgColor
And in swift, you don't need to import "QuartzCore/QuartzCore.h"
Just use:
button.layer.borderWidth = 0.8
button.layer.borderColor = (UIColor( red: 0.5, green: 0.5, blue:0, alpha: 1.0 )).cgColor
or
button.layer.borderWidth = 0.8
button.layer.borderColor = UIColor.grayColor().cgColor
The problem setting the layer's borderWidth and borderColor is that the when you touch the button the border doesn't animate the highlight effect.
Of course, you can observe the button's events and change the border color accordingly but that feels unnecessary.
Another option is to create a stretchable UIImage and setting it as the button's background image. You can create an Image set in your Images.xcassets like this:
Then, you set it as the button's background image:
If your image is a template image you can set tint color of the button and the border will change:
Now the border will highlight with the rest of the button when touched.
Here's an updated version (Swift 3.0.1) from Ben Packard's answer.
import UIKit
#IBDesignable class BorderedButton: UIButton {
#IBInspectable var borderColor: UIColor? {
didSet {
if let bColor = borderColor {
self.layer.borderColor = bColor.cgColor
}
}
}
#IBInspectable var borderWidth: CGFloat = 0 {
didSet {
self.layer.borderWidth = borderWidth
}
}
override var isHighlighted: Bool {
didSet {
guard let currentBorderColor = borderColor else {
return
}
let fadedColor = currentBorderColor.withAlphaComponent(0.2).cgColor
if isHighlighted {
layer.borderColor = fadedColor
} else {
self.layer.borderColor = currentBorderColor.cgColor
let animation = CABasicAnimation(keyPath: "borderColor")
animation.fromValue = fadedColor
animation.toValue = currentBorderColor.cgColor
animation.duration = 0.4
self.layer.add(animation, forKey: "")
}
}
}
}
The resulting button can be used inside your StoryBoard thanks to the #IBDesignable and #IBInspectable tags.
Also the two properties defined, allow you to set the border width and color directly on interface builder and preview the result.
Other properties could be added in a similar fashion, for border radius and highlight fading time.
To change button Radius, Color and Width I set like this:
self.myBtn.layer.cornerRadius = 10;
self.myBtn.layer.borderWidth = 1;
self.myBtn.layer.borderColor =[UIColor colorWithRed:189.0/255.0f green:189.0/255.0f blue:189.0/255.0f alpha:1.0].CGColor;
This can be achieved in various methods in Swift 3.0 Worked on Latest version August - 2017
Option 1:
Directly assign the borderWidth property values for UI Button:
btnUserButtonName.layer.borderWidth = 1.0
Set Title with Default Color values for UI Button:
btnUserButtonName.setTitleColor(UIColor.darkGray, for: .normal)
Set Border with Default Color for the border property values for UI Button:
btnUserButtonName.layer.borderColor = UIColor.red
Set user defined Color for the border property values for UI Button:
let myGrayColor = UIColor(red: 0.889415, green: 0.889436, blue:0.889424, alpha: 1.0 )
btnUserButtonName.layer.borderColor = myGrayColor.cgColor
Option 2: [Recommended]
Use the Extension method, so the Button through out the application will be looking consistent and no need to repeat multiple lines of code every where.
//Create an extension class in any of the swift file
extension UIButton {
func setBordersSettings() {
let c1GreenColor = (UIColor(red: -0.108958, green: 0.714926, blue: 0.758113, alpha: 1.0))
self.layer.borderWidth = 1.0
self.layer.cornerRadius = 5.0
self.layer.borderColor = c1GreenColor.cgColor
self.setTitleColor(c1GreenColor, for: .normal)
self.layer.masksToBounds = true
}
}
Usage in code:
//use the method and call whever the border has to be applied
btnUserButtonName.setBordersSettings()
Output of Extension method Button:
You don't need to import QuartzCore.h now. Taking iOS 8 sdk and Xcode 6.1 in referrence.
Directly use:
[[myButton layer] setBorderWidth:2.0f];
[[myButton layer] setBorderColor:[UIColor greenColor].CGColor];
Here's a UIButton subclass that supports the highlighted state animation without using images. It also updates the border color when the view's tint mode changes.
class BorderedButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
layer.borderColor = tintColor.cgColor
layer.borderWidth = 1
layer.cornerRadius = 5
contentEdgeInsets = UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10)
}
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
override func tintColorDidChange() {
super.tintColorDidChange()
layer.borderColor = tintColor.cgColor
}
override var isHighlighted: Bool {
didSet {
let fadedColor = tintColor.withAlphaComponent(0.2).cgColor
if isHighlighted {
layer.borderColor = fadedColor
} else {
layer.borderColor = tintColor.cgColor
let animation = CABasicAnimation(keyPath: "borderColor")
animation.fromValue = fadedColor
animation.toValue = tintColor.cgColor
animation.duration = 0.4
layer.add(animation, forKey: nil)
}
}
}
}
Usage:
let button = BorderedButton(style: .System) //style .System is important
Appearance:
Swift 5
button.layer.borderWidth = 2
To change the colour of the border use
button.layer.borderColor = CGColor(srgbRed: 255/255, green: 126/255, blue: 121/255, alpha: 1)
****In Swift 3****
To create border
btnName.layer.borderWidth = 1
btnName.layer.borderColor = UIColor.black.cgColor
To make corner rounded
btnName.layer.cornerRadius = 5
Update with Swift 3
button.layer.borderWidth = 0.8
button.layer.borderColor = UIColor.blue.cgColor

Cocoa Touch: How To Change UIView's Border Color And Thickness?

I saw in the inspector that I can change the background color, but I'd like to also change the border color and thickness, is this possible?
You need to use view's layer to set border property. e.g:
#import <QuartzCore/QuartzCore.h>
...
view.layer.borderColor = [UIColor redColor].CGColor;
view.layer.borderWidth = 3.0f;
You also need to link with QuartzCore.framework to access this functionality.
Xcode 6 update
Since Xcode's newest version there is a better solution to this:
With #IBInspectable you can set Attributes directly from within the Attributes Inspector.
This sets the User Defined Runtime Attributes for you:
There are two approaches to set this up:
Option 1 (with live updating in Storyboard)
Create MyCustomView.
This inherits from UIView.
Set #IBDesignable (this makes the View update live).*
Set your Runtime Attributes (border, etc.) with #IBInspectable
Change your Views Class to MyCustomView
Edit in Attributes Panel and see changes in Storyboard :)
`
#IBDesignable
class MyCustomView: UIView {
#IBInspectable var cornerRadius: CGFloat = 0 {
didSet {
layer.cornerRadius = cornerRadius
layer.masksToBounds = cornerRadius > 0
}
}
#IBInspectable var borderWidth: CGFloat = 0 {
didSet {
layer.borderWidth = borderWidth
}
}
#IBInspectable var borderColor: UIColor? {
didSet {
layer.borderColor = borderColor?.CGColor
}
}
}
* #IBDesignable only works when set at the start of class MyCustomView
Option 2 (not working since Swift 1.2, see comments)
Extend your UIView Class:
extension UIView {
#IBInspectable var cornerRadius: CGFloat = 0 {
didSet {
layer.cornerRadius = cornerRadius
layer.masksToBounds = cornerRadius > 0
}
}
#IBInspectable var borderWidth: CGFloat = 0 {
didSet {
layer.borderWidth = borderWidth
}
}
#IBInspectable var borderColor: UIColor? {
didSet {
layer.borderColor = borderColor?.CGColor
}
}
}
This way, your default View always has those extra editable fields in Attributes Inspector. Another advantage is that you don't have to change the class to MycustomView every time.
However, one drawback to this is that you will only see your changes when you run your app.
You can also create border with the color of your wish..
view.layer.borderColor = [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1.0].CGColor;
*r,g,b are the values between 0 to 255.
Add following #IBInspectables in UIView extension
extension UIView {
#IBInspectable var borderWidth: CGFloat {
get {
return layer.borderWidth
}
set(newValue) {
layer.borderWidth = newValue
}
}
#IBInspectable var borderColor: UIColor? {
get {
if let color = layer.borderColor {
return UIColor(CGColor: color)
}
return nil
}
set(newValue) {
layer.borderColor = newValue?.CGColor
}
}
}
And then you should be able to set borderColor and borderWidth attributes directly from Attribute inspector. See attached image
Attributes Inspector
view.layer.borderWidth = 1.0
view.layer.borderColor = UIColor.lightGray.cgColor
When I use Vladimir's CALayer solution, and on top of the view I have an animation, like a modal UINavigationController dismissing, I see a lot of glitches happening and having drawing performance issues.
So, another way to achieve this, but without the glitches and performance loss, is to make a custom UIView and implement the drawRect message like so:
- (void)drawRect:(CGRect)rect
{
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(contextRef, 1);
CGContextSetRGBStrokeColor(contextRef, 255.0, 255.0, 255.0, 1.0);
CGContextStrokeRect(contextRef, rect);
}
Try this code:
view.layer.borderColor = [UIColor redColor].CGColor;
view.layer.borderWidth= 2.0;
[view setClipsToBounds:YES];
I wouldn't suggest overriding the drawRect due to causing a performance hit.
Instead, I would modify the properties of the class like below (in your custom uiview):
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.layer.borderWidth = 2.f;
self.layer.borderColor = [UIColor redColor].CGColor;
}
return self;
I didn't see any glitches when taking above approach - not sure why putting in the initWithFrame stops these ;-)
I wanted to add this to #marczking's answer (Option 1) as a comment, but my lowly status on StackOverflow is preventing that.
I did a port of #marczking's answer to Objective C. Works like charm, thanks #marczking!
UIView+Border.h:
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface UIView (Border)
-(void)setBorderColor:(UIColor *)color;
-(void)setBorderWidth:(CGFloat)width;
-(void)setCornerRadius:(CGFloat)radius;
#end
UIView+Border.m:
#import "UIView+Border.h"
#implementation UIView (Border)
// Note: cannot use synthesize in a Category
-(void)setBorderColor:(UIColor *)color
{
self.layer.borderColor = color.CGColor;
}
-(void)setBorderWidth:(CGFloat)width
{
self.layer.borderWidth = width;
}
-(void)setCornerRadius:(CGFloat)radius
{
self.layer.cornerRadius = radius;
self.layer.masksToBounds = radius > 0;
}
#end
#IBInspectable is working for me on iOS 9 , Swift 2.0
extension UIView {
#IBInspectable var borderWidth: CGFloat {
get {
return layer.borderWidth
}
set(newValue) {
layer.borderWidth = newValue
}
}
#IBInspectable var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set(newValue) {
layer.cornerRadius = newValue
}
}
#IBInspectable var borderColor: UIColor? {
get {
if let color = layer.borderColor {
return UIColor(CGColor: color)
}
return nil
}
set(newValue) {
layer.borderColor = newValue?.CGColor
}
}
If you didn't want to edit the layer of a UIView, you could always embed the view within another view. The parent view would have its background color set to the border color. It would also be slightly larger, depending upon how wide you want the border to be.
Of course, this only works if your view isn't transparent and you only want a single border color. The OP wanted the border in the view itself, but this may be a viable alternative.
item's border color in swift 4.2:
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell_lastOrderId") as! Cell_lastOrder
cell.layer.borderWidth = 1
cell.layer.borderColor = UIColor.white.cgColor
cell.layer.cornerRadius = 10
If you want to add different border on different sides, may be add a subview with the specific style is a way easy to come up with.
[self.view.layer setBorderColor: [UIColor colorWithRed:0.265 green:0.447 blue:0.767 alpha:1.0f].CGColor];

Resources