TableView that has a shadow in its cells - ios

Hi I have normal TableView and I want to update it to be with spaces/gaps between each cell in TableView also from each side(up, down, left, right )
As you can see here:

First of all, you need to create a new UITableViewCell that has one view inside it contentView. You need to make this view that you added smaller than your view and filled so you can create gapes between each tableviewCell.
Like that:
Than you create constraints that will make the view smaller and fit inside the cell like that:
After that you can use this extension here to create the shadow in the view:
https://stackoverflow.com/a/40720122/8792385
The code snippet is something like this:
extension UIView {
// OUTPUT 1
func dropShadow(scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.5
layer.shadowOffset = CGSize(width: -1, height: 1)
layer.shadowRadius = 1
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
}
then you just need to create a reference for your view and in your configuration cell method you call
view.dropShadow()
the result will look something like this:

Related

How to implement a layer blur in Swift

In figma, there's an effect you can apply to a view called a layer blur.
I cannot figure out how to replicate this sort of effect as its own view in Swift.
For example, say I want to add a view that goes under a button with a layer effect as so:
I have tried exporting the blur view from figma and using it as an image in code, but it just ends up like this:
These are the settings for the layer blur view I want to implement:
Any and all help would be greatly appreciated.
You can add drop shadow to a button this way.
Add an extension of UIView Like this.
extension UIView{
func dropShadow() {
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.16
layer.shadowOffset = CGSize(width: 0, height: 3)
layer.shadowRadius = 3
}
}
Add drop shadow to your button by adding this line.
yourButton.dropShadow()
enter image description hereYou can add a shadow to your Button(View) for that purpose
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowRadius = 20
view.layer.shadowOpacity = 1
view.layer.shadowOffset = .zero
shadowOpacity: sets the transparency, where 0 will be without shadow, and 1 is the strongest.
shadowRadius: sets how wide the shadow is.
shadowOffset: sets the distance between the view and shadow.

UICollectionViewCell inconsistent shadow with decoration view

I am using IGListKit with my UICollectionView. For now the CollectionView is pretty simple, as it only have one cell per section. That cell contains an inner horizontal UICollectionView as an image slideshow.
As I need some shadowing around my entire sections, I am using Decoration Views, and apply it a border shadow: layer.shadowPath
I noticed something weird, the shadow's opacity changes upon the picture currently displayed in the slideshow. If the picture (or a portion of the picture) is bright, you can see the shadow opacity changing.
I don't know if it is something I can fix.
You can clearly see that if I take a screenshot while swiping pictures in the slideshow, the shadow on the top is darker on one side.
Code for decoration view:
class FeedItemBackgroundShadowView: UICollectionReusableView {
// MARK: Initialization
... Constructors calling setup
// MARK: Setup
override func layoutSubviews() {
super.layoutSubviews()
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: Constants.cornerRadius).cgPath
}
func setup() {
self.layer.cornerRadius = 12.0
self.layer.backgroundColor = UIColor.white.cgColor
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0.0, height: 2.5)
self.layer.shadowRadius = 12.0
self.layer.shadowOpacity = 0.35
}
Rest of the code a simply UICollectionViewCells that embed a UICollectionView
Are you certain it isn't an optical illusion?

How can i drop shadow to top and bottom of view which is inside the table view cell?

I have a view inside the table view cell and I want to show shadow to that view if below situations
if view on first cell then drop shadow to top, left, right.
if view on last cell then drop shadow to bottm, left, right.
other wise drop shadow only on left and right.
Thank You.
yourView.layer.shadowColor = UIColor.black.cgColor
yourView.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
yourView.layer.shadowOpacity = 0.5
yourView.layer.shadowRadius = 2.0
And adapt the code below to the way you want for the corners that you'd like to drop shadow.
var shadowRect: CGRect = yourView.bounds.insetBy(dx: 0, dy: 4) // inset top and bottom of the cell
yourView.layer.shadowPath = UIBezierPath(rect: shadowRect).cgPath
put this code in your class
extension UIView {
func dropShadow(scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.2
layer.shadowOffset = CGSize.zero
layer.shadowRadius = 5
}}
Then call it:--
YourView.dropShadow(scale: true)

