Delegaci w pośpiechu?


132

Jak zabrać się za delegowanie, tj. NSUserNotificationCenterDelegateSzybko?


4
Czy masz na myśli wdrożenie delegata czy zdefiniowanie własnego pełnomocnika?
drewag

Odpowiedzi:


72

Nie różni się zbytnio od obj-c. Najpierw musisz określić protokół w swojej deklaracji klasy, na przykład:

class MyClass: NSUserNotificationCenterDelegate

Implementacja będzie wyglądać następująco:

// NSUserNotificationCenterDelegate implementation
func userNotificationCenter(center: NSUserNotificationCenter, didDeliverNotification notification: NSUserNotification) {
    //implementation
}

func userNotificationCenter(center: NSUserNotificationCenter, didActivateNotification notification: NSUserNotification) {
    //implementation
}

func userNotificationCenter(center: NSUserNotificationCenter, shouldPresentNotification notification: NSUserNotification) -> Bool {
    //implementation
    return true
}

Oczywiście musisz ustawić delegata. Na przykład:

NSUserNotificationCenter.defaultUserNotificationCenter().delegate = self;

1
Co się dzieje, gdy chcesz rozszerzyć UIViewController, na przykład w Objective-c, możesz mieć coś takiego @interface MyCustomClass: UIViewController <ClassIWantToUseDelegate>, co pozwala na zainicjowanie / skonfigurowanie kontrolera widoku, a także wywołanie metod delegata na podglądzie? Coś podobnego do tego ?
Mahmud Ahmad,

1
Cześć Adam, szybkie pytanie, jak mogę ustawić delegate = self, jeśli nie mogę utworzyć wystąpienia obiektu, ponieważ jest to klasa ogólna, do której nie mam dostępu w innej klasie, ale chcę, aby klasa ogólna wywoływała funkcję w druga klasa, stąd potrzeba delegata?
Marin

234

Oto mała pomoc dotycząca delegatów między dwoma kontrolerami widoku:

Krok 1: Stwórz protokół w UIViewController, który usuniesz / będziesz wysyłać dane.

protocol FooTwoViewControllerDelegate:class {
    func myVCDidFinish(_ controller: FooTwoViewController, text: String)
}

Krok 2: Zadeklaruj delegata w klasie wysyłającej (tj. UIViewcontroller)

class FooTwoViewController: UIViewController {
    weak var delegate: FooTwoViewControllerDelegate?
    [snip...]
}

Krok 3: Użyj delegata w metodzie klasowej, aby wysłać dane do metody odbierającej, czyli dowolnej metody, która przyjmuje protokół.

@IBAction func saveColor(_ sender: UIBarButtonItem) {
        delegate?.myVCDidFinish(self, text: colorLabel.text) //assuming the delegate is assigned otherwise error
}

Krok 4: Przyjęcie protokołu w klasie odbierającej

class ViewController: UIViewController, FooTwoViewControllerDelegate {

Krok 5: Zaimplementuj metodę delegata

func myVCDidFinish(_ controller: FooTwoViewController, text: String) {
    colorLabel.text = "The Color is " +  text
    controller.navigationController.popViewController(animated: true)
}

Krok 6: Ustaw delegata w pliku PrzygotowanieForSegue:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "mySegue" {
        let vc = segue.destination as! FooTwoViewController
        vc.colorString = colorLabel.text
        vc.delegate = self
    }
}

I to powinno działać. To oczywiście tylko fragmenty kodu, ale powinno dać ci pomysł. Aby uzyskać długie wyjaśnienie tego kodu, możesz przejść do mojego wpisu na blogu tutaj:

segues i delegaci

Jeśli jesteście ciekawi co się dzieje pod maską z delegatem to napisałem o tym tutaj:

pod maską z delegatami


23
Czy w kroku 2 nie powinno być słabego odniesienia do delegowania? jeśli mam rację, edytuj to. Przy okazji możesz ustawić to jako wartość opcjonalną. To byłoby szybsze. słaby delegat var: FooTwoViewControllerDelegate? PS: delegat powinien być słabym celem zachowania kręgu, dziecko nie powinno mieć silnego odniesienia do rodzica
Shial

