Is it possible to apply a .symbolEffect
(ie, the animation such as pulse) to an SF system image to a UIButton
in UIKit, ie not a SwiftUI app or insert ?
(Pls note that there are a 100 questions explaining how to animate images in general, I am asking how to apply symbolEffect specifically, is it possible in UIKit apps?? - ie not SwiftUI ?)
In UIKit, support for adding symbol effects only exists for UIImageView
and UIBarButtonItem
. It's really odd that it's not supported for UIButton
.
Here's an extension to UIButton
that provides the same API as UIImageView
:
extension UIButton {
private var imageView: UIImageView? {
for subview in subviews {
if let iv = subview as? UIImageView {
return iv
}
}
return nil
}
func addSymbolEffect(_ effect: some IndefiniteSymbolEffect & SymbolEffect, options: SymbolEffectOptions = .default, animated: Bool = true, completion: UISymbolEffectCompletion? = nil) {
imageView?.addSymbolEffect(effect, options: options, animated: animated, completion: completion)
}
func addSymbolEffect(_ effect: some DiscreteSymbolEffect & IndefiniteSymbolEffect & SymbolEffect, options: SymbolEffectOptions = .default, animated: Bool = true, completion: UISymbolEffectCompletion? = nil) {
imageView?.addSymbolEffect(effect, options: options, animated: animated, completion: completion)
}
func addSymbolEffect(_ effect: some DiscreteSymbolEffect & SymbolEffect, options: SymbolEffectOptions = .default, animated: Bool = true, completion: UISymbolEffectCompletion? = nil) {
imageView?.addSymbolEffect(effect, options: options, animated: animated, completion: completion)
}
func removeSymbolEffect(ofType effect: some IndefiniteSymbolEffect & SymbolEffect, options: SymbolEffectOptions = .default, animated: Bool = true, completion: UISymbolEffectCompletion? = nil) {
imageView?.removeSymbolEffect(ofType: effect, options: options, animated: animated, completion: completion)
}
func removeSymbolEffect(ofType effect: some DiscreteSymbolEffect & IndefiniteSymbolEffect & SymbolEffect, options: SymbolEffectOptions = .default, animated: Bool = true, completion: UISymbolEffectCompletion? = nil) {
imageView?.removeSymbolEffect(ofType: effect, options: options, animated: animated, completion: completion)
}
func removeSymbolEffect(ofType effect: some DiscreteSymbolEffect & SymbolEffect, options: SymbolEffectOptions = .default, animated: Bool = true, completion: UISymbolEffectCompletion? = nil) {
imageView?.removeSymbolEffect(ofType: effect, options: options, animated: animated, completion: completion)
}
func removeAllSymbolEffects(options: SymbolEffectOptions = .default, animated: Bool = true) {
imageView?.removeAllSymbolEffects(options: options, animated: animated)
}
}
Here's some test code you can put in a Playground (along with the above extension):
import UIKit
import PlaygroundSupport
let image = UIImage(systemName: "eye.fill")
var cfg = UIButton.Configuration.bordered()
cfg.image = image
let button = UIButton(configuration: cfg)
button.addSymbolEffect(.pulse)
PlaygroundPage.current.liveView = button
This will show a button with a pulsing eye symbol.