iOS Clean corners for rounded profile image - ios

So I followed an AppCoda tutorial on rounding the corners of a profile image, and it worked fine, except for one thing. Wherever the image was rounded, there is a bit of bleed-through from the image (especially if a white border is used).
self.imageview.image = image
self.imageview.layer.cornerRadius = 10.0
self.imageview.layer.borderWidth = 3.0
self.imageview.layer.borderColor = UIColor.whiteColor().CGColor
self.imageview.clipsToBounds = true

You can also add a mask which is inset a little, if you want:
let path = UIBezierPath(roundedRect: CGRectInset(imageView.bounds, 0.5, 0.5), cornerRadius: 10.0)
let mask = CAShapeLayer()
mask.path = path.CGPath
imageview.layer.mask = mask

You could create a mask over the rectangle. This seems to give clean edges, at least in Playground. Here is the code, but you will need to modify it a bit to get rounded inner rect.
// simple red rect
var view = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
view.backgroundColor = UIColor.redColor()
view.layer.borderColor = UIColor.whiteColor().CGColor
view.layer.borderWidth = 6.0
// path for the mask
let rectanglePath = UIBezierPath(roundedRect:view.bounds, cornerRadius: 20)
// applying the mask over the view
let maskLayer = CAShapeLayer()
maskLayer.frame = view.bounds
maskLayer.path = rectanglePath.CGPath
view.layer.mask = maskLayer

A simple solution is that you can enlarge layer's bounds a little bit to cover the edge of view's image:
CGFloat offset = 1.f; // .5f is also good enough
self.imageview.image = image;
self.imageview.layer.cornerRadius = 10.0;
self.imageview.layer.borderWidth = 3.0 + offset;
self.imageview.layer.borderColor = UIColor.whiteColor().CGColor;
self.imageview.layer.masksToBounds = YES;
[self.imageview.layer setBounds:CGRectMake(-offset,
-offset,
CGRectGetWidth(self.imageview.frame) + offset * 2.f,
CGRectGetHeight(self.imageview.frame) + offset * 2.f)];

Related

How To Draw the Oval Shape Layer to the UITextField

I need to draw the Oval shape to my textfield , I don't know how to draw using layer, if anyone help me for this design shadow,
Here's the Objective-C translation of Anton's answer since question is tagged for Objective-C and not Swift:
CAShapeLayer *shadowLayer = [[CAShapeLayer alloc] init];
shadowLayer.fillColor = [UIColor lightGrayColor].CGColor;
shadowLayer.lineWidth = 0.0f;
CGSize shadowSize = CGSizeMake(textField.bounds.size.width + 40, 40);
CGRect shawdowBounds = CGRectMake(0, 0, shadowSize.width, shadowSize.height);
shadowLayer.path = [UIBezierPath bezierPathWithOvalInRect:shawdowBounds].CGPath;
shadowLayer.bounds = shawdowBounds;
shadowLayer.position = CGPointMake(CGRectGetMidX(textField.bounds), CGRectGetMaxY(textField.bounds));
[containerView.layer insertSublayer:shadowLayer atIndex0];
You can use this code as an example:
let shadowLayer = CAShapeLayer()
// set your shadow color here
shadowLayer.fillColor = UIColor.lightGray.cgColor
shadowLayer.lineWidth = 0
// calc size of your shadow according to your design
let shadowSize = CGSize(width: textField.bounds.width + 40, height: 20)
let shadowBounds = CGRect(origin: .zero, size: shadowSize)
shadowLayer.path = UIBezierPath(ovalIn: shadowBounds).cgPath
shadowLayer.bounds = shadowBounds
shadowLayer.position = CGPoint(x: textField.bounds.midX, y: textField.bounds.maxY)
containerView.layer.insertSublayer(shadowLayer, at: 0)
Here I assume that you have some containerView, containing your text field as a subview and keep reference to the text field in variable called textField.

CAShapeLayer cutout in UIView

My goal is to create a pretty opaque background with a clear rounded rect inset
let shape = CGRect(x: 0, y: 0, width: 200, height: 200)
let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: shape, cornerRadius: 16).cgPath
maskLayer.fillColor = UIColor.clear.cgColor
maskLayer.fillRule = kCAFillRuleEvenOdd
let background = UIView()
background.backgroundColor = UIColor.black.withAlphaComponent(0.8)
view.addSubview(background)
constrain(background) {
$0.edges == $0.superview!.edges
}
background.layer.addSublayer(maskLayer)
// background.layer.mask = maskLayer
When I uncomment background.layer.mask = maskLayer, the view is totally clear. When I have it commented out, I see the semi-opaque background color but no mask cutout
Any idea what I'm doing wrong here? Thanks!
Here is a playground that I think implements your intended effect (minus .edges and constrain - that appear to be your own extensions).
a) used a red background instead of black with alpha (just to make the effect stand out)
b) set maskLayer.backgroundColor instead of maskLayer.fillColor
Adding the maskLayer as another layer to uiview is superfluous (as indicated by #Ken) but seems to do no harm.
import UIKit
import PlaygroundSupport
let bounds = CGRect(x: 0, y: 0, width: 400, height: 200)
let uiview = UIView(frame: bounds)
uiview.backgroundColor = UIColor.red.withAlphaComponent(0.8)
PlaygroundPage.current.liveView = uiview
let shape = CGRect(x: 100, y: 50, width: 200, height: 100)
let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: shape, cornerRadius: 16).cgPath
maskLayer.backgroundColor = UIColor.clear.cgColor
maskLayer.fillRule = kCAFillRuleEvenOdd
uiview.layer.mask = maskLayer
image of clear inset
On the other hand, if you intended a picture frame / colored-envelope-with-transparent-window effect like below, then see gist
image of picture frame
Your mask Layer has no size.
Add this line maskLayer.frame = shape
and you don't need background.layer.addSublayer(maskLayer)