How to add shadow to a view inside cell in tableview in ios?

I have created a tableviewcell in XIB and used it in tableview using registering the class. I am using autolayout here.
But the problem is when i loading the tableview in viewcontroller the shadow of the view inside the cell is not setting correctly. it exceeds its bounds as i shown in picture with red box.
But when is scrolling the tableview then the shadow of the view is shown correctly as expected.
The code used in cellForRowAtIndexPath is shown below:
let layer = cell.cellContectView.layer
layer.shadowOffset = CGSizeMake(1.0, 1.0)
layer.shadowColor = UIColor.blackColor().CGColor
layer.shadowRadius = 5
layer.shadowOpacity = 0.2
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(rect: layer.bounds).CGPath
Please note: the blurred text is not an issue. Its hidden for security reason
You can try to remove the code for the layer from the cellForRowAtIndexPath and put it in your cell class in:
override func prepareForReuse() {
let layer = self.contentView.layer
layer.shadowOffset = CGSizeZero
layer.shadowColor = UIColor.blackColor().CGColor
layer.shadowRadius = 5
layer.shadowOpacity = 0.2
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(rect: layer.bounds).CGPath
}
Adding a shadow inside UITableViewCell using QuartzCore code - can decrease scroll performance dramatically.
Better to create shadow graphic asset and add it as UIImageView.

How to keep a round imageView round using auto layout?

