I want to add a drop shadow using CALayer to a UI(Image)View. The following code works generally fine
previewImage.layer.shadowColor = [[UIColor blackColor] CGColor];
previewImage.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
previewImage.layer.shadowOpacity = 1.0f;
previewImage.layer.shadowRadius = 8.0f;
However, this only works if I create that view programmatically and ad开发者_运维问答d it as a subview to my main view. When that view is set up in InterfaceBuilder and defined as an IBOutlet UIImageView this does NOT work. No shadow appears. So what am I missing here?
Add a file named UIView.swift in your project (or just paste this in any file) :
import UIKit
@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
}
}
}
Then this will be available in Interface Builder for every view in the Utilities Panel > Attributes Inspector :
You can easily set the shadow now.
Notes:
- The shadow will only appear at runtime.
- clipsToBounds
should be false (by default it is)
I know this question has long, but recently i was in a similar situation so i decided to put my answer for those who are in a situation like this.
I wanted to be able to set the borderColor
and shadowColor
on an UIView
through the Interface Builder, but the type of a layer’s borderColor
property is CGColor
(just like shadowColor
) which is not one of the types allowed to be changed in the user-defined runtime attributes feature.
So i made an extension for CALayer
and i added two properties called borderColorIB and shadowColorIB that are of type UIColor:
RuntimeAttributes.h
@import QuartzCore;
@interface CALayer (IBConfiguration)
@property(nonatomic, assign) UIColor* borderColorIB;
@property(nonatomic, assign) UIColor* shadowColorIB;
@end
RuntimeAttributes.m
#import <UIKit/UIKit.h>
#import "RuntimeAttributes.h"
@implementation CALayer (IBConfiguration)
-(void)setBorderColorIB:(UIColor*)color
{
self.borderColor = color.CGColor;
}
-(UIColor*)borderColorIB
{
return [UIColor colorWithCGColor:self.borderColor];
}
-(void)setShadowColorIB:(UIColor*)color
{
self.shadowColor = color.CGColor;
}
-(UIColor*)shadowColorIB
{
return [UIColor colorWithCGColor:self.shadowColor];
}
@end
Now i alredy be able to set this two properties through Interface Builder like this:
- In the 'user-defined runtime attributes' section (Identity inspector)
Make sure the UIView is selected, and add the following runtime attributes:
- layer.borderWidth, Number, 1
- layer.borderColorIB, Color, someColor
<- my custom property to set the borderColor
- layer.shadowColorIB, Color, someColor
<- my custom property to set the shadowColor
- layer.shadowOpacity, Number, 0.8
- layer.shadowOffset, size, {5,5}
- layer.cornerRadius, Number, 5
Here is an image to show you how i did:
... and The result will be apparent during runtime, not in Xcode:
i hope this can help some people out there!
I'm not sure what the issue is - ensure your UIImageView
's clipsToBounds
property is set to NO
. You can do this in viewDidLoad
after loading from the nib file by referencing your IBOutlet. You shouldn't need to wrap it in another view.
Edit
In light of you needing your image to be scaled using aspect fill, you can use the contentsRect
property of the UIImageView
's underlying layer to 'simulate' the effect of the contents clipping. contentsRect
is rectangle in the unit coordinate space of the layer's content (in this case your image) that defines a sub-rectangle of the contents that should be drawn.
With a little bit of maths, we can find this rectangle by comparing the image view size with the image size (accounting for the aspect fill scaling):
CGSize imageViewSize = previewImage.size;
CGSize imageSize = previewImage.image.size;
// Find the scaling required for the image to fit the image view (as for aspect fill).
CGFloat imageWidthScale = fabsf(imageViewSize.width / imageSize.width);
CGFloat imageHeightScale = fabsf(imageViewSize.height / imageSize.height);
CGFloat imageScale = (imageWidthScale > imageHeightScale) ? imageWidthScale : imageHeightScale;
// Determine the new image size, after scaling.
CGSize scaledImageSize = CGSizeApplyAffineTransform(imageSize, CGAffineTransformMakeScale(imageScale, imageScale));
// Set the layer's contentsRect property in order to 'clip' the image to the image view's bounds.
previewImage.layer.contentsRect = CGRectMake(((scaledImageSize.width - imageViewSize.width) / 2.0f) / scaledImageSize.width,
((scaledImageSize.height - imageViewSize.height) / 2.0f) / scaledImageSize.height,
imageViewSize.width / scaledImageSize.width,
imageViewSize.height / scaledImageSize.height);
Doing this, you can leave clipsToBounds
set to NO
for your image view, but the image will still appear clipped. If you need to change the image view size at all, it might be convenient to wrap this code up into a method that takes a UIImageView
as a parameter.
I hope this helps.
Swift 5 Xcode 11.3.1 version of mauricioconde's answer. Note that without @IBInspectable it wouldn't work.
import UIKit
import QuartzCore
extension CALayer {
@IBInspectable
var shadowUIColor: UIColor? {
get { shadowColor != nil ? UIColor(cgColor: shadowColor!) : nil }
set { shadowColor = newValue?.cgColor }
}
}
UIView shadow with corner radius + interface builder - swift4
extension UIView {
@IBInspectable
var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
if shadowOpacity > 0.0 {
layer.masksToBounds = false
}
else {
layer.masksToBounds = true
}
}
}
@IBInspectable
var borderWidth: CGFloat {
get {
return layer.borderWidth
}
set {
layer.borderWidth = newValue
}
}
@IBInspectable
var borderColor: UIColor? {
get {
if let color = layer.borderColor {
return UIColor(cgColor: color)
}
return nil
}
set {
if let color = newValue {
layer.borderColor = color.cgColor
} else {
layer.borderColor = nil
}
}
@IBInspectable var shadowColor: UIColor? {
set {
layer.shadowColor = newValue!.cgColor
}
get {
if let color = layer.shadowColor {
return UIColor(cgColor: color)
}
else {
return nil
}
}
}
@IBInspectable var shadowOpacity: Float {
set {
layer.shadowOpacity = newValue
}
get {
return layer.shadowOpacity
}
}
@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)
}
}
@IBInspectable var shadowRadius: CGFloat {
set {
layer.shadowRadius = newValue
}
get {
return layer.shadowRadius
}
}
}
精彩评论