Reputation: 16735
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.
Update
Here's a Git repo of what I've tried. The subclass is called "FrostyButton".
Upvotes: 0
Views: 527
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
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