0

I'm trying to show the shadow of red/green behind my profile image when I clicked play/pause button.

I've tried

@objc func toggleButton(_ sender: UIButton) {

    //Play
    if sender.image(for: .normal) == UIImage(named: "play") {

        profileImage.layer.borderColor  = UIColor(hexString: "#8e8e8e").cgColor
        profileImage.layer.shadowColor = UIColor(hexString: "#099d57").cgColor
        profileImage.layer.cornerRadius = profileImage.frame.width / 2
        profileImage.layer.shadowOpacity = 1
        profileImage.layer.shadowOffset = CGSize.zero
        profileImage.layer.shadowRadius = 10
        profileImage.layer.shouldRasterize = true
        profileImage.layer.masksToBounds = false

    } else {
    //Pause

        profileImage.layer.borderColor  = UIColor(hexString: "#434343").cgColor
        profileImage.layer.shadowColor = UIColor(hexString: "#df544a").cgColor
        profileImage.layer.cornerRadius = profileImage.frame.width / 2
        profileImage.layer.shadowOpacity = 1
        profileImage.layer.shadowOffset = CGSize.zero
        profileImage.layer.shadowRadius = 10
        profileImage.layer.shouldRasterize = true
        profileImage.layer.masksToBounds = false
    }
}

I couldn't seem to get it to work. Can someone please give me a little hints on how to achive what I am after.

code-8
  • 54,650
  • 106
  • 352
  • 604
  • 1
    Problem is, that you need to have shadow visible, so you need to set either `masksToBounds` or `clipsToBounds` to `false`, but then, your view won't be rounded. What now? Oh, so I found this answer which is exactly what you need for having shadow behind rounded `UIImageView`: [creating a shadow for a uiimageview that has rounded corners](https://stackoverflow.com/a/41475658/10253094) – Robert Dresler Feb 05 '19 at 17:32

2 Answers2

1

If you set clipsToBounds to true, this will round the corners but prevent the shadow from appearing. In order to resolve this, you can create two views. The container view should have the shadow, and its subview should have the rounded corners.

The container view has clipsToBounds set to false, and has the shadow properties applied. If you want the shadow to be rounded as well, use the UIBezierPath constructor that takes in a roundedRect and cornerRadius.

let outerView = UIView(frame: profileImage.layer.frame)
outerView.clipsToBounds = false
outerView.layer.shadowColor = UIColor.black.cgColor
outerView.layer.shadowOpacity = 1
outerView.layer.shadowOffset = CGSize.zero
outerView.layer.shadowRadius = 10
outerView.layer.shadowPath = UIBezierPath(roundedRect: outerView.bounds, cornerRadius: 10).cgPath

Next, set the image view (or any other type of UIView) to be the same size of the container view, set clipsToBounds to true, and give it a cornerRadius.

let myImage = UIImageView(frame: outerView.bounds)
myImage.clipsToBounds = true
myImage.layer.cornerRadius = 10

Finally, remember to make the image view a subview of the container view.

outerView.addSubview(myImage)
Arash Etemad
  • 1,827
  • 1
  • 13
  • 29
  • I tried your code, it has no effect when I click on my play/pause button. Look at the code and the result here : https://i.imgur.com/g5cuC1O.png – code-8 Feb 05 '19 at 17:13
  • of course you should set the frame of `outerView` as your profileView. I updated answer, check it again. – Arash Etemad Feb 05 '19 at 17:18
  • Y0ur latest code is not working still. So sorry.. I see 0 effect. Can I use your exact code and test ? – code-8 Feb 05 '19 at 17:21
  • 1
    @kyo That is because you are discarding your `outerView` instance right after setting it up. Right after `outerView.addSubview(myImage)` line in your imgur link to be exact. You need to setup your image view inside a container to begin with, like in `awakeFromNib()` or in the storyboard/xib file itself and then add/remove the shadow at runtime in your button action function. – Pranay Feb 05 '19 at 17:22
  • 1
    @kyo As I said, your problem is `outerView.addSubview(myImage)` after this line, you are not doing anything with the `outerView` instance. If you do something like `self.view.addSubview(outerView)` or `self.addSubview(outerView)` depending on your setup, you should see a result. But then, it is not a good way to implement this since it will be adding a new subview each time your user clicks the button. So you need to set it up once in the beginning. This could be either in the interface builder or in `awakeFromNib()` or some other method – Pranay Feb 05 '19 at 17:26
  • @Pranay Can you please help suggest base on my code ? I don't know enough to make changes base on your suggestions. I'n new to iOS. – code-8 Feb 05 '19 at 17:29
  • @kyo I definitely can. But I need more code from you in order to do so. What is the class which contains the `profileImage` view? I would say, update your answer with more code from your class (of course obscuring code you feel is sensitive to your project) and I will post an answer accordingly. – Pranay Feb 05 '19 at 17:33
  • @Pranay I update my post. Please let me know if good enough. Looking forward to try your answer – code-8 Feb 05 '19 at 17:34
  • @kyo Yeah that's good enough. I just posted an answer. Please note that I typed it in the SO editor and not in Xcode so there may be a syntax or compiler error I didn't catch. Check it out and let me know if that works! – Pranay Feb 05 '19 at 17:47
