Delegados em rápido?

132

Como se faz para fazer um delegado, ou seja NSUserNotificationCenterDelegate, rapidamente?

user3718173
fonte
4
Você quer dizer implementar um delegado ou definir seu próprio delegado?
drewag

Respostas:

72

Não é tão diferente de obj-c. Primeiro, você deve especificar o protocolo na sua declaração de classe, como a seguir:

class MyClass: NSUserNotificationCenterDelegate

A implementação terá a seguinte aparência:

// 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
}

Claro, você deve definir o delegado. Por exemplo:

NSUserNotificationCenter.defaultUserNotificationCenter().delegate = self;
Adão
fonte
1
O que acontece quando você deseja estender o UIViewController, por exemplo, no objetivo-c, pode ter algo a ver com isso @interface MyCustomClass: UIViewController <ClassIWantToUseDelegate>, permitindo iniciar / configurar o viewcontroller, assim como chamar métodos delegados nas sub-visualizações? Algo semelhante a isso ?
Mahmud Ahmad
1
Oi Adam, pergunta rápida, como posso definir delegate = self, se não consigo instanciar um objeto porque é uma classe genérica à qual não tenho acesso na outra classe, mas quero que a classe genérica chame uma função no a outra classe, daí a necessidade de delegar?
Marin
234

Aqui está uma pequena ajuda para delegados entre dois controladores de exibição:

Etapa 1: faça um protocolo no UIViewController que você removerá / enviará os dados.

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

Etapa 2: declarar o delegado na classe de envio (ou seja, UIViewcontroller)

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

Etapa 3: use o delegado em um método de classe para enviar os dados para o método de recebimento, que é qualquer método que adote o protocolo.

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

Etapa 4: adote o protocolo na classe de recebimento

class ViewController: UIViewController, FooTwoViewControllerDelegate {

Etapa 5: implementar o método delegado

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

Etapa 6: defina o delegado no prepareForSegue:

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
    }
}

E isso deve funcionar. Obviamente, isso é apenas fragmentos de código, mas você deve ter uma ideia. Para uma longa explicação desse código, você pode ir até a entrada do meu blog aqui:

segues e delegados

Se você está interessado no que está acontecendo sob o capô com um delegado, escrevi sobre isso aqui:

sob o capô com delegados

MakeAppPie
fonte
23
Etapa 2 não deve haver uma referência fraca para delegar? se eu estiver certo, edite-o. Btw você pode torná-lo valor opcional. Isso seria mais rápido. delegado var fraco: FooTwoViewControllerDelegate? PS: delegado deve ser fraco cus de reter círculo, criança shouldynt manter forte referência para o pai
Shial
1
Do meu modo, quando você tornar o delegado opcional, você resolverá o erro de desembrulhar. delegate? .myVCDidFinish Porque se o delegado não estiver definido, o bacalhau não será executado agora :) Na sua versão, ele tentará executar e falhará ao desembrulhar se o delegado for nulo e você for.
Shial
4
você precisa declarar protocolo como este, a fim de fazer referência fraca possível para FooTwoViewControllerDelegate protocolo de delegado: class {}
codingrhythm
Você poderia definir por cada etapa em que VC você é como VC1 e VC2. Não tenho muita certeza de onde colocá-los.
Cing
2
@Shial - Na verdade, parece um pouco complicado. weaké necessário apenas para classes, não estruturas e enumerações. Se o delegado for uma estrutura ou enum, não será necessário se preocupar com ciclos de retenção. No entanto, o delegado é uma classe (isso ocorre em muitos casos, já que muitas vezes é um ViewController), então você precisa, weakmas precisa declarar seu protocolo como uma classe. Há mais informações aqui stackoverflow.com/a/34566876/296446
Robert
94

Os delegados sempre me confundiram até perceber que um delegado é apenas uma turma que trabalha para outra turma . É como ter alguém lá para fazer todo o trabalho sujo para você que você não quer fazer sozinho.

Eu escrevi uma pequena história para ilustrar isso. Leia-o no Playground, se quiser.