1
Na mój sposób, gdy sprawisz, że delegat będzie opcjonalny, rozwiążesz problem podczas rozpakowywania. delegate? .myVCDidFinish. Jeśli delegat nie jest ustawiony, kod nie będzie się teraz wykonywał :) W twojej wersji będzie próbował wykonać i zakończy się niepowodzeniem, jeśli delegat ma wartość zero, a ty tak jest.
Shial

4
musisz zadeklarować taki protokół, aby umożliwić słabe odniesienie dla protokołu delegata FooTwoViewControllerDelegate: class {}
codingrhythm

Czy mógłbyś ustawić na każdym kroku, w którym VC jesteś jak VC1 i VC2. Nie jestem pewien, gdzie je umieścić.
Cing

2
@Shial - Właściwie wydaje się to trochę skomplikowane. weakjest potrzebny tylko w przypadku klas, a nie struktur i wyliczeń. Jeśli delegat ma być strukturą lub wyliczeniem, nie musisz się martwić o zachowanie cykli. Jednak delegat jest klasą a (jest to prawdą w wielu przypadkach, ponieważ dość często jest to ViewController), wtedy potrzebujesz, weakale musisz zadeklarować swój protokół jako klasę. Więcej informacji można znaleźć tutaj stackoverflow.com/a/34566876/296446
Robert

94

Delegaci zawsze wprawiali mnie w zakłopotanie, dopóki nie zdałem sobie sprawy, że delegat to tylko klasa, która wykonuje jakąś pracę dla innej klasy . To tak, jakby ktoś inny wykonywał za Ciebie całą brudną robotę, której nie chcesz wykonywać samodzielnie.

Napisałem krótką historię, aby to zilustrować. Przeczytaj to na placu zabaw, jeśli chcesz.

Pewnego razu...

// MARK: Background to the story

// A protocol is like a list of rules that need to be followed.
protocol OlderSiblingDelegate: class {
    // The following command (ie, method) must be obeyed by any 
    // underling (ie, delegate) of the older sibling.
    func getYourNiceOlderSiblingAGlassOfWater()
}

// MARK: Characters in the story

class BossyBigBrother {
    
    // I can make whichever little sibling is around at 
    // the time be my delegate (ie, slave)
    weak var delegate: OlderSiblingDelegate?
    
    func tellSomebodyToGetMeSomeWater() {
        // The delegate is optional because even though 
        // I'm thirsty, there might not be anyone nearby 
        // that I can boss around.
        delegate?.getYourNiceOlderSiblingAGlassOfWater()
    }
}

// Poor little sisters have to follow (or at least acknowledge) 
// their older sibling's rules (ie, protocol)
class PoorLittleSister: OlderSiblingDelegate {

    func getYourNiceOlderSiblingAGlassOfWater() {
        // Little sis follows the letter of the law (ie, protocol),
        // but no one said exactly how she had to respond.
        print("Go get it yourself!")
    }
}

// MARK: The Story

// Big bro is laying on the couch watching basketball on TV.
let bigBro = BossyBigBrother()

// He has a little sister named Sally.
let sally = PoorLittleSister()

// Sally walks into the room. How convenient! Now big bro 
// has someone there to boss around.
bigBro.delegate = sally

// So he tells her to get him some water.
bigBro.tellSomebodyToGetMeSomeWater()

// Unfortunately no one lived happily ever after...

// The end.

W przeglądzie istnieją trzy kluczowe części tworzenia i używania wzorca delegata.

  1. protokół , który określa, jakie pracownik musi zrobić
  2. klasa szef , który ma zmienną delegata, który używa powiedzieć klasę pracowników, co robić
  3. klasy pracownik , który przyjmuje protokół i robi to, co jest wymagane

Prawdziwe życie

W porównaniu z powyższą historią Bossy Big Brother, delegaci są często wykorzystywani do następujących praktycznych zastosowań:

  1. Komunikacja : jedna klasa musi wysłać informacje do innej klasy.
  2. Dostosowywanie : jedna klasa chce pozwolić innej klasie na jej dostosowanie.

Wspaniałą częścią jest to, że te klasy nie muszą wcześniej nic o sobie wiedzieć, z wyjątkiem tego, że klasa delegatów jest zgodna z wymaganym protokołem.

Gorąco polecam przeczytanie poniższych dwóch artykułów. Pomogli mi zrozumieć delegatów nawet lepiej niż dokumentacja .

