Reputation: 20546
I have the following Swift code.
extension UIImageView {
func enableClickablePrint() {
let imageTap = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
self.addGestureRecognizer(imageTap)
self.isUserInteractionEnabled = true
}
func disableClickablePrint() {
// HERE
}
func toggleClickablePrint() {
// HERE
}
@objc fileprivate func imageTapped(_ sender: UITapGestureRecognizer) {
print("Image tapped")
}
}
The problem I'm running into is how to fill out the disableClickablePrint
and toggleClickablePrint
functions.
I'd like to be able to do something like the following.
extension UIImageView {
var imageTap: UITapGestureRecognizer?
func enableClickablePrint() {
imageTap = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
self.addGestureRecognizer(imageTap)
self.isUserInteractionEnabled = true
}
func disableClickablePrint() {
if let theImageTap = imageTap {
self.removeGestureRecognizer(theImageTap)
imageTap = nil
}
}
func toggleClickablePrint() {
if let theImageTap = imageTap {
disableClickablePrint()
} else {
enableClickablePrint()
}
}
@objc fileprivate func imageTapped(_ sender: UITapGestureRecognizer) {
print("Image tapped")
}
}
But of course the problem is you can't store properties in extensions like I'm wanting to do.
Anyway to achieve this? I want to try to keep this as clean as possible, without resorting to fancy tricks unless absolutely required.
Would the correct thing to do to be to convert this into a subclass of UIImageView? I'd like to try to avoid that if possible just because if I want to turn this into a framework or library or something, subclasses don't integrate as nicely into interface builder and the developer would have to add the extra step of changing the class of all their image views. Which I think would be awesome to avoid if possible.
Upvotes: 2
Views: 855
Reputation: 514
I've use this extension for years and it work not only image but with any view.
extension UIView {
func addTapGuesture(target: Any, action: Selector) {
let tap = UITapGestureRecognizer(target: target, action: action)
tap.numberOfTapsRequired = 1
addGestureRecognizer(tap)
isUserInteractionEnabled = true
}
}
Usage:
imageView.addTapGuesture(target: self, action: #selector(imageTapped))
@objc func imageTapped() {
print("Tapped")
}
Upvotes: 0
Reputation: 154691
Your problem is that you need to be able to recognize the UIGestureRecognizer
you added, but you can't store it in a property.
Here's a (tested) solution that subclasses UITapGestureRecognizer
to make the UIGestureRecognizer
identifiable and then searches self.gestureRecognizers
with first(where:)
to see if one has been added:
extension UIImageView {
class _CFTapGestureRecognizer : UITapGestureRecognizer { }
private var _imageTap: _CFTapGestureRecognizer? { return self.gestureRecognizers?.first(where: { $0 is _CFTapGestureRecognizer }) as? _CFTapGestureRecognizer }
func enableClickablePrint() {
// Only enable once
if _imageTap == nil {
let imageTap = _CFTapGestureRecognizer(target: self, action: #selector(imageTapped))
self.addGestureRecognizer(imageTap)
self.isUserInteractionEnabled = true
}
}
func disableClickablePrint() {
if let theImageTap = _imageTap {
self.removeGestureRecognizer(theImageTap)
}
}
func toggleClickablePrint() {
if _imageTap == nil {
enableClickablePrint()
} else {
disableClickablePrint()
}
}
@objc fileprivate func imageTapped(_ sender: UITapGestureRecognizer) {
print("Image tapped")
}
}
Upvotes: 3
Reputation: 1398
Since Extensions in swift can't contain stored properties, these are the solutions:
Fisrt one is the most used workaround is to use Objective_C runtime, by using objc_getAssociatedObject
and objc_setAssociatedObject
functions.
OK it's a nice solution, but if there is pure swift approach to do that so why not!
In your extension, define a struct with the field that you want to use, here you want for example UITapGestureRecognizer
Tip Create this struct as private You don't need anyone to access it of course.
Then define a computed property that will use this struct....
By doing this you have achieved what you need and you don't use the Objective-C at all .
Example :
extension UIImageView {
private struct TapGestureHelper{
static var tapGestureRecognizer : UITapGestureRecognizer?
}
var imageTap: UITapGestureRecognizer?{
get {
return TapGestureHelper.tapGestureRecognizer
}
set {
TapGestureHelper.tapGestureRecognizer = newValue
}
}
func enableClickablePrint() {
imageTap = UITapGestureRecognizer(target: self, action: #selector(imageTapped))
self.addGestureRecognizer(imageTap!)
self.isUserInteractionEnabled = true
}
func disableClickablePrint() {
guard let gesture = self.gestureRecognizers?.first else {
return
}
self.removeGestureRecognizer(gesture)
self.imageTap = nil
}
func toggleClickablePrint() {
if let theImageTap = imageTap {
disableClickablePrint()
} else {
enableClickablePrint()
}
}
@objc fileprivate func imageTapped(_ sender: UITapGestureRecognizer) {
print("Image tapped")
}
}
Upvotes: -1
Reputation: 7746
You could make an extension variable using a getter/setter:
extension UIImageView {
var tap: UITapGestureRecognizer {
get {
return //the created tap
}
set(value) {
print(value)
}
}
}
Upvotes: -1