1

Ok, you can do something like this:

// Replace this:
@IBOutlet weak var profileImage: UIImageView!
// With this:
@IBOutlet weak var profileImageContainer: UIView! // Change this to a UIView both in your storyboard or nib and here.
private weak var profileImageView: UIImageView? // Add this new variable

Then in your awake from nib method:

override func awakeFromNib() {
        super.awakeFromNib()

        /* rest of your setup */
        this.setupProfileImageView()    
    }

private func setupProfileImageView() -> Void {
    if let superview = self.profileImageContainer {
        superview.clipsToBounds = false
        let imageView = UIImageView(frame: superview.bounds)
        superview.addSubview(imageView)
        imageView.layer.cornerRadius = imageView.frame.height / 2.0
        imageView.layer.borderColor = // your color
        imageView.clipsToBounds = true
        imageView.contentMode = .scaleAspectFill
        imageView.image = //your profile image here
        self.profileImageView = imageView
        // You may add constraints to pin your imageview here if you'd like. I would recommend that.
    }
}

@objc func toggleButton(_ sender: UIButton) {

    // Here I would try to compare the state if you are updating your state correctly instead of comparing the image. If this doesn't work, you may stick to what works for you
    if sender.state == .normal {

        self.profileImageView?.layer.borderColor  = UIColor(hexString: "#8e8e8e").cgColor
        self.profileImageContainer.layer.shadowColor = UIColor(hexString: "#099d57").cgColor
        self.profileImageContainer.layer.shadowOpacity = 1
        self.profileImageContainer.layer.shadowOffset = CGSize.zero
        self.profileImageContainer.layer.shadowRadius = 10
        self.profileImageContainer.layer.shouldRasterize = true
    } else {
    //Pause
        self.profileImageContainer.layer.borderColor  = UIColor(hexString: "#434343").cgColor
        self.profileImageContainer.layer.shadowColor = UIColor(hexString: "#df544a").cgColor
        self.profileImageContainer.layer.shadowOpacity = 1
        self.profileImageContainer.layer.shadowOffset = CGSize.zero
        self.profileImageContainer.layer.shadowRadius = 10
        self.profileImageContainer.layer.shouldRasterize = true
    }
}
Pranay
  • 846
  • 6
  • 10
  • Thanks Pranay, but I got this on the private function. : https://i.imgur.com/MYsyG17.png – code-8 Feb 05 '19 at 17:54
  • @kyo That is because you need to replace my comments with whatever values are applicable. Like this line: `imageView.layer.borderColor = // your color` instead of `// your color` put in whatever color you'd want for the border color of that image view. Maybe that: `imageView.layer.borderColor = UIColor(hexString: "#8e8e8e").cgColor`. After you do that, remove the `as! CGColor as! Bool`. Then here: `imageView.image = //your profile image here` set the image if you have one, else remove this line entirely if you're setting it elsewhere. – Pranay Feb 05 '19 at 17:59
  • @kyo Did you look at my comment? Either set the image here if you want or remove this line: `imageView.image = //your profile image here` – Pranay Feb 05 '19 at 18:03