开发者

UIView shadow and InterfaceBuilder

开发者 https://www.devze.com 2023-04-06 16:30 出处:网络
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];

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 :

UIView shadow and InterfaceBuilder

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 CALayerand 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:

  1. In the 'user-defined runtime attributes' section (Identity inspector)
  2. 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:

UIView shadow and InterfaceBuilder

... and The result will be apparent during runtime, not in Xcode:

UIView shadow and InterfaceBuilder

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 and InterfaceBuilder


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
            }
        }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消