Делегати швидко?


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, наприклад, у aim-c, ви можете щось це @interface MyCustomClass: UIViewController <ClassIWantToUseDelegate>полегшити, що дозволяє вам ініціювати / налаштувати viewcontroller, а також викликати методи делегування на підпоглядах? Щось подібне до цього ?
Махмуд Ахмад

1
Привіт, Адаме, швидке запитання, як я можу встановити delegate = self, якщо я не можу створити об'єкт, оскільки це загальний клас, до якого я не маю доступу в іншому класі, але я хочу, щоб клас generics викликав функцію в інший клас, звідси потреба в делегаті?
Марін

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: Встановіть делегата в PripravForSegue:

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, чи не має бути слабкого посилання на делегата? якщо я маю рацію, будь ласка, відредагуйте його. До речі, ви можете зробити це необов'язковим значенням. Це було б швидше. слабкий вар-делегат: FooTwoViewControllerDelegate? PS: делегат повинен бути слабким причиною збереження кола, дитина не повинна зберігати чітке посилання на батьків
Шиаль

1
По-моєму, коли ви зробите делегат необов’язковим, ви усунете помилку, що розгортається делегат? .myVCDidFinish Becouse, якщо делегат не встановлений, трек виконувати зараз :) У вашій версії він спробує виконати і не вдасться розкрутити, якщо делегат нульовий, а ви це є.
Шиал

4
вам потрібно оголосити такий протокол, щоб зробити слабким посилання можливим делегатний протокол FooTwoViewControllerDelegate: class {}
кодування-ритм

Чи можете ви встановити будь-який крок, в якому VC ви схожі на VC1 та VC2. Я не дуже впевнений, куди їх поставити.
Cing

2
@Shial - насправді це здається трохи складним. weakпотрібен лише для занять, а не структури та перерахунки. Якщо делегат буде структурою або перерахунком, то вам не потрібно турбуватися про збереження циклів. Однак делегат свого класу (це справедливо для багатьох випадків, оскільки досить часто це 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. клас працівник , який приймає протокол і робить те , що потрібно

Справжнє життя

Порівняно з нашою вище розповіддю про Bossy Big Brother, делегати часто використовуються для таких практичних застосувань:

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

Велика частина полягає в тому, що цим класам не потрібно заздалегідь нічого знати один про одного, за винятком того, що клас делегата відповідає необхідному протоколу.

Я настійно рекомендую прочитати наступні дві статті. Вони допомогли мені зрозуміти делегатів навіть краще, ніж це робила документація .

Ще одна примітка

Делегати, які посилаються на інші класи, якими вони не володіють, повинні використовувати weakключове слово, щоб уникнути сильних еталонних циклів. Дивіться цю відповідь для отримання більш детальної інформації.


3
Нарешті хтось, хто може пояснити протокол і делегувати здоровим глуздом! дякую людино!
Engineeroholic

Що станеться, коли Боссі-великий брат не знає, що він брат (Generics)?
Марін

@Marin, я не дуже впевнений, що розумію твоє запитання. Список правил (протокол) не має значення, хто це закликає дотримуватися правил або хто дотримується правил. Вони просто правила.
Сурагч

В основному я маю на увазі своє питання, трохи спрощене тут. stackoverflow.com/questions/41195203/…
Марін

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ви можете зірватися, якщо делегат не встановлений, то це в основному спосіб для вас бути в безпеці ...
Honey

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

@SeeMeCode так, ви добре зрозуміли моє запитання. Я спробував вашу пропозицію btw, але коли я створюю клас делегата a.swiftвідповідно до вашої відповіді вище, він не з'являється b.swift. Я не можу досягти будь-якого класу поза моїм швидким файлом. якісь тверді?
Фарук

я не розумію, чому я повинен створити новий екземпляр YelpApi, щоб я викликав делегата YelpApi? Що робити, якщо запущений екземпляр відрізняється від 'нового', який я щойно створив ... як дізнатися, який делегат належить до того, який екземпляр YelpApi?
Марин

15

ДЕЛЕГАТИ В SWIFT 2

Я пояснюю на прикладі Delegate з двома viewControllers. У цьому випадку SecondVC Object повертає дані назад на перший контролер перегляду.

Клас з декларацією протоколу

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

Визначення методу протоколу в контролері першого перегляду (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)

Ваші останні три рядки допомогли мені зрозуміти мій сценарій і вирішили мою проблему. Спасибі людина! :)
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'запропонуйте. Це мій шостий день у швидкому :)
Вайбхав Саран

4

Дуже легкий крок за кроком (100% робочий і перевірений)

step1: Створіть метод на контролері першого виду

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

крок 2: Встановіть делегат під час натискання на контролер другого виду

@IBAction func buttonAction(_ sender: Any) {

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

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

клас ViewController: UIViewController, ProcessStatusDelegate {

крок 4: Створення протоколу

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

step5: візьміть змінну

var delegate:ProcessStatusDelegate?

крок 6: Поки повертаємось до попереднього методу делегування виклику контролера перегляду, тому спочатку контролер огляду сповіщає дані

@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
Ключове слово клас означає, що це протокол лише для класу. Ви можете обмежити прийняття протоколу типом класу, а не структурами чи перерахуваннями, додавши ключове слово класу. Я, мабуть, не повинен був додавати його, щоб уникнути плутанини, але оскільки ви просили, я буду тримати.
Боббі

2

Делегати - це схема дизайну, яка дозволяє одному об’єкту надсилати повідомлення іншому об'єкту, коли відбувається певна подія. Уявіть, що об'єкт A викликає об'єкт B для виконання дії. Після завершення дії об’єкт A повинен знати, що B виконав завдання і вжив необхідних дій, цього можна досягти за допомогою делегатів! Ось підручник із впровадженням делегатів крок за кроком у швидкому 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


Мені цікаво дізнатися більше про це. Чи можете ви пояснити детальніше про використовувані терміни: у поєднанні, "уникайте повторного використання одного і того ж протоколу", "загального стирання типу". Чому таке абстрагування важливо? Чи слід завжди це робити?
Сурагч

0

У швидкому 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.