Делегаты по-быстрому?
Как можно сделать делегата, т.е. NSUserNotificationCenterDelegate
в быстром?
15 ответов
Это ничем не отличается от 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, который вы будете удалять / будут отправлять данные.
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
}
}
И это должно работать. Это, конечно, только фрагменты кода, но это должно дать вам представление. Для подробного объяснения этого кода вы можете перейти к моей записи в блоге здесь:
Если вы заинтересованы в том, что происходит под капотом с делегатом, я напишу об этом здесь:
Делегаты всегда смущали меня, пока я не понял, что делегат - это просто класс, который выполняет некоторую работу для другого класса. Это как если бы кто-то другой делал всю грязную работу за вас, которую вы не хотите делать сами.
Я написал небольшую историю, чтобы проиллюстрировать это. Прочитайте это на детской площадке, если хотите.
Давным-давно...
// 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.
В обзоре есть три ключевых части создания и использования шаблона делегата.
- протокол, который определяет, что должен делать работник
- класс босса, который имеет переменную делегата, которую он использует, чтобы указать рабочему классу, что делать
- рабочий класс, который принимает протокол и делает то, что требуется
Реальная жизнь
По сравнению с нашей историей о Босси-старшем брате делегатов часто используют для следующих практических применений:
- Связь: один класс должен отправить некоторую информацию другому классу.
- Настройка: один класс хочет позволить другому классу настроить его.
Самое замечательное в том, что этим классам не нужно ничего знать друг о друге заранее, за исключением того, что класс делегата соответствует требуемому протоколу.
Я настоятельно рекомендую прочитать следующие две статьи. Они помогли мне понять делегатов даже лучше, чем документация.
- Что такое делегирование? - Руководство разработчика Swift
- Как работает делегирование - Руководство разработчика Swift
Еще одна заметка
Делегаты, которые ссылаются на другие классы, которые им не принадлежат, должны использовать weak
ключевое слово, чтобы избежать сильных ссылочных циклов. Смотрите этот ответ для более подробной информации.
Я получил несколько исправлений к посту @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")
Вот суть, которую я собрал. Мне было интересно то же самое, и это помогло улучшить мое понимание. Откройте это на игровой площадке 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()
ДЕЛЕГАТЫ В SWIFT 2
Я объясняю на примере Delegate с двумя viewControllers. В этом случае 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)
}
}
В First ViewController Protocol согласование осуществляется здесь:
class ViewController: UIViewController, getDataDelegate
Определение метода протокола в First View Controller(ViewController)
func getDataFromAnotherVC(temp : String)
{
// dataString from SecondVC
lblForData.text = dataString
}
Во время нажатия SecondVC из First View Controller(ViewController)
let objectPush = SecondVC()
objectPush.delegateCustom = self
self.navigationController.pushViewController(objectPush, animated: true)
Первый класс:
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)
}
}
Очень просто шаг за шагом (100% работает и проверено)
Шаг 1: Создать метод на первом контроллере представления
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)
}
шаг 3: установить делегата как
Класс ViewController: UIViewController,ProcessStatusDelegate {
Шаг 4: Создать протокол
protocol ProcessStatusDelegate:NSObjectProtocol{
func updateProcessStatus(isCompleted : Bool)
}
шаг 5: взять переменную
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)
}
Простой пример:
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
Вот простой пример кода делегата:
//MARK: - Protocol ShowResult
protocol ShowResult: AnyObject {
func show(value: Int)
}
//MARK: - MyOperation Class
class MyOperation {
weak var delegate: ShowResult?
func sum(fNumber: Int, sNumber: Int) {
delegate?.show(value: fNumber + sNumber)
}
}
//MARK: - ViewController Class
class ViewController: UIViewController,ShowResult {
var myOperation: MyOperation?
override func viewDidLoad() {
super.viewDidLoad()
loadMyOperation()
myOperation?.delegate = self
myOperation?.sum(fNumber: 100, sNumber: 20)
}
private func loadMyOperation() {
if myOperation == nil {
myOperation = MyOperation()
}
}
func show(value: Int) {
print("value: \(value)")
}
}
Делегаты - это шаблон проектирования, который позволяет одному объекту отправлять сообщения другому объекту, когда происходит определенное событие. Представьте, что объект A вызывает объект B для выполнения действия. Как только действие завершено, объект A должен знать, что B выполнил задачу, и предпринять необходимые действия, это может быть достигнуто с помощью делегатов! Вот руководство по реализации делегатов шаг за шагом в Swift 3
Вот сценарий делегата из реальной жизни.
Давайте создадим наши собственные UITextField и UITextFieldDelegate.
// THE MYSTERIOUS UITEXTFIELD
protocol UITextFieldDelegate {
func textFieldDidChange(_ textField: UITextField) -> Void
}
class UITextField {
var delegate: UITextFieldDelegate?
private var mText: String?
var text: String? {
get {
return mText
}
}
init(text: String) {
}
init() {
}
func setText(_ text: String) {
mText = text
delegate?.textFieldDidChange(self)
}
}
// HERE IS MY APP
class Main {
let textfield = UITextField()
func viewDidLoad() {
print("viewDidLoad")
textfield.delegate = self
textfield.setText("Hello")
}
}
extension Main: UITextFieldDelegate {
func textFieldDidChange(_ textField: UITextField) {
print(textField.text ?? "No string")
}
}
let main = Main()
main.viewDidLoad()
Представленные выше решения казались немного связанными и в то же время избегали повторного использования одного и того же протокола в других контроллерах, поэтому я пришел с решением, которое более строго типизировано с использованием универсального стирания типов.
@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
В Свифт 5
Я новичок, я думаю, что это самый простой способ понять практический сценарий.
Примечание: любые импровизации приветствуются
protocol APIService {
func onSuccessResponse() -> AnyObject
func onFailureResponse() -> AnyObject
}
class APIHelper{
var delegate : APIService?
func postUsersDataAPI() {
//assuming API communication is success
if(success){
let _: AnyObject? = delegate?.onSuccessResponse()
}else if(failure){
let _: AnyObject? = delegate?.onFailureResponse()
}
}
func getAllUsersAPI() {
//assuming API communication is success
if(success){
let _: AnyObject? = delegate?.onSuccessResponse()
}else if(failure){
let _: AnyObject? = delegate?.onFailureResponse()
}
}
}
class ViewController:UIViewController,APIService {
func onSuccessResponse() -> AnyObject {
print("onSuccessResponse") as AnyObject
}
func onFailureResponse() -> AnyObject {
print("onFailureResponse") as AnyObject
}
@IBAction func clickBtnToPostUserData(_ sender: Any) {
let apiHelper = APIHelper()
apiHelper.delegate = self
apiHelper.postAPI()
}
В Swift 4.0
Создайте делегата в классе, который должен отправить некоторые данные или предоставить некоторые функции другим классам.
подобно
protocol GetGameStatus {
var score: score { get }
func getPlayerDetails()
}
После этого в классе, который собирается подтвердить этот делегат
class SnakesAndLadders: GetGameStatus {
func getPlayerDetails() {
}
}