Jeszcze jedna uwaga

Delegaci odwołujący się do innych klas, których nie są właścicielami, powinni używać weaksłowa kluczowego, aby uniknąć silnych cykli odwołań. Zobacz tę odpowiedź, aby uzyskać więcej informacji.


3
Wreszcie ktoś, kto potrafi wyjaśnić protokół i delegować ze zdrowym rozsądkiem! dzięki!
Engineeroholic

Co się dzieje, gdy Bossy Big Brother nie wie, że jest bratem (Generics)?
Marin

@ Marin, nie jestem pewien, czy rozumiem twoje pytanie. Lista reguł (protokołów) nie ma znaczenia, kto wzywa do przestrzegania zasad lub kto je przestrzega. To tylko zasady.
Suragch

Zasadniczo mam na myśli moje pytanie, tutaj nieco uproszczone. stackoverflow.com/questions/41195203/…
Marin,

47

Mam kilka poprawek do posta @MakeAppPie

Po pierwsze, podczas tworzenia protokołu delegata powinien on być zgodny z protokołem klasy. Jak w przykładzie poniżej.

protocol ProtocolDelegate: class {
    func myMethod(controller:ViewController, text:String)
}

Po drugie, twój delegat powinien być słaby, aby uniknąć zatrzymania cyklu.

class ViewController: UIViewController {
    weak var delegate: ProtocolDelegate?
}

Wreszcie, jesteś bezpieczny, ponieważ Twój protokół jest wartością opcjonalną. Oznacza to, że jego wiadomość „nil” nie zostanie wysłana do tej właściwości. Jest podobny do instrukcji warunkowej z respondToselectorw objC, ale tutaj masz wszystko w jednym wierszu:

if ([self.delegate respondsToSelector:@selector(myMethod:text:)]) {
    [self.delegate myMethod:self text:@"you Text"];
}

Powyżej znajduje się przykład obj-C, a poniżej przykład języka Swift, jak to wygląda.

delegate?.myMethod(self, text:"your Text")

jesteś bezpieczny, ponieważ twój protokół jest wartością opcjonalną ..... ponieważ używasz opcjonalnego łączenia łańcuchowego delegate?.myMethod, nie ulegnie awarii, ponieważ jeśli delegat jest, nilto nic się nie stanie. Jednak jeśli się pomylisz i napisał delegate!.myMethodpan mógł upaść, jeżeli pełnomocnik nie jest ustawiona, więc jej w zasadzie sposobem, aby być bezpieczne ...
Miód

32

Oto streszczenie, które zebrałem. Zastanawiałem się nad tym samym, co pomogło mi w lepszym zrozumieniu. Otwórz to w Xcode Playground, aby zobaczyć, co się dzieje.

protocol YelpRequestDelegate {
    func getYelpData() -> AnyObject
    func processYelpData(data: NSData) -> NSData
}

class YelpAPI {
    var delegate: YelpRequestDelegate?

    func getData() {
        println("data being retrieved...")
        let data: AnyObject? = delegate?.getYelpData()
    }

    func processYelpData(data: NSData) {
        println("data being processed...")
        let data = delegate?.processYelpData(data)
    }
}

class Controller: YelpRequestDelegate {
    init() {
        var yelpAPI = YelpAPI()
        yelpAPI.delegate = self
        yelpAPI.getData()
    }
    func getYelpData() -> AnyObject {
        println("getYelpData called")
        return NSData()
    }
    func processYelpData(data: NSData) -> NSData {
        println("processYelpData called")
        return NSData()
    }
}

var controller = Controller()

Kocham to. Bardzo pomocny
Aspen

@SeeMeCode Cześć, na początku był to dobry przykład, ale nadal mam problem. Jak mogę sprawić, aby moja dowolna UIViewControllerklasa była zgodna z delegatem, którego stworzyliśmy? Czy muszą być zadeklarowane w jednym szybkim pliku? Każda pomoc będzie wiele znaczyć.
Faruk

@Faruk Minęło trochę czasu, odkąd to opublikowałem, ale myślę, że to, o co prosisz, powinno być dość proste (jeśli nie zrozumiałem, przepraszam). Po prostu dodaj delegata do UIViewController po dwukropku. Więc coś w stylu class ViewController : UIViewController NameOfDelegate.
SeeMeCode