Shadow on UIView layer

I've make a path in order to mask my view:
let path = // create magic path (uiview bounds + 2 arcs)
let mask = CAShapeLayer()
mask.path = path.cgPath
view.layer.masksToBounds = false
view.layer.mask = mask
Up to here all ok.
Now I would like to add a shadow that follows path, is it possibile?
I try in several way, the last one is:
mask.shadowPath = path.cgPath
mask.shadowColor = UIColor.red.cgColor
mask.shadowOffset = CGSize(width: 10, height: 2.0)
mask.shadowOpacity = 0.5
But this produce a partial shadow and with color of the original view..
With debug view hierarchy:
Any advice?
Final result should be similar to this, but with shadow that "follows" arcs on path.
When you add a mask to a layer, it clips anything outside that mask - including the shadow. To achieve this you'll need to add a "shadow" view below your masked view, that has the same path as the mask.
Or add a shadow layer to the masked view's superview.
let view = UIView(frame: CGRect(x: 50, y: 70, width: 100, height: 60))
view.backgroundColor = .cyan
let mask = CAShapeLayer()
mask.path = UIBezierPath(roundedRect: view.bounds, cornerRadius: 10).cgPath
view.layer.mask = mask
let shadowLayer = CAShapeLayer()
shadowLayer.frame = view.frame
shadowLayer.path = UIBezierPath(roundedRect: view.bounds, cornerRadius: 10).cgPath
shadowLayer.shadowOpacity = 0.5
shadowLayer.shadowRadius = 5
shadowLayer.masksToBounds = false
shadowLayer.shadowOffset = .zero
let container = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
container.backgroundColor = .white
container.layer.addSublayer(shadowLayer)
container.addSubview(view)
If you're going to be using this elsewhere, you could create a ShadowMaskedView that contains the shadow layer, and the masked view - maybe with a path property.
You can try this extension:
extension UIView {
func dropShadow() {
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOpacity = 0.5
self.layer.shadowOffset = CGSize(width: -1, height: 1)
self.layer.shadowRadius = 1
self.layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath
self.layer.shouldRasterize = true
}
}

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 add a shadow and CAShapeLayer to the same UIView

I'm trying to add a 6px radius to the bottom left and right corners of my view innerView, as well as a shadow to the whole of innerView. innerView is enclosed in a view called view2.
The only way I can get this to work is by using the following code:
let containerLayer = CALayer()
containerLayer.shadowColor = UIColor.blackColor().CGColor
containerLayer.shadowOffset = CGSizeMake(1, 2)
containerLayer.shadowOpacity = 0.2
containerLayer.shadowRadius = 2
let maskPath = UIBezierPath(roundedRect: innerView.bounds, byRoundingCorners: UIRectCorner.BottomLeft.union(.BottomRight), cornerRadii: CGSize(width: 6, height: 6)).CGPath
let maskLayer = CAShapeLayer()
maskLayer.path = maskPath
innerView.layer.mask = maskLayer
containerLayer.addSublayer(innerView.layer)
view2.layer.addSublayer(containerLayer)
However, since view2 is actually the contentView of a UITableViewCell, this code disables scrolling of the UITableView when dragging on containerLayer...
Swift 4 :
This Code Helped Me To Drop A Shadow To UIVIEW And Add CornerRadious Using UIBezierPath. Set Your View's Background Color As ClearColor
yourView.backgroundColor=UIColor.clear
let path1 = UIBezierPath(roundedRect:yourView.bounds,
byRoundingCorners:[.topRight, .bottomLeft],
cornerRadii: CGSize(width: 20, height: 20))
let maskLayer1 = CAShapeLayer()
maskLayer1.path = path1.cgPath
maskLayer1.fillColor = UIColor.white.cgColor
maskLayer1.shadowColor = UIColor.darkGray.cgColor
maskLayer1.shadowPath = maskLayer1.path
maskLayer1.shadowOffset = CGSize(width: 0.0, height: 2.0)
maskLayer1.shadowOpacity = 0.6
maskLayer1.shadowRadius = 2
yourView.layer.insertSublayer(maskLayer1, at: 0)
Solved it by embedding innerView in another view. I applied the shadow to the new view and the radius to the innerView:
func addShadowTo(shadowView: UIView, andRadiusTo radiusView: UIView) {
let maskPath = UIBezierPath(roundedRect: radiusView.bounds, byRoundingCorners: UIRectCorner.BottomLeft.union(.BottomRight), cornerRadii: CGSize(width: 6, height: 6)).CGPath
shadowView.layer.shadowPath = maskPath
shadowView.layer.shadowColor = UIColor.blackColor().CGColor
shadowView.layer.shadowOffset = CGSizeMake(1, 2)
shadowView.layer.shadowOpacity = 0.2
shadowView.layer.shadowRadius = 2
let maskLayer = CAShapeLayer()
maskLayer.path = maskPath
radiusView.layer.mask = maskLayer
}
If you are using Autolayout, then remove UIBezierPath. Try this code and custom one for yourself:
//This code custom a view with shadow and corner radius = 6
let shadowLayer: CALayer = shadowView.layer
shadowView.clipsToBounds = false
shadowLayer.shadowColor = 'your color'
shadowLayer.shadowOffset = CGSizeZero
shadowLayer.shadowOpacity = 1
shadowLayer.shadowRadius = 3
shadowLayer.shouldRasterize = true
let innerLayer: CALayer = innerView.layer
innerLayer.cornerRadius = 6
innerView.clipsToBounds = true
innerLayer.borderColor = 'your color'.CGColor
innerLayer.borderWidth = 1
The "innerView" is inside "shadowView" (on my xib file).

Resources