1

I have a custom view with corner radius and shadow, and I'm trying to add an image inside it. The image should be inside the rounded area. But I'm getting a black area in the view, where the image should be, (or the image as an square, without round corners, on my last attempt). How can I add this image?

The wrong result I'm getting:

enter image description here

My code:

import Foundation
import UIKit

@IBDesignable

class RoundedSquareView: UIView {
  var shadowLayer: CAShapeLayer!
  @IBInspectable var image: UIImage! = UIImage()

  override func layoutSubviews() {
    super.layoutSubviews()

    if shadowLayer == nil {
      shadowLayer = CAShapeLayer()
      shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 14).cgPath
      shadowLayer.fillColor = UIColor.white.cgColor

      shadowLayer.shadowColor = UIColor.darkGray.cgColor
      shadowLayer.shadowPath = shadowLayer.path
      shadowLayer.shadowOffset = CGSize(width: 0, height: 10.0)
      shadowLayer.shadowOpacity = 0.3
      shadowLayer.shadowRadius = 10

      layer.insertSublayer(shadowLayer, at: 0)
    }

    let imgLayer = CAShapeLayer()
    let myImage = image.cgImage
    imgLayer.frame = bounds
    imgLayer.masksToBounds = true
    imgLayer.contents = myImage
    imgLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 14).cgPath
    imgLayer.cornerRadius  = 14
    layer.addSublayer(imgLayer)
  }

}

EDIT:

The expected result:

enter image description here

4 Answers4

2

You could try a simple extension. Here's an example:

extension UIImageView {
    func render(with radius: CGFloat) {

        // add the shadow to the base view
        self.backgroundColor = UIColor.clear
        self.layer.shadowColor = UIColor.black.cgColor
        self.layer.shadowOffset = CGSize(width: 0, height: 10)
        self.layer.shadowOpacity = 0.3
        self.layer.shadowRadius = 10

        // add a border view for corners
        let borderView = UIView()
        borderView.frame = self.bounds
        borderView.layer.cornerRadius = radius
        borderView.layer.masksToBounds = true
        self.addSubview(borderView)

        // add the image for clipping
        let subImageView = UIImageView()
        subImageView.image = self.image
        subImageView.frame = borderView.bounds
        borderView.addSubview(subImageView)

        //for performance
        self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: 10).cgPath
        self.layer.shouldRasterize = true
        self.layer.rasterizationScale = UIScreen.main.scale
    }
}

Usage:

myImageView.image = image
myImageView.render(with: 10)

Obviously you can add as many parameters as you want to the extension, set defaults, break it into separate methods, etc.

Result:

enter image description here

Frankie
  • 11,508
  • 5
  • 53
  • 60
1

You can just add the image in and give it a few attributes to make it round. When you have the UImage selected click on the attributes tab and click on the '+' and type in

layer.cornerRadius

And change it to a number instead of a string. All number 1-50 work. If you want a perfect circle then type in 50.

Oren Edrich
  • 674
  • 1
  • 7
  • 23
  • I'm already doing this. Check the last part of the code. Isn't working that way :( – Charles Prado Feb 16 '17 at 18:05
  • just edited the post adding the expected result. Please, take a look. PS: I'm not applying cornerRadius to the view on storyboard because that kills the shadow layer. – Charles Prado Feb 16 '17 at 18:14
1

You have to do the following below super.layoutSubviews():

self.clipToBound = true

What is ClipToBound?

clipsToBounds property

A Boolean value that determines whether subviews are confined to the bounds of the view.

Discussion Setting this value to YES causes subviews to be clipped to the bounds of the receiver. If set to NO, subviews whose frames extend beyond the visible bounds of the receiver are not clipped. The default value is NO.

J Manuel
  • 3,010
  • 22
  • 39
  • The image layer already have imgLayer.masksToBounds = true. Isn't working. This should work on this custom view. The view should be rounded, with a shadow layer, and have an image inside. – Charles Prado Feb 16 '17 at 18:08
  • I'm talking about clipToBounds, no masksToBounds (Here is the difference: http://stackoverflow.com/a/39466262/4077559) . I just edited my answer @CharlesPrado – J Manuel Feb 16 '17 at 18:09
  • just edited the post adding the expected result. Please, take a look. PS: I'm not applying cornerRadius to the view on storyboard because that kills the shadow layer. – Charles Prado Feb 16 '17 at 18:13
1

You could try to remove the :

let imgLayer = CAShapeLayer()
let myImage = image.cgImage

And replace the code with:

Image.frame = bounds
Image.masksToBounds = true
Image.contents = myImage
Image.path = UIBezierPath(roundedRect: bounds, cornerRadius: 14).cgPath
Image.cornerRadius  = 14

Xcode might not be liking the alias that you giving the image.

Then you also need to write in the brackets for the UIView

Layer.addsublayer(Image)
Oren Edrich
  • 674
  • 1
  • 7
  • 23