@SeeMeCode tak, dobrze zrozumiałeś moje pytanie. Przy okazji wypróbowałem twoją sugestię, ale kiedy tworzę klasę delegatów a.swiftzgodnie z twoją odpowiedzią powyżej, nie pojawia się b.swift. Nie mogę dotrzeć do żadnej klasy poza moim szybkim plikiem. jakieś trudności?
Faruk

jedno, czego nie rozumiem, to dlaczego powinienem utworzyć nową instancję YelpApi tylko po to, aby zadzwonić do przedstawiciela YelpApi? A co, jeśli uruchomiona instancja różni się od „nowej”, którą właśnie utworzyłem… skąd ma wiedzieć, który delegat należy do której instancji YelpApi?
Marin

15

DELEGACI W SWIFT 2

Wyjaśniam na przykładzie Delegata z dwoma ViewControllers.W tym przypadku obiekt SecondVC wysyła dane z powrotem do pierwszego kontrolera widoku.

Klasa z deklaracją protokołu

protocol  getDataDelegate  {
    func getDataFromAnotherVC(temp: String)
}


import UIKit
class SecondVC: UIViewController {

    var delegateCustom : getDataDelegate?
    override func viewDidLoad() {
        super.viewDidLoad()
     }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    @IBAction func backToMainVC(sender: AnyObject) {
      //calling method defined in first View Controller with Object  
      self.delegateCustom?.getDataFromAnotherVC("I am sending data from second controller to first view controller.Its my first delegate example. I am done with custom delegates.")
        self.navigationController?.popViewControllerAnimated(true)
    }

}

W First ViewController zgodność z protokołem odbywa się tutaj:

class ViewController: UIViewController, getDataDelegate

Definicja metody protokołu w kontrolerze First View (ViewController)

func getDataFromAnotherVC(temp : String)
{
  // dataString from SecondVC
   lblForData.text = dataString
}

Podczas wypychania SecondVC z kontrolera pierwszego widoku (ViewController)

let objectPush = SecondVC()
objectPush.delegateCustom = self
self.navigationController.pushViewController(objectPush, animated: true)

Twoje ostatnie 3 linijki pomogły mi zrozumieć mój scenariusz i rozwiązały mój problem. Dzięki! :)
iHarshil

6

Pierwsza klasa:

protocol NetworkServiceDelegate: class {

    func didCompleteRequest(result: String)
}


class NetworkService: NSObject {

    weak var delegate: NetworkServiceDelegate?

    func fetchDataFromURL(url : String) {
        delegate?.didCompleteRequest(url)
    }
}

Druga klasa:

class ViewController: UIViewController, NetworkServiceDelegate {

    let network = NetworkService()

    override func viewDidLoad() {
        super.viewDidLoad()
        network.delegate = self
        network.fetchDataFromURL("Success!")
    }



    func didCompleteRequest(result: String) {
        print(result)
    }


}

przy kompilacji powyższego kodu pokazuje błąd, który Type 'ViewController' does not conform to protocol 'NetworkServiceDelegate'sugeruje plz. To mój szósty dzień na szybkim :)
Vaibhav Saran

4

Bardzo łatwe krok po kroku (w 100% sprawne i przetestowane)

krok 1: Utwórz metodę na pierwszym kontrolerze widoku

 func updateProcessStatus(isCompleted : Bool){
    if isCompleted{
        self.labelStatus.text = "Process is completed"
    }else{
        self.labelStatus.text = "Process is in progress"
    }
}

Krok 2: ustaw delegata podczas wypychania do drugiego kontrolera widoku

@IBAction func buttonAction(_ sender: Any) {

    let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController") as! secondViewController
    secondViewController.delegate = self
    self.navigationController?.pushViewController(secondViewController, animated: true)
}

step3: ustaw delegata jak

