Label et Double Tap

#1

Bonjour à tous,

Je suis nouveau dans le dev iOS et je me suis lancé dans la programmation d’un app pour le taff.

J’ai un petit souci pour appeler la numéro présent dans un label.
Avec un seul cela fonctionne correctement mais impossible si j’en mets plusieurs.

Voici le code:

@IBOutlet weak var lbl2: UILabel!
@IBOutlet weak var lbl3: UILabel!
@IBOutlet weak var lbl4: UILabel!

@IBOutlet weak var pvemergency: UIPickerView!

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    pvemergency.dataSource = self
    pvemergency.delegate = self
    
    lbl2.isUserInteractionEnabled = true
    lbl3.isUserInteractionEnabled = true
    lbl4.isUserInteractionEnabled = true
    
    let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.longPress))
    lbl2.addGestureRecognizer(longPressGesture)
    //lbl3.addGestureRecognizer(longPressGesture)
    //lbl4.addGestureRecognizer(longPressGesture)
    
    longPressGesture.minimumPressDuration = 2.0
    
    let doubleTapGesture =  UITapGestureRecognizer(target: self, action: #selector(self.doubleTap))
    lbl2.addGestureRecognizer(doubleTapGesture)
    //lbl3.addGestureRecognizer(doubleTapGesture)
    //lbl4.addGestureRecognizer(doubleTapGesture)
    
    doubleTapGesture.numberOfTapsRequired = 2
    
}

@objc func longPress(){
    //let phone = String(lbl2.text!)
    //callNumber(phonenumber: phone)
    UIPasteboard.general.string = lbl2.text
}

@objc func doubleTap (){
    //lbl2.text = "double tap"
    let phone = String(lbl2.text!)
    callNumber(phonenumber: phone)
    //UIPasteboard.general.string = lbl2.text
}

func callNumber (phonenumber:String)
{
    if let phoneCallURL = URL(string: "tel://\(phonenumber)"){
        
        let application:UIApplication = UIApplication.shared
        
        if (application.canOpenURL(phoneCallURL)){
            application.open(phoneCallURL,options: [:], completionHandler: nil)
        }
    }

Si je décommente //lbl3.addGestureRecognizer(doubleTapGesture)
il ne se passe plus rien comme sil y avait un conflis.

De plus, comment puis-je passer le nom du label que je tapotte dans let phone = String(lbl2.text!)

au lieu d’avoir le nom en dur.

Merci de votre aide,

Yvan

#2

Salut Yvan,

Tu as plusieurs labels avec des numéros de téléphone sur un ViewController et tu aimerais que ça déclenches un appel quand tu cliques sur l’un ou l’autre des labels avec le numéro qui est écrit sur le label c’est ça ?

#3

Hello,

Oui exactement. J’ai un picker view qui selon mon choix affiche les bons numéros dans les labels (tout fonctionne pour cette partie).

Quand j’avais un seul label cela fonctionnait mais je n’arrive pas à faire fonctionner avec plusieurs et surtout dire que c’est tel ou tel label que je presse (double tap)

Merci

#4

Alors, dans la première application que j’ai développé (que je n’ai pas encore sortie car c’est lié à un objet connecté et bordel, que ça met du temps à développer le hardware !!), j’avais fait comme ça.

if let url = URL(string: "tel://\(+33600000000 as UInt64)"), UIApplication.shared.canOpenURL(url) {
            if #available(iOS 10, *) {
                UIApplication.shared.open(url)
            } else {
                UIApplication.shared.openURL(url)
            }
        }

Mais comme tu t’imagines, je n’ai pas encore eu le temps de pousser les tests à fond pour voir si ça fonctionnait parfaitement.
L’avantage, dans ton cas, c’est que tu peux facilement modifier le numéro de téléphone avec une variable.
Essaye de ton côté et dis moi et tiens moi au courant :slight_smile:

#5

Merci de ta réponse.

Ma fonction callNumber() fonctionne.
Le problème cest que dans ma fonction doubletap(), je lui passe la valeur du label2 en dur et donc cela ne détecte pas sur quel label je doubletap.

Si je test avec un seul label, l’appuie long et le double tap fonctionne correctement.
Mais avec plusieurs j’ai plus rien si je dé-commente cette partie lbl3.addGestureRecognizer(longPressGesture).

Edit: en remplacant lbl3.addGestureRecognizer(longPressGesture) par view.addGestureRecognizer(longPressGesture) ca semble bien detecter le bon label

Merci

#6

Le problème des fonctions en objC avec un #selector c’est que tu ne peux pas leur passer d’arguments.
Dans ces cas là, c’est très compliqué de faire quelque chose de dynamique comme dans ton cas.

Par exemple, tu ne peux pas faire :
let doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(self.doubleTap(lbl2.text)))
Il va te mettre sur une erreur sur (lbl2.text)

#7

ok merci.
bon du coup la partie doubletap fonctionne mais je vais être coincé avec ma fonction en objC.
A moins que je change ma func callNumber qui est appelé dans la fonc objC

#8

Oui, sur le GestureRecognizer, ça doit pouvoir se gérer.
Mais côté #selector, ça va coincer je pense !

#9