Era uma vez...

// 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.

Na revisão, há três partes principais para criar e usar o padrão de delegação.

  1. o protocolo que define o que o trabalhador precisa fazer
  2. a classe chefe que possui uma variável delegada, usada para informar à classe trabalhador o que fazer
  3. a classe de trabalhador que adota o protocolo e faz o que é necessário

Vida real

Em comparação com a história do Bossy Big Brother acima, os delegados são frequentemente usados ​​para as seguintes aplicações práticas:

  1. Comunicação : uma classe precisa enviar algumas informações para outra classe.
  2. Personalização : uma classe deseja permitir que outra classe a personalize.

A grande parte é que essas classes não precisam saber nada uma sobre a outra, exceto que a classe delegada está em conformidade com o protocolo necessário.

Eu recomendo a leitura dos dois artigos a seguir. Eles me ajudaram a entender os delegados ainda melhor do que a documentação .

Mais uma nota

Os delegados que fazem referência a outras classes que não possuem devem usar a weakpalavra-chave para evitar ciclos de referência fortes. Veja esta resposta para mais detalhes.

Suragch
fonte
3
Finalmente, alguém que possa explicar o protocolo e delegar com bom senso! valeu cara!
Engineeroholic
O que acontece quando o Bossy Big Brother não sabe que é irmão (Genéricos)?
Marin
@ Marin, não tenho muita certeza de entender sua pergunta. A lista de regras (protocolo) não se importa com quem está pedindo que as regras sejam seguidas ou quem está seguindo as regras. São apenas regras.
Suragch
Basicamente, estou me referindo à minha pergunta, um pouco simplificada por aqui. stackoverflow.com/questions/41195203/…
Marin
47

Recebi algumas correções para publicar no @MakeAppPie

Primeiro, quando você estiver criando um protocolo de delegação, ele deve estar em conformidade com o protocolo de classe. Como no exemplo abaixo.

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

Segundo, seu delegado deve ser fraco para evitar o ciclo de retenção.

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

Por último, você está seguro porque seu protocolo é um valor opcional. Isso significa que sua mensagem "nula" não será enviada para esta propriedade. É semelhante à declaração condicional com respondToselectorem objC, mas aqui você tem tudo em uma linha:

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

Acima, você tem um exemplo de obj-C e, abaixo, um exemplo rápido de como ele se parece.

delegate?.myMethod(self, text:"your Text")
Shial
fonte
você está seguro porque seu protocolo é um valor opcional ... porque você usa encadeamento opcional delegate?.myMethodnão trava porque se delegado for nil, nada aconteceria. No entanto, se você fez erro e escreveu delegate!.myMethodvocê pode falhar se um delegado não está definido, então é basicamente uma forma de você para ser seguro ...
Mel
33

Aqui está uma essência que eu montei . Eu estava pensando o mesmo e isso ajudou a melhorar minha compreensão. Abra isso no Xcode Playground para ver o que está acontecendo.

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()
SeeMeCode
fonte
Amo isso. Muito útil
Aspen
@SeeMeCode Oi, foi um bom exemplo em primeiro lugar, mas ainda tenho um problema. Como posso fazer com que minha UIViewControllerclasse conheça o delegado que fizemos? Eles precisam ser declarados em um arquivo rápido? Qualquer ajuda significará muito.
Faruk
@ Faruk Faz um tempo desde que publiquei isso, mas acho que o que você está pedindo deve ser bem simples (se estou entendendo mal, peço desculpas). Basta adicionar o delegado ao seu UIViewController após os dois pontos. Então, algo como class ViewController : UIViewController NameOfDelegate.
SeeCode
@SeeMeCode sim, você entendeu bem minha pergunta. Tentei sua sugestão, mas quando crio uma classe delegada de a.swiftacordo com a sua resposta acima, ela não aparece b.swift. Não consigo alcançar nenhuma classe fora do meu arquivo rápido. algum resistente?
Faruk
Uma coisa que eu não entendo é por que devo criar uma nova instância do YelpApi para ligar para o delegado do YelpApi? E se a instância em execução for diferente da 'nova' que acabei de criar ... como ela sabe qual delegado pertence a qual instância do YelpApi?
Marin
15

DELEGADOS NO SWIFT 2

Estou explicando com o exemplo de Delegado com dois viewControllers. Nesse caso, o SecondVC Object está enviando dados novamente para o primeiro View Controller.

Classe com declaração de protocolo

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)
    }

}

No primeiro protocolo ViewController, a conformidade é feita aqui:

class ViewController: UIViewController, getDataDelegate

Definição de método de protocolo no First View Controller (ViewController)

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

Durante o push, o SecondVC do First View Controller (ViewController)

let objectPush = SecondVC()
objectPush.delegateCustom = self
self.navigationController.pushViewController(objectPush, animated: true)
Maninderjit Singh
fonte
Suas últimas três linhas me ajudaram a entender meu cenário e resolver meu problema. Obrigado cara! :)
iHarshil
6

Primeira classe:

protocol NetworkServiceDelegate: class {

    func didCompleteRequest(result: String)
}


class NetworkService: NSObject {

    weak var delegate: NetworkServiceDelegate?

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

Segunda classe:

class ViewController: UIViewController, NetworkServiceDelegate {

    let network = NetworkService()

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



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


}
Ekambaram E
fonte
ao compilar o código acima, mostra o erro Type 'ViewController' does not conform to protocol 'NetworkServiceDelegate'plz sugerido. É minha 6º dia em rápida :)
Vaibhav Saran
4

Passo a passo muito fácil (100% funcionando e testado)

Etapa 1: Criar método no primeiro controlador de exibição

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

Etapa 2: definir delegado enquanto pressiona para o segundo controlador de exibição

@IBAction func buttonAction(_ sender: Any) {

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

step3: defina delegar como

classe ViewController: UIViewController, ProcessStatusDelegate {

etapa 4: criar protocolo

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

step5: pegue uma variável

var delegate:ProcessStatusDelegate?

passo 6: Volte ao método anterior de delegação de chamada do controlador de exibição, para que o primeiro visualize o controlador notificando com dados

@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)

}
Mr.Javed Multani
fonte
3

Exemplo simples:

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
Bobby
fonte
por que você usa a palavra-chave "class" na descrição do protocolo? qual é a diferença em usar e não usá-lo?
Vlad
2
A palavra-chave class significa que é um protocolo somente de classe. Você pode limitar a adoção do protocolo a tipos de classe, e não a estruturas ou enumerações, adicionando a palavra-chave da classe. Eu provavelmente não deveria ter adicionado para evitar confusão, mas desde que você pediu, eu continuarei.
Bobby
2

Delegados são um padrão de design que permite que um objeto envie mensagens para outro objeto quando um evento específico acontece. Imagine um objeto A chama um objeto B para executar uma ação. Depois que a ação é concluída, o objeto A deve saber que B concluiu a tarefa e tomar as medidas necessárias, isso pode ser alcançado com a ajuda dos delegados! Aqui está um tutorial implementando delegados passo a passo no rápido 3

Link do tutorial

James Rochabrun
fonte
0

As soluções acima pareciam um pouco acopladas e, ao mesmo tempo, evitavam reutilizar o mesmo protocolo em outros controladores, é por isso que eu vim com a solução mais forte digitada usando apagamento de tipo genérico.

@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")    

output : obteve novo valor newValue

Jans
fonte
Estou interessado em aprender mais sobre isso. Você pode explicar mais sobre os termos que usa: acoplado, "evite reutilizar o mesmo protocolo", "apagamento genérico de tipo". Por que abstraí-lo assim é importante? Deve-se sempre fazer isso?
Suragch 06/02
0

No veloz 4.0

Crie um representante na classe que precise enviar alguns dados ou fornecer alguma funcionalidade para outras classes

Gostar

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

Depois disso, na classe que vai confirmar para este delegado

class SnakesAndLadders: GetGameStatus {
    func getPlayerDetails() {

 }
}
Saranjith
fonte