class ViewController: UIViewController, ProcessStatusDelegate {

krok 4: Utwórz protokół

protocol ProcessStatusDelegate:NSObjectProtocol{
func updateProcessStatus(isCompleted : Bool)
}

krok 5: weź zmienną

var delegate:ProcessStatusDelegate?

Krok 6: Podczas gdy wróć do poprzedniego widoku metody delegata wywołania kontrolera, najpierw wyświetl powiadomienie kontrolera z danymi

@IBAction func buttonActionBack(_ sender: Any) {
    delegate?.updateProcessStatus(isCompleted: true)
    self.navigationController?.popViewController(animated: true)
}

@IBAction func buttonProgress(_ sender: Any) {
    delegate?.updateProcessStatus(isCompleted: false)
    self.navigationController?.popViewController(animated: true)

}

3

Prosty przykład:

protocol Work: class {
    func doSomething()
}

class Manager {
    weak var delegate: Work?
    func passAlong() {
        delegate?.doSomething()
    }
}

class Employee: Work {
    func doSomething() {
        print("Working on it")
    }
}

let manager = Manager()
let developer = Employee()
manager.delegate = developer
manager.passAlong() // PRINTS: Working on it

dlaczego używasz słowa kluczowego „class” w opisie protokołu? Jaka jest różnica w używaniu i nieużywaniu go?
Vlad

2
Słowo kluczowe class oznacza, że ​​jest to protokół wyłącznie klasy. Możesz ograniczyć adopcję protokołu do typów klas, a nie do struktur lub wyliczeń, dodając słowo kluczowe class. Prawdopodobnie nie powinienem był tego dodawać, aby uniknąć nieporozumień, ale skoro prosiłeś, zachowam.
Bobby

2

Delegaci to wzorzec projektowy, który umożliwia jednemu obiektowi wysyłanie komunikatów do innego obiektu, gdy ma miejsce określone zdarzenie. Wyobraź sobie, że obiekt A wywołuje obiekt B, aby wykonać akcję. Po zakończeniu działania obiekt A powinien wiedzieć, że B wykonał zadanie i podjąć niezbędne działania, można to osiągnąć z pomocą delegatów! Oto samouczek implementujący delegatów krok po kroku w Swift 3

Link do samouczka


0

Powyższe rozwiązania wydawały się nieco powiązane, a jednocześnie unikały ponownego użycia tego samego protokołu w innych kontrolerach, dlatego przyszedłem z rozwiązaniem, które jest silniej wpisywane przy użyciu ogólnego wymazywania typów.

@noreturn public func notImplemented(){
    fatalError("not implemented yet")
}


public protocol DataChangedProtocol: class{
    typealias DataType

    func onChange(t:DataType)
}

class AbstractDataChangedWrapper<DataType> : DataChangedProtocol{

    func onChange(t: DataType) {
        notImplemented()
    }
}


class AnyDataChangedWrapper<T: DataChangedProtocol> : AbstractDataChangedWrapper<T.DataType>{

    var base: T

    init(_ base: T ){
        self.base = base
    }

    override func onChange(t: T.DataType) {
        base.onChange(t)
    }
}


class AnyDataChangedProtocol<DataType> : DataChangedProtocol{

    var base: AbstractDataChangedWrapper<DataType>

    init<S: DataChangedProtocol where S.DataType == DataType>(_ s: S){
        self.base = AnyDataChangedWrapper(s)
    }

    func onChange(t: DataType) {
        base.onChange(t)
    }
}



class Source : DataChangedProtocol {
    func onChange(data: String) {
        print( "got new value \(data)" )
    }
}


class Target {
    var delegate: AnyDataChangedProtocol<String>?

    func reportChange(data:String ){
        delegate?.onChange(data)
    }
}


var source = Source()
var target = Target()

target.delegate = AnyDataChangedProtocol(source)
target.reportChange("newValue")    

wyjście : otrzymano nową wartość newValue


Chciałbym dowiedzieć się więcej na ten temat. Czy możesz wyjaśnić więcej na temat używanych terminów: sprzężony, „unikaj ponownego używania tego samego protokołu”, „ogólne usuwanie typów”. Dlaczego abstrakcja jest taka ważna? Czy zawsze należy to robić?
Suragch

0

W szybkim 4.0

Utwórz delegata w klasie, która musi wysyłać dane lub udostępniać funkcje innym klasom

Lubić

protocol GetGameStatus {
    var score: score { get }
    func getPlayerDetails()
}

Potem w klasie, która potwierdzi delegatowi

class SnakesAndLadders: GetGameStatus {
    func getPlayerDetails() {

 }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.