Adrian
Adrian

Reputation: 16735

Inserting a UIVisualEffectView behind an transparent imageView Image on a UIButton

I'm attempting to create a button with a clear background and a UIVisualEffectView behind a transparent image on the button. The myButton.imageView.image has an image set (myPNGWithTransparentBackground).

I thought it would be pretty simple, but it's proven to be more difficult than I envisioned...here's what I've tried:

let frost = UIVisualEffectView(effect: UIBlurEffect(style: .light))
frost.frame = button.bounds
frost.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// button.insertSubview(frost, at: 0)
// button.insertSubview(frost, at: button.subviews.startIndex)
// button.insertSubview(frost, at: button.subviews.endIndex)
// button.imageView?.insertSubview(frost, at: (button.imageView?.subviews.endIndex)!)
button.imageView?.insertSubview(frost, at: (button.imageView?.subviews.startIndex)!)

Regardless of where I put insertSubview, it always ends up in front of the UIButton's imageView.image.

enter image description here

Update

Here's a Git repo of what I've tried. The subclass is called "FrostyButton".

Upvotes: 0

Views: 527

Answers (2)

Adrian
Adrian

Reputation: 16735

Not sure what the issue was, but I got my custom button class working...I was trying to match up buttons with some vanilla UIViews and tweaking the alpha on the UIView's background wasn't cutting it. Here's the class I came up with.

I ended up putting my UIVisualEffectView inside a UIView which I stuck inside the button. isEnabled toggles between visible and not visible. I use this in a class that has several buttons, only one of which runs at a time.

import UIKit

@IBDesignable class FrostyButton: UIButton {

    @IBInspectable override var isEnabled: Bool {
        didSet {
            if isEnabled {
                self.visualEffectContainer.alpha = 1.0
            }
            else {
                let animator = UIViewPropertyAnimator(duration: 0.5, curve: .linear, animations: {
                    self.visualEffectContainer.alpha = 0.0
                })
                animator.startAnimation()
            }
        }
    }

    @IBInspectable var cornerRadius: CGFloat {
        get {
            return layer.cornerRadius
        }

        set {
            layer.cornerRadius = newValue
            layer.masksToBounds = newValue > 0
        }
    }

    private let visualEffectContainer = UIView()

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.backgroundColor = .clear
        makeItFrosty()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
    }

    private func makeItFrosty() {

        let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
        effectView.frame = self.bounds
        effectView.clipsToBounds = true
        effectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

        visualEffectContainer.frame = self.bounds
        visualEffectContainer.clipsToBounds = true
        visualEffectContainer.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        visualEffectContainer.addSubview(effectView)
        visualEffectContainer.isUserInteractionEnabled = false
        if imageView != nil {
            self.insertSubview(visualEffectContainer, at: 0)
        }
    }
}

Upvotes: 1

user7014451
user7014451

Reputation:

After looking at the demo project, it looks like you need to add an imageView on top of the UIButton image. Here's my adjusted code - feel free to clean it up as needed!

private var imageView2 = UIImageView()  // declared globally

private func makeItFrosty() {
    let frost = UIVisualEffectView(effect: UIBlurEffect(style: .light))
    frost.frame = self.bounds
    frost.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    if let imageView = imageView {
        imageView.addSubview(frost)
        // these three lines basically 'clone' the existing imageView and adds it on top of the 'frost' view
        imageView2.image = imageView.image
        imageView2.frame = imageView.frame
        self.addSubview(imageView2)
    }
}

I'm not too surprised that you need to do something like this - the UIButton image is likely deeply embedded to "bring to front".

Upvotes: 1

Related Questions