5

I'm having a hard time getting a drop shadow to show when I am using "User Defined Runtime Attributes".

It seems to work completely fine if I use code, as follows.

func formatView(view: UIView, cornerRadius: Bool) {

    if (cornerRadius) {view.layer.cornerRadius = 12 }
    view.layer.shadowColor = UIColor.black.cgColor
    view.layer.shadowOffset = CGSize.zero
    view.layer.shadowRadius = 3
    view.layer.shadowOpacity = 0.3
}

But when I try it with User Defined Runtime Attributes it doesn't show anymore. These are the ones I'm currently using.

enter image description here

The only thing that is weird is if I remove the layer.shadowColor attribute, then it seems to work again. But I can no longer control the color. It seems to default to black, but if I ever decide to choose a grey color instead, I wouldn't be able to change it.

Is this because the Color Attribute is a UIColor and shadowColor expects a CGColor?

Jason Brady
  • 1,034
  • 1
  • 11
  • 27
  • 1
    note that somewhat confusingly, for a UILabel, the ".shadowColor" is indeed just a UIColor (!!!!!!!!!!) so you CAN USE just the u.d.r.a !!! – Fattie Feb 28 '18 at 16:13

1 Answers1

12

It is indeed as you stated because the Color type in the User Defined Runtime Attributes panel creates a UIColor, but layer.borderColor holds a cgColor type.

You could solve this by creating a category that allows a proxy color to be set through Interface Builder:

extension CALayer {
    var borderUIColor: UIColor {
        set {
            self.borderColor = newValue.cgColor
        }

        get {
            return UIColor(cgColor: self.borderColor!)
        }
    }
}

But a much nicer way is to use IBDesignable instead of User Defined Runtime Attributes, it is more clear.

You do this by adding a new swift file named UIViewExtentions.swift in your project (or just paste this in any file) :

import UIKit

@IBDesignable extension UIView {
    @IBInspectable var borderColor:UIColor? {
        set {
            layer.borderColor = newValue!.cgColor
        }
        get {
            if let color = layer.borderColor {
                return UIColor(cgColor:color)
            }
            else {
                return nil
            }
        }
    }
    @IBInspectable var borderWidth:CGFloat {
        set {
            layer.borderWidth = newValue
        }
        get {
            return layer.borderWidth
        }
    }
    @IBInspectable var cornerRadius:CGFloat {
        set {
            layer.cornerRadius = newValue
            clipsToBounds = newValue > 0
        }
        get {
            return layer.cornerRadius
        }
    }
}

Then this will be available in Interface Builder for every button, imageView, label, etc. in the Utilities Panel > Attributes Inspector:

enter image description here

Now if you set you values in the Attributes Inspector and look back at the User Defined Runtime Attributes, you'll see they are automatically filed out for you!

EDIT: For more, see: http://nshipster.com/ibinspectable-ibdesignable/

jo.On
  • 508
  • 6
  • 9
  • 2
    I just wanted to comment back that this worked perfectly for me. And that I'm excited on the possibilities I can use this for in the future. You also helped me to finally understand the power of extensions. One question though, should my changes take affect in the main.storyboard or only when I run the app. Currently I see them in the running, just not on the storyboard. – Jason Brady Jan 20 '17 at 17:21
  • 2
    I'm glad to be of help :) For now when you extend an existing control, the properties are not rendered in real time but only when you run the app. Currently, the @IBDesignable and @IBInspectable live rendering is only supported on custom controls using subclasses like `class CustomButton: UIButton { @IBInspectable var highlightedBackgroundColor:UIColor? { } }` – jo.On Jan 22 '17 at 12:23