Il pattern Delegate. Il concetto di Delegazione tra oggetti

ENTRA NEL NOSTRO GRUPPO FACEBOOK

×

Corso creare applicazioni per iOS con il linguaggio Swift

Ci sono due Re di due paesi differenti. Il primo regna a Topolandia ed il secondo comanda Plutolandia. Entrambi si odiano e non voglio parlarsi a vicenda. Il re di Topolandia, quando vuole comunicare con il re di Plutolandia, manda un messaggero a cavallo che, ricordati bene questo punto, non fa parte di nessuno dei due [...]

Torna a: Corso creare applicazioni per iOS con il linguaggio Swift > Applicazioni Multi Interfacce
  • Francesco

    Ciao Peppe,

    scusa io ho provato nel simulatore la chiusa, tutto ok, però se con il mouse tocco una parte dello schermo la stasera non si chiude.

    E’ un problema perché si utilizza il simulatore o bisogno implementare altro codice?

    Grazie

    • Ciao Francesco,

      Per poter chiudere la tastiera tramite la pressione sullo schermo devi implementare il metodo per la gestione dei tocchi sullo schermo dell’applicazione (pensavo di averlo scritto ma forse l’avrò saltato):

      // la funzione viene attivata ogni qual volta l’utente clicca sullo schermo dell’applicazione
      func touchesBegan(touches: Set, withEvent event: UIEvent?) {
      textField.resignFirstResponder()
      }

      grazie a te per avermelo fatto notare,
      alla prossima ;)

      • Francesco

        Ciao Peppe,

        ho provato a scrivere il codice da te indicato ma mi notifica errore

        • Ciao Francesco,

          Dove hai messo il codice?

          Devi metterlo nel ViewController perché è un metodo della classe UIViewController e non viene trasportato con i protocolli di delega.

          • Francesco

            Ciao Peppe,

            ho inserito nella classe ma mi notifica questi errori:

            Use of undeclared type “uitouch”
            use of unresolved identifier “TextField”

            • Avevo scritto male il metodo, è

              func touchesBegan(touches: Set, withEvent event: UIEvent?)

              Per il resto, dentro il metodo devi richiamare la tua TextField cioè le IBOutlet delle tue textField. Io ho scritto in modo generico solo per far capire il meccanismo.

              Fammi sapere ;)

            • Francesco

              Ciao Peppe,

              scusami se insisto ma voglio capire e riuscire a farlo

              mi da sempre errore: Use of undeclared type “uitouch”

            • Certo Francesco,
              questo cavolo di disqus mi passa a minuscolo quello che scrivo tra le parentesi angolari. Devi scrivere UITouch perché è una classe del framework UI

            • Francesco

              Ciao Peppe,

              ci siamo riusciti :-)
              però xcode mi ha chiesto di fare un overraide

              Peppe mi confermi che questo metodo non può essere allora implementato in una classe tipo come abbiamo fatto nell’esempio della chiusa della tastiera tramite singleton?

              un mio pensiero ma non potevo fare visto l’utilizzo costante della tastiera la chiusura in automatico senza dover scrivere tutti questi metodi?

              Grazie Peppe :-)

            • Si, l’ovveride è d’obbligo dato che è un metodo della super class.

              Mentre per metterlo in un’altra classe non è decisamente consigliato dato che sono metodi prettamente legati al ViewController. Quello che potresti fare è fare una estensione della classe e creare un metodo in cui passi i riferimenti alle textField che vuoi chiudere:

              exstension UIViewController {

              func closeTextFieldAtTouch(txtFields: [UITextField]) {

              for x in txtFields {

              x.resignFirstResponder()

              }

              }

              }

              Così nel metodo touchesBegan metteresti solo

              func touchesBegan(touches: Set, withEvent event: UIEvent?) {

              self.closeTextFieldAtTouch([primaTextField, secondaTextField])
              }

            • Per la seconda domanda non ho ben capito cosa intendevi.

            • Francesco

              Dicevo nell’esercizio precedente ci hai fatto creare una classe singleton a parte da poter richiamare e abbiamo inserito i metodi per la chiusura della tastiera in modo da non dover inserire l’intero codice in ogni singolo file, esso non può essere integrato anche con questo codice in modo da avere in un unico file entrambe le chiusure?

            • Purtroppo non esiste un protocollo di delega per la classe UIResponder (che è quella da cui prendi il metodo touchbegan (la UIView ed il ViewController a loro volta ereditano dalla UIResponder)).
              Se proprio vuoi usare il delegate devi comunque implementare il touchbegan in ogni ViewController ed in ogni metodo richiamare un altro metodo di chiusura presente nel Delegate. Altri sistemi oltre quello dell’exstension non me ne vengono.

              Se dovessi trovare qualcosa ti faccio sapere.

  • Alessandro

    Ciao Beppe (tra l’altro non capisco perché non compare il mio avatar qui su Disqus). Tornando a noi, io nella mia app ho implementato delle azioni all’interno dei metodi del UITextFieldDelegate. Ad esempio, se il campo test è vuoto, il pulsante SALVA nella barra di navigazione è disabilitato e si attiva solo quando c’è del testo inserito. Oppure richiamando il metodo textFieldShouldClear faccio in modo che il pulsante SALVA torni ad essere disabilitato perché ho cancellato il contenuto del campo di testo. Perciò richiamando altri oggetti all’interno di questi metodi, come faccio ad uniformizzare il tutto creando una classe singleton? dopo tutto lei non sarà a conoscenza di questi oggetti e non potrà interagire con loro..
    grazie!

    • Ciao Ale,

      Credo, confermami se ho capito bene, che tu voglia fare in modo che il Delegate sia a conoscenza di altri oggetti non strettamente legati alle textField (in questo caso abbiamo un Delegate per le textField).
      La classe Delegate non ha nessun vincolo preciso quindi può essere estesa per assolvere a più deleganti. Una volta che nel Delegante hai definito chi è il delegato puoi creare, in quest’ultima classe, delle nuove proprietà o metodi che puoi utilizzare per passare i valori della classe Delegante (ad esempio con metodi inout o proprietà let).

      • Alessandro

        Hai azzeccato il punto. Possiamo fare un esempio pratico? ad esempio, nella classe delegante ho self.pulsanteSalva.enabled = true e voglio fare in modo che quando ripulisco la text field con il metodo textFieldShouldClear, il pulsante di salvataggio cambi in false. Il pulsante di salvataggio non esiste nel controller singleton che andrò a creare…come glielo passo?

        • Capito perfettamente.

          Per questo specifico esempio la classe Delegata, cioè la TextFieldController, dovrebbe conoscere il bottone o i bottoni. Quindi uno dei modi, cioè quello più semplice, è di creare delle proprietà nella classe TextFieldController a cui passare i riferimenti ai vari bottoni dopo l’assegnazione del delegate:

          self.tuaText.delegate = il txtFieldontroller.singleton
          self.tuaText.delegate.proprietàBottone = self.tuoBottoneDaControllare

          Così nel textFieldShouldClear puoi richiamare il bottone direttamente dall’interno della classe.

          Questo è il metodo più semplice che mi viene in mente. Ce n’è altri ma sono più complessi e secondo me non utili a questa semplice operazione.

          • Alessandro

            Penso di aver capito, devo provare. Mentre invece il metodo suggerito da Alessandro come lo trovi? ovvero quello di mettere un guard che intercetta il metodo textFieldShouldClear() e se è true allora il mio bottone diventa enabled altrimenti rimane disabled. Ho fatto una roba del genere:

            guard textFieldShouldClear(self.miaTextField) == false else {
            self.saveButton.enabled = false
            return
            }

            ma ho provato e non va..ho sbagliato qualcosa? ovviamente il guard è nel viewDidLoad della classe delegante e il metodo è nella classe appunto.

            • Alessandro

              io dicevo cosi:
              guard self.tuaTextField.delegate!.textFieldShouldClear(self.tuaTextField) == false else {
              self.saveButton.enabled = false
              return
              }
              però ripeto non so se funziona XD

            • alearcy

              vero avevo dimenticato un pezzo, ad ogni modo non mi da errori ma non funziona..

            • Alessandro

              lo sospettavo…perchè quando premi il tasto della textField non viene nuovamente invocato il viewDidLoad

          • alearcy

            Niente non riesco…forse è un livello ancora troppo avanzato ma mi sta fumando la testa :-D

  • nickbonny

    Ciao Beppe, una domanda che credo sia semplice ma non sono riuscito a risolvere..ho un viewController che uso per fare il login, e all’interno ci sono 2 textField: Utente e Password. Io vorrei non dover creare un button con collegata una IBAction ma semplicemente usare il tasto invio sul tastierino per controllare se ho inserito i dati correttamente. Come posso fare?
    grazie

    • Ciao Nick,
      Puoi modificare il codice che ho scritto nella lezione aggiungendo un nuovo delegate alla classe TextFieldController. In questo caso il delegato sarà il ViewController, cioè le due classi si delegano a vicenda. Così, dai metodi della TextFieldController puoi vedere il contenuto delle IBOutlet delle due textField e, se sono piene, far partire una funzione dal ViewController (direttamente dal TextFieldController) dove invochi un segue per i llogin effettuato.

      Non so se mi sono spiegato!
      In caso apri un topic sul forum che lo vediamo nel dettaglio ;)

      • nickbonny

        Mmm non ho ben capito allora ho aperto un topico nel forum! Grazie comunque

  • fabiod

    Ciao Giuseppe, volevo chiederti quanto segue: se nel textFieldShouldReturn voglio fare dei controlli differenziati per le 2 o 3 text field che ho nel ViewController, come faccio ad identificarle? Il parametro textField della funzione textFieldShouldReturn contiene qualche proprietà da cui deduco che si sta riferendo alla mia text_field1 oppure alla text_field2? In modo da fare dei controlli differenziati per ogni textField (controllare ad esempio se nella text_field1 è stato inserito un numero o un indirizzo mail)? Io ho trovato un escamotage un po bizzarro; assegno in precedenza nel placeholder di ogni text field un testo differente e poi nel textFieldShouldReturn inserisco questo: if textField.placeholder! == “telefono” { ecc.}. Come soluzione un po più sensata ho trovato questa: if textField.isEqual(text_field2) { ecc.}. Funziona però soltanto nel ViewController. Nella classe delegata, la TextFieldController, non funziona poiché non riconosce il nome della text field. Esiste nel parametro textField della funzione textFieldShouldReturn un riferimento alla text field specifica che ha generato l’evento? Grazie.

    • Il sistema da te proposto è quello più semplice e immediato da utilizzare (non lo nego che lo utilizzo anche io :P ).

      C’è però un sistema che mette in campo le conoscenze acquisite nel primo corso. In realtà si potrebbe creare una subclass della UITextField in cui si aggiunge un nuovo attributo chiamato “identifier” (o quello che preferisci) e questa nuova subclass viene assegnata come classe che gestisce le varie textfield del tuo progetto. A questo punto, nella textFieldShouldReturn potresti utilizzare questo identifier per controllare quale textField stai checkando.

      Indubbiamente il primo sistema è quello più semplice e sbrigativo!

      Esiste anche un parametro, chiamato restorationIdentifier e accessibilityIdentifier che non ti consiglio di utilizzarli perché hanno altri scopi.

      Per qualsiasi altro dubbio, non esitare a scrivere!

      • fabiod

        Giuseppe ciao, ho provato a seguire il tuo consiglio ma mi sa che ho saltato qualche passaggio o comunque ho sbagliato qualcosa. Ti dico cosa ho fatto. In un file separato da quello del ViewController ho inserito la subclass della classe UITextField con la nuova proprietà che mi interessava:

        class NewTextField: UITextField {
        var identifier = String()
        }

        Poi nella classe ViewController ho cambiato il tipo delle @IBOutlet delle textfield, da UITextField a NewTwxtField:

        @IBOutlet var campo0Tf: NewTextField!
        @IBOutlet var campo1Tf: NewTextField!
        @IBOutlet var campo2Tf: NewTextField!

        Infine sempre nella classe ViewController:

        override func viewDidAppear(animated: Bool) {
        self.campo0Tf.identifier = “campo0Tf”
        self.campo1Tf.identifier = “campo1Tf”
        self.campo2Tf.identifier = “campo3Tf”
        }

        Poi dentro la textFieldShouldReturn, che è inserita nella classe che ha il protocollo di delega, cioè TextFieldController, ho messo il controllo sull’identifier (con un po di accorgimenti per castare da UITextField a NewTextField).
        Risultato, quando faccio partire il programma, quando esegue il viewDidAppear mi da errore su:

        self.campo0Tf.identifier = “campo0Tf”

        con l’avviso: Thread 1: EXC_BAD_ACCESS
        Che devo fare? Sembrerebbe che tutto il resto funzioni (tra l’altro se cancello le tre righe dentro il viewDidAppear, tutto funziona, a parte il controllo sull’identifier che non da risultato). Tu hai scritto di assegnare la subclass da me creata come classe che gestisce le varie textfied. Questa cosa non l’ho capita, non so come mettere in relazione questa subclass con la classe TextFieldController dove si trovano tutti i metodi relativi alle textfield.
        Detto questo, nonostante sia curioso di risolvere questo problema.. mi sa che finirò per seguire il consiglio mi hai dato anche in relazione ad altre domande, cioè di fare cose semplici almeno che non sia strettamente necessario.
        Aspetto una tua risposta, grazie mille ancora!

        • Ciao Fabio!

          Hai messo la classe anche nel Class Inspector per tutte e tre le textfield? Perché non basta cambiare il tipo di dato delle tre iboutlet, devi cambiarlo anche da storyboard.
          Credo sia questo il problema. Fammi sapere

          Giuseppe Sapienza
          xCoding.it | Sviluppa, Crea e codifica i tuoi sogni!

  • Marco Dognini

    ciao Peppe, ormai se non commento ogni lezione non sono contento ;)
    stavolta però è una domanda facile facile:
    una volta implementata la classe TextFieldController non è più necessario aggiungere il protocollo UITextFieldDelegate alla classe ViewController, corretto?
    Grazie

  • Marco Dognini

    Se può essere utile di seguito riporto come ho implementato un sistema che gestisce il passaggio tra TextField quando l’utente preme Invio e la scomparsa della tastiera se viene premuto fuori dalla TextField:
    si tratta di 2 file, uno è “TextFieldControler.swift”, l’altro è il “ViewController.swift”

    import Foundation

    import UIKit

    //classe per la gestione di tutti gli eventi delle TextField, conforme al protocollo UITextFieldDelegate

    //i metodi di UITextFieldDelegate sono tutti opzionali, quindi posso decidere quali implementare

    class TextFieldController: NSObject, UITextFieldDelegate {

    //implemento il singleton

    static let singleton = TextFieldController()

    private override init() {

    super.init()

    }

    func textFieldShouldReturn(textField: UITextField) -> Bool {

    //cerco la prossima textfield a cui passare (quella con il tag+1)

    //assegno a nextField la textfield con il tag successivo

    if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {

    //se esiste allora diventa la FirstResponder, cioè si attiva (e compare la tastiera)

    nextField.becomeFirstResponder()

    } else {

    //se non la trova resignFirstResponder() riporta la textfield al suo stato naturale (tastiera chiusa)

    textField.resignFirstResponder()

    }

    return true

    }

    }

    //estensione della classe UIViewController

    extension UIViewController {

    //aggiungo la funzione per far scomparire la tastiera

    func closeTextFieldAtTouch(txtFields: [UITextField]) {

    for x in txtFields {

    x.resignFirstResponder()

    }

    }

    }

    import UIKit

    class ViewController: UIViewController {

    //creo 2 o più IBOutlet

    @IBOutlet var tf_1: UITextField!

    @IBOutlet var tf_2: UITextField!

    override func viewDidLoad() {

    super.viewDidLoad()

    // Do any additional setup after loading the view, typically from a nib.

    //assegno come delegate delle textfield l’istanza singleton della classe TextFieldController

    self.tf_1.delegate = TextFieldController.singleton

    self.tf_2.delegate = TextFieldController.singleton

    //assegno dei tag progressivi a seconda dell’ordine delle textfield che voglio passare

    self.tf_1.tag = 0

    self.tf_2.tag = 1

    }

    override func didReceiveMemoryWarning() {

    super.didReceiveMemoryWarning()

    // Dispose of any resources that can be recreated.

    }

    //———-DA RIPORTARE IN OGNI VC——————–

    //faccio l’override della func touchesBegan

    //(è la funzione viene attivata ogni qual volta l’utente clicca sullo schermo dell’applicazione)

    override func touchesBegan(touches: Set, withEvent event: UIEvent?) {

    //richiamo la funzione creata nell’extension inserendo come parametro un array di textfield

    //per le quali voglio che sparisca la tastiera quando viene premuto al di fuori di esse

    self.closeTextFieldAtTouch([tf_1,tf_2])

    }

    //—————————————————–

    }

Start typing and press Enter to search

Le Stack View in Xcode