Reputation: 691
I need to add tap functionality to labels
(to open a website) in my app dynamically, I was thinking in create static function inside a class
. I want to launch the function in any ViewController
of my app.
I´ve done this using Swift 3:
class Launcher {
static func openWeb(label: UILabel, url: String) {
func tapFunction(sender:UITapGestureRecognizer) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(URL(string: url)!, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(URL(string: url)!)
}
}
let tap = UITapGestureRecognizer(target: self, action: #selector(tapFunction))
label.isUserInteractionEnabled = true
label.addGestureRecognizer(tap)
if #available(iOS 10.0, *) {
UIApplication.shared.open(URL(string: url)!, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(URL(string: url)!)
}
}
}
But doesn't work because I'm getting error in action: #selector(tapFunction)
error: Argument of '#selector' cannot refer to local function 'tapFunction(sender:)'
If I use this inside a ViewController
code using action: #selector(myView.tapFunction)
like the following, works
Inside viewDidLoad
let tap = UITapGestureRecognizer(target: self, action: #selector(MyViewController.tapFunction))
mylabel.isUserInteractionEnabled = true
mylabel.addGestureRecognizer(tap)
Separated function inside ViewController
func tapFunction(sender:UITapGestureRecognizer) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(URL(string: url)!, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(URL(string: url)!)
}
}
I want to convert this last code to static function inside my Class
to call it in any ViewController
. Thank you
Upvotes: 0
Views: 403
Reputation: 7746
First, there are some general problems with your approach. Selectors work by message passing. E.g. with UITapGestureRecognizer(target:, action:)
The message calling the method (the action
) is sent to the variable ( the target
).
When you create a local function, that function is a member of the enclosing function and not the containing class or instance, so your approach categorically cannot work.
Even if it could work, it would also go against OOP and MVC design principals. A Label
should not be in charge of what happens when it's tapped, just as the title of a book is not in charge of opening the book.
Taking all that into consideration, this is how I would solve your problem:
extension UIViewController {
func addTapGesture(to view: UIView, with selector: Selector) {
view.isUserInteractionEnabled = true
view.addGestureRecognizer(UITapGestureRecognizer(target: view, action: selector))
}
}
extension UIApplication {
func open(urlString: String) {
if #available(iOS 10.0, *) {
open(URL(string: urlString)!, options: [:], completionHandler: nil)
} else {
openURL(URL(string: urlString)!)
}
}
}
class YourVC: UIViewController {
var aLabel: UILabel? = nil
func addTapGesture() {
addTapGesture(to: aLabel!, with: #selector(tapGestureActivated))
}
func tapGestureActivated(gesture: UITapGestureRecognizer?) {
UIApplication.shared.open(urlString: "YourURLHere")
}
}
This abstracts away the boilerplate and is simple at the point of use, while still properly separating out Type responsibilities and using generalized functionality that can be re-used elsewhere.
Upvotes: 1
Reputation: 534958
A method that is supposed to communicate with an instance cannot be a static / class method. That's what static / class method means; it is about the class — there is no instance in the story. Thus what you are proposing is impossible, unless you hand the static method the instance it is supposed to talk to.
To me personally, a tappable URL-opening label sounds like a label subclass, and this functionality would then be an instance method of that label.
Upvotes: 2
Reputation: 1313
Try resolve it using extension
extension UILabel {
func openWeb(url: String) {
let tap = UITapGestureRecognizer(target: self, action: #selector(tapFunction))
self.isUserInteractionEnabled = true
self.addGestureRecognizer(tap)
openURL(url: url)
}
func tapFunction(sender:UITapGestureRecognizer) {
openURL(url: "")
}
func openURL(url: String) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(URL(string: url)!, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(URL(string: url)!)
}
}
}
let label = UILabel()
label.openWeb(url: "123")
To store link into label you can use associations with label object.
Upvotes: 2