donc la en faisant :
view.addGestureRecognizer(doubleTapGesture)

Je récupère bien le nom du label ou je press

Par contre dans ma fonction :

@objc func doubleTap (){

    let phone = String(lbl2.text!)
    callNumber(phonenumber: phone)
    
}

je dois pouvoir remplacer lbl2.text par une variable qui sait ou je press comme le view utilisé avant.

Merci

#10

Oui et c’est là où ça se complique puisque tu ne peux pas passer d’argument dans ta fonction en objC.
Avec View, est-ce que tu arrives à récupérer quelque chose ?

#11

pas dans la fonction.

du coup je suis coincé

#12

Je n’ai pas touché à Xcode depuis l’an dernier, je suis un peu rouillé avec ces histoires de gesture. J’entrevois cependant une solution possible à ton problème, en passant par le délégué de la GestureRecognizer, plutôt qu’une target objc-C.

Je ne sais pas si ça fonctionne, je n’ai pas essayé (Kotlin a tellement pompé de place sur mon SSD que je ne peux plus installer Xcode - j’achète bientôt un Mac Mini avec un gros SSD pour le dédier au développement iOS).

Dans un autre ordre, ton code est dangereux.

let phone = String(lbl2.text!) 
callNumber(phonenumber: phone)

Si le label lbl2 est vide, sans le moindre texte dedans, ton code provoque un plantage immédiat de l’application, à cause de l’utilisation non sécurisé de l’opérateur “!”.

Tu devrais réviser les notions de variables optionals avec les “?” et “!”. C’est vital pour créer du code sécurisé, ne partant pas dans le décor au premier problème.

EDIT : Et utilise des noms de variables explicites. lbl2 ne veux pas dire grand chose. Grâce à la complétion automatique, on peut employer des variables avec des noms bien significatifs sans s’embêter à retaper l’intégralité du nom à chaque fois.

#13

Hello Draken,

Merci pour l’info, je vais voir pour utiliser GestureRecognizer

#14

Je viens de commander sur Amazon un SSD Thunderbolt 500 Go pour augmenter la capacité disque de mon p’ov MBRr 2015. Je vais réinstaller Xcode sur ma machine, ce WE, pour me remettre dans le bain.

Si j’ai le temps, je me pencherais sur ton problème de gesture et d’identification de labels. Cela me fera du bien de replonger dans du Swift.

#15

Merci.

De mon cote, je vais essayer de trouver une alternative

#16

Essaye de regarder dans ce sens, ça n’offre pas vraiment toutes les options de gestures que tu voulais mais il y a peut-être moyen de s’en sortir autrement.

#17

Problème résolu !

Je n’avais jamais essayé d’utiliser une gesture avec plusieurs vues. Après un premier essai j’ai eu un message d’avertissement me prévenant que iOS refuse de faire ça. Une gesture ne peut être associée qu’à une seule et unique vue.

Je suis alors parti sur l’idée de créer une gesture par vue. Ce n’est pas bien compliqué avec Storyboard. Et cela permet d’utiliser une @IBAction avec plusieurs détecteurs de gestures.

Mon application de test est simple :

  • 3 labels (+ 1 pour afficher le résultat du test)
  • 3 gestures (créés avec Storyboard, sans ligne de code)
  • une seule fonction (@IBAction) pour traiter les gestures

Le code est court :

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var label01: UILabel!
    @IBOutlet weak var label02: UILabel!
    @IBOutlet weak var label03: UILabel!
    
    @IBOutlet weak var contenuLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    
    @IBAction func traiterGesture(_ sender: UITapGestureRecognizer) {
        
        // On récupére la vue (UIView) concernée par la gesture
        let vueGesture = sender.view
        // Et on la caste en UILabel pour lire son contenu
        if let label = vueGesture as? UILabel {
            // Lecture contenu Label
            // Il peut être nul, d'où l'utilisation d'un if let
            if let contenu = label.text {
                // Affichage contenu
                contenuLabel.text = "Contenu : " + contenu
            }
        }
    }
    
}

Chaque fois qu’un détecteur de gesture détecte un double tap, il appelle la fonction traiterGesture() en lui transmettant une référence vers lui-même : le sender.

Pour connaître la vue concernée, il faut consulter la propriété view de la gesture. C’est une information générique de type UIView (pour fonctionner avec n’importe quelle type de vue). Dans le cas présent c’est un UILabel, alors on passe par un casting (as?) pour le convertir en Label.

Je ne sais pas si tu comprends tout, parce que j’ai beaucoup utilisé Storyboard pour créer les objets et les configurer. N’hésite pas à demander (ou revoir les cours de Maxime) si quelque chose t’échappe (la création d’une @IBAction par exemple).

#18

Hello,

Merci de ton aide, cela fonctionne, je peux appeler ma fonction callNumber en récuperant la valeur du label.

J’ai vu comment connecter la fonction au viewcontroler depuis Main Storyboard

je vais essayer pour le longpress maintenant

Merci

#19

A ton service ! Toutes les gestures fonctionnent sur le même principe. Tu ne devrais pas avoir de problème. Attention quand même à ne pas mettre trop de gestures sur le même composant graphique.