How do I turn a rectangular image view into a circular image view that can hold shape in auto layout without setting width and height restraints? Thereby allowing the imageView to define it’s size, and size bigger and smaller relative to objects around it with leading, trailing, top, and bottom constraints.
I asked a similar question the other day, but I think this might be posed in a more concise way. Thanks so much!
EDIT
Ok, I started over to make this as simple as possible. I have a view named "Cell" and a UIImageView named "dog" within the cell, and that's it. I don't have "unable to simultaneously satisfy constraints" in the console anymore, just two simple views using auto layout. I'm still trying to use this code to round the UIImageView:
profileImageView.layer.cornerRadius = profileImageView.frame.size.width / 2
profileImageView.clipsToBounds = true
Here is the cell constraint setup:
Here is the profile pic constraint setup:
Here is the result without the code, no rounding, but nice and square:
Here is the result with the code to round:
This makes no sense to me, because without the rounding code the image is square, and with the code it's diamond shaped. If it's square shouldn't it be a circle with no issues?
EDIT 2
Here's what happens when I remove the bottom constraint and add a multiplier of .637 for equal height to superview.
Unfortunately you cannot do this using cornerRadius and autolayout — the CGLayer is not affected by autolayout, so any change in the size of the view will not change the radius which has been set once causing, as you have noticed, the circle to lose its shape.
You can create a custom subclass of UIImageView and override layoutSubviews in order to set the cornerRadius each time the bounds of the imageview change.
EDIT
An example might look something like this:
class Foo: UIImageView {
override func layoutSubviews() {
super.layoutSubviews()
let radius: CGFloat = self.bounds.size.width / 2.0
self.layer.cornerRadius = radius
}
}
And obviously you would have to constrain the Foobar instance's width to be the same as the height (to maintain a circle). You would probably also want to set the Foobar instance's contentMode to UIViewContentMode.ScaleAspectFill so that it knows how to draw the image (this means that the image is likely to be cropped).
Setting radius in viewWillLayoutSubviews will solve the problem
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
profileImageView.layer.cornerRadius = profileImageView.frame.height / 2.0
}
create new interface in your .h file like
#interface CornerClip : UIImageView
#end
and implementation in .m file like
#implementation cornerClip
-(void)layoutSubviews
{
[super layoutSubviews];
CGFloat radius = self.bounds.size.width / 2.0;
self.layer.cornerRadius = radius;
}
#end
now just give class as "CornerClip" to your imageview.
100% working... Enjoy
First of all, I should mention that u can get a circle shape for your UIView/UIImageView only if the width and height will be equal. It's important to understand. In all other cases (when width != height), you won't get a circle shape because the initial shape of your UI instance was a rectangle.
OK, with this so UIKit SDK provides for developers a mechanism to manipulate the UIview's layer instance to change somehow any of layer's parameters, including setting up a mask to replace the initial shape of UIView element with the custom one. Those instruments are IBDesignable/IBInspectable. The goal is to preview our custom views directly through Interface Builder.
So using those keywords we can write our custom class, which will deal only with the single condition whether we need to round corners for our UI element or not.
For example, let's create the class extended from the UIImageView.
#IBDesignable
class UIRoundedImageView: UIImageView {
#IBInspectable var isRoundedCorners: Bool = false {
didSet { setNeedsLayout() }
}
override func layoutSubviews() {
super.layoutSubviews()
if isRoundedCorners {
let shapeLayer = CAShapeLayer()
shapeLayer.path = UIBezierPath(ovalIn:
CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.width, height: bounds.height
)).cgPath
layer.mask = shapeLayer
}
else {
layer.mask = nil
}
}
}
After setting the class name for your UIImageView element (where the dog picture is), in your storyboard, you will get a new option, appeared in the Attributes Inspector menu (details at the screenshot).
The final result should be like this one.
It seems when you add one view as a subview of another that netted view will not necessarily have the same height as its superview. That's what the problem seems like. The solution is to not add your imageView as a subview, but have it on top of your backgroundView. In the image below I'm using a UILabel as my backgroundView.
Also in your case, when you're setting the cornerRadius use this: let radius: CGFloat = self.bounds.size.height / 2.0.
With my hacky solution you'll get smooth corner radius animation alongside frame size change.
Let's say you have ViewSubclass : UIView. It should contain the following code:
class ViewSubclass: UIView {
var animationDuration : TimeInterval?
let imageView = UIImageView()
//some imageView setup code
override func layoutSubviews() {
super.layoutSubviews()
if let duration = animationDuration {
let anim = CABasicAnimation(keyPath: "cornerRadius")
anim.fromValue = self.imageView.cornerRadius
let radius = self.imageView.frame.size.width / 2
anim.toValue = radius
anim.duration = duration
self.imageView.layer.cornerRadius = radius
self.imageView.layer.add(anim, forKey: "cornerRadius")
} else {
imageView.cornerRadius = imageView.frame.size.width / 2
}
animationDuration = nil
}
}
An then you'll have to do this:
let duration = 0.4 //For example
instanceOfViewSubclass.animationDuration = duration
UIView.animate(withDuration: duration, animations: {
//Your animation
instanceOfViewSubclass.layoutIfNeeded()
})
It's not beautiful, and might not work for complex multi-animations, but does answer the question.
Swift 4+ clean solution based on omaralbeik's answer
import UIKit
extension UIImageView {
func setRounded(borderWidth: CGFloat = 0.0, borderColor: UIColor = UIColor.clear) {
layer.cornerRadius = frame.width / 2
layer.masksToBounds = true
layer.borderWidth = borderWidth
layer.borderColor = borderColor.cgColor
}
}
Sample usage in UIViewController
1.Simply rounded UIImageView
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
imageView.setRounded()
}
2.Rounded UIImageView with border width and color
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
imageView.setRounded(borderWidth: 1.0, borderColor: UIColor.red)
}
write this code
override viewDidLayoutSubViews() {
profileImageView.layer.cornerRadius = profileImageView.frame.size.width / 2
profileImageView.clipsToBounds = true
}
in this case it will called after calculating the autolayout calculations in the first code you called the cornerradius code before calculating the actual size of the view cuz it's dynamically calculated using aspect ratio , the actual corner radius is calculated before equaling the width and the height of the view
I have same problem, and Now I get what happened here, hope some ideas can help you:
there are something different between the tows:
your profileImageView in storyboard
your profileImageView in viewDidLoad
the size of bound and frame is different when viewDidLoad and in storyboard,just because view is resize for different device size.
You can try it print(profileImageView.bounds.size) in viewDidLoad and viewDidAppear you will find the size in viewDidLoad you set cornerRadius is not the real "running" size.
a tips for you:
you can use a subClass of ImageView to avoid it, or do not use it in storyboard,
If you have subclassed the UIImageView. Then just add this piece of magical code in it.
Written in : Swift 3
override func layoutSubviews() {
super.layoutSubviews()
if self.isCircular! {
self.layer.cornerRadius = self.bounds.size.width * 0.50
}
}
I am quite new to iOS native development, but I had the same problem and found a solution.
So the green background has this constraints:
backgroundView.translatesAutoresizingMaskIntoConstraints = false
backgroundView.topAnchor.constraint(equalTo: superview!.safeAreaLayoutGuide.topAnchor).isActive = true
backgroundView.leftAnchor.constraint(equalTo: superview!.leftAnchor).isActive = true
backgroundView.widthAnchor.constraint(equalTo: superview!.widthAnchor).isActive = true
backgroundView.heightAnchor.constraint(equalTo: superview!.heightAnchor, multiplier: 0.2).isActive = true
The image constraints:
avatar.translatesAutoresizingMaskIntoConstraints = false
avatar.heightAnchor.constraint(equalTo: backgroundView.heightAnchor, multiplier: 0.8).isActive = true
avatar.widthAnchor.constraint(equalTo: backgroundView.heightAnchor, multiplier: 0.8).isActive = true
avatar.centerYAnchor.constraint(equalTo: backgroundView.centerYAnchor, constant: 0).isActive = true
avatar.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor, constant: 20).isActive = true
on viewWillLayoutSubviews() method I set the corner radius
to
avatar.layer.cornerRadius = (self.frame.height * 0.2 * 0.8) / 2
Basically, I am simply calculating the height of the image and then divide it by 2. 0.2 is the backgroungView height constraint multiplier and 0.8 the image width/height constraint multiplier.
Solution: Crop the image
[imageView setImage:[[imageView image] imageWithRoundedCorners:imageView.image.size.width/2]];
imageView.contentMode = UIViewContentModeScaleAspectFill;
I was looking for the same solution for profile pictures. After some hit and try and going through available functions, I came across something which works and is a nice way to ensure its safe. You can use the following function to crop out a round image from the original image and then you need not worry about the corner radius.
Post this even if your view size changes the image remains round and looks good.
Add a 1:1 aspect ratio constraint to the imageView for it to remain circular, despite any height or width changes.
I added custom IBInspectable cornerRadiusPercent, so you can do it without any code.
class RoundButton: UIButton {
override var bounds: CGRect {
didSet {
updateCornerRadius()
}
}
//private var cornerRadiusWatcher : CornerRadiusPercent?
#IBInspectable var cornerRadiusPercent: CGFloat = 0 {
didSet {
updateCornerRadius()
}
}
func updateCornerRadius()
{
layer.cornerRadius = bounds.height * cornerRadiusPercent
}
}
Can be easily done by creating an IBOutlet for the constraint which needs to be changed at runtime. Below is the code:
Create a IBOutlet for the constraint that needs to be changed at run time.
#IBOutlet var widthConstraint: NSLayoutConstraint!
Add below code in viewDidLoad():
self.widthConstraint.constant = imageWidthConstraintConstant()
Below function determines for device types change the width constraint accordingly.
func imageWidthConstraintConstant() -> CGFloat {
switch(self.screenWidth()) {
case 375:
return 100
case 414:
return 120
case 320:
return 77
default:
return 77
}
}

Resources