Делегаты по-быстрому?


132

Как сделать делегата, то есть NSUserNotificationCenterDelegateбыстро?


4
Вы имеете в виду реализацию делегата или определение собственного делегата?
drewag

Ответы:


72

Это ничем не отличается от obj-c. Во-первых, вы должны указать протокол в объявлении вашего класса, как показано ниже:

class MyClass: NSUserNotificationCenterDelegate

Реализация будет выглядеть следующим образом:

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

Конечно, вы должны установить делегата. Например:

NSUserNotificationCenter.defaultUserNotificationCenter().delegate = self;

1
Что происходит, когда вы хотите расширить UIViewController, например, в объекте-c, вы можете что-то @interface MyCustomClass: UIViewController <ClassIWantToUseDelegate>обмануть, позволяя вам инициализировать / настраивать контроллер представления, а также вызывать методы делегата для вложенных представлений? Что-то похожее на это ?
Махмуд Ахмад

1
Привет, Адам, быстрый вопрос, как я могу установить delegate = self, если я не могу создать экземпляр объекта, потому что это общий класс, к которому у меня нет доступа в другом классе, но я хочу, чтобы класс универсальных шаблонов вызывал функцию в другой класс, следовательно, необходимость делегата?
Marin

234

Вот небольшая справка по делегатам между двумя контроллерами представления:

Шаг 1: Создайте протокол в UIViewController, который вы будете удалять / будут отправлять данные.

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

Шаг 2: объявите делегата в отправляющем классе (например, UIViewcontroller)

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

Шаг 3. Использование делегата в методе класса для отправки данных в метод приема, который является любым методом, который принимает протокол.

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

Шаг 4: Примите протокол в получающем классе

class ViewController: UIViewController, FooTwoViewControllerDelegate {

Шаг 5. Реализация метода делегата

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

Шаг 6: Установите делегата в 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
    }
}

И это должно сработать. Это, конечно, только фрагменты кода, но это должно дать вам представление. Для подробного объяснения этого кода вы можете перейти к моей записи в блоге здесь:

сегменты и делегаты

Если вам интересно, что происходит под капотом с делегатом, я написал об этом здесь:

под капотом с делегатами


23
Step2 не должно быть слабой ссылки на делегирование? если я прав, пожалуйста, отредактируйте. Кстати, вы можете сделать это необязательным значением. Так будет быстрее. слабый делегат var: FooTwoViewControllerDelegate? PS: делегат должен быть слабым из-за сохраняющего круга, ребенок не должен держать сильную ссылку на родителя
Shial

1
По-моему, когда вы сделаете делегат необязательным, вы разрешите ошибку распаковки. делегат? .myVCDidFinish Потому что, если делегат не установлен, код не будет выполняться сейчас :) В вашей версии он попытается выполнить и не сможет развернуть, если делегат равен нулю, а вы это делаете.
Шиал

4
вам нужно объявить протокол, подобный этому, чтобы сделать возможной слабую ссылку для протокола делегата FooTwoViewControllerDelegate: class {}
codingrhythm

Не могли бы вы установить для каждого шага, в каком VC вы похожи на VC1 и VC2. Я не совсем уверен, где их поставить.
Cing

2
@Shial - На самом деле это немного сложно. weakнужен только для классов, а не структур и перечислений. Если делегат будет структурой или перечислением, вам не нужно беспокоиться о циклах сохранения. Однако делегируйте свой класс a (это верно для многих случаев, поскольку довольно часто это ViewController), тогда вам нужно, weakно вам нужно объявить свой протокол как класс. Здесь есть дополнительная информация stackoverflow.com/a/34566876/296446
Роберт

94

Делегаты всегда сбивали меня с толку, пока я не понял, что делегат - это просто класс, который выполняет некоторую работу для другого класса . Это как если бы кто-то другой делал за вас всю грязную работу, которую вы не хотите делать сами.

Я написал небольшую историю, чтобы проиллюстрировать это. Прочитайте это на детской площадке, если хотите.

Давным-давно...

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

Итак, можно сказать, что создание и использование шаблона делегата состоит из трех ключевых частей.

  1. протокол , который определяет , что работник должен делать
  2. класс босса , который имеет переменную делегат, который он использует , чтобы сказать классу уборщицы , что делать
  3. класс работник , который принимает протокол и делает то , что требуется

Реальная жизнь

По сравнению с нашей историей о Босси-Большом Брате выше, делегаты часто используются для следующих практических целей:

  1. Связь : один класс должен отправить некоторую информацию другому классу.
  2. Настройка : один класс хочет позволить другому классу настроить его.

Самое замечательное в том, что этим классам не нужно ничего знать друг о друге заранее, за исключением того, что класс делегата соответствует требуемому протоколу.

Я настоятельно рекомендую прочитать следующие две статьи. Они помогли мне понять делегатов даже лучше, чем документация .

Еще одна заметка

Делегаты, которые ссылаются на другие классы, которые им не принадлежат, должны использовать weakключевое слово, чтобы избежать циклов сильных ссылок. Смотрите этот ответ для более подробной информации.


3
Наконец-то кто-то, кто может объяснить протокол и передать свои полномочия здравым смыслом! Спасибо чувак!
Engineeroholic

Что происходит, когда Босси Большой Брат не знает, что он брат (Дженерики)?
Marin

@ Марин, я не совсем уверен, что понимаю ваш вопрос. Список правил (протокол) не заботится о том, кто призывает к соблюдению правил или кто соблюдает правила. Это просто правила.
Suragch

В основном я имею в виду мой вопрос, слегка упрощенный здесь. stackoverflow.com/questions/41195203/…
Marin

47

Я получил несколько исправлений к посту @MakeAppPie

Прежде всего, когда вы создаете протокол делегата, он должен соответствовать протоколу Class. Как в примере ниже.

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

Во-вторых, ваш делегат должен быть слабым, чтобы избежать цикла.

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

Наконец, вы в безопасности, потому что ваш протокол является необязательным значением. Это означает, что его «ноль» сообщение не будет отправлено в это свойство. Это похоже на условный оператор respondToselectorв objC, но здесь все в одной строке:

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

Выше у вас есть пример obj-C, а ниже у вас есть пример Swift того, как это выглядит.

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

вы в безопасности, потому что ваш протокол является необязательным значением ..... потому что вы используете необязательную цепочку delegate?.myMethod, не произойдет сбой, потому что если делегат, nilто ничего не произойдет. Однако, если вы допустили ошибку и написали, delegate!.myMethodвы можете потерпеть крах, если делегат не установлен, так что это, по сути, способ обезопасить вас ...
Дорогая,

33

Вот суть, которую я собрал. Мне было интересно то же самое, и это помогло улучшить мое понимание. Откройте это на игровой площадке Xcode, чтобы увидеть, что происходит.

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 Привет, во-первых, это был хороший пример, но у меня все еще есть проблема. Как я могу сделать свой любой UIViewControllerкласс соответствующим делегату, который мы сделали? Они должны быть объявлены в одном быстром файле? Любая помощь будет много значить.
Фарук

@Faruk Прошло некоторое время с тех пор, как я опубликовал это, но я думаю, что то, что вы спрашиваете, должно быть довольно простым (если я неправильно понял, прошу прощения). Просто добавьте делегата в свой UIViewController после двоеточия. Так что-то вроде class ViewController : UIViewController NameOfDelegate.
SeeMeCode

@ SeMeCode да, вы правильно поняли мой вопрос. Я попробовал ваше предложение, кстати, но когда я создаю класс делегата в a.swiftсоответствии с вашим ответом выше, он не появляется b.swift. Я не могу добраться до любого класса за пределами моего файла swift. какие-нибудь проблемы?
Фарук

я не понимаю одной вещи: зачем мне создавать новый экземпляр YelpApi только для того, чтобы вызывать делегата YelpApi? Что, если запущенный экземпляр отличается от «нового», который я только что создал ... как он узнает, какой делегат принадлежит какому экземпляру YelpApi?
Марин

15

ДЕЛЕГАТЫ В SWIFT 2

Я объясняю на примере Delegate с двумя viewController'ами. В этом случае SecondVC Object отправляет данные обратно в первый View Controller.

Класс с протоколом декларации

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

}

В первом протоколе ViewController согласование выполняется здесь:

class ViewController: UIViewController, getDataDelegate

Определение метода протокола в First View Controller (ViewController)

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

Во время нажатия SecondVC из контроллера первого представления (ViewController)

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

Ваши последние 3 строки помогли мне понять мой сценарий и решили мою проблему. Спасибо чувак! :)
iHarshil

6

Первый класс:

protocol NetworkServiceDelegate: class {

    func didCompleteRequest(result: String)
}


class NetworkService: NSObject {

    weak var delegate: NetworkServiceDelegate?

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

Второй класс:

class ViewController: UIViewController, NetworkServiceDelegate {

    let network = NetworkService()

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



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


}

при компиляции кода выше отображается ошибка, Type 'ViewController' does not conform to protocol 'NetworkServiceDelegate'пожалуйста. Это мой 6-й день на swift :)
Вайбхав Саран

4

Очень просто пошагово (100% работает и проверено)

step1: Создать метод на первом контроллере представления

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

step2: установить делегата при нажатии на второй контроллер представления

@IBAction func buttonAction(_ sender: Any) {

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

step3: установите делегата как

class ViewController: UIViewController, ProcessStatusDelegate {

Шаг 4: Создать протокол

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

step5: взять переменную

var delegate:ProcessStatusDelegate?

step6: Вернувшись к предыдущему представлению, контроллер вызовет метод делегата, чтобы первый контроллер представления уведомил с данными

@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

Простой пример:

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

почему вы используете ключевое слово "класс" в описании протокола? какая разница в использовании и не использовании?
Влад

2
Ключевое слово class означает, что это протокол только для класса. Вы можете ограничить принятие протокола типами классов, а не структурами или перечислениями, добавив ключевое слово class. Вероятно, мне не следовало добавлять его, чтобы избежать путаницы, но, поскольку вы спросили, я сохраню.
Бобби

2

Делегаты - это шаблон проектирования, который позволяет одному объекту отправлять сообщения другому объекту, когда происходит определенное событие. Представьте, что объект A вызывает объект B для выполнения действия. Как только действие завершено, объект A должен знать, что B выполнил задачу, и предпринять необходимые действия, это может быть достигнуто с помощью делегатов! Вот руководство по реализации делегатов шаг за шагом в Swift 3

Ссылка на учебник


0

Вышеупомянутые решения казались немного связанными и в то же время избегали повторного использования одного и того же протокола в других контроллерах, поэтому я пришел с решением, которое более строго типизировано с использованием универсального стирания типов.

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

вывод : получил новое значение newValue


Мне интересно узнать об этом больше. Не могли бы вы подробнее объяснить используемые вами термины: связанный, «избегать повторного использования одного и того же протокола», «универсальное стирание типа». Почему так важно абстрагироваться? Нужно ли всегда делать это?
Suragch

0

В Swift 4.0

Создайте делегата в классе, который должен отправить некоторые данные или предоставить некоторые функции другим классам

подобно

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

После этого в классе, который собирается подтвердить этому делегату

class SnakesAndLadders: GetGameStatus {
    func getPlayerDetails() {

 }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.