simplificar el esquema de delegates con bloques, ¿es posible en este context?

He leído tantas cosas positivas sobre el uso de bloques, en particular, que simplifica el código eliminando las llamadas delegadas. He encontrado ejemplos donde los bloques se usan al final de la animation en lugar de las llamadas de delegado, allí entiendo cómo se puede hacer.

Pero realmente me gustaría saber si el engorroso esquema de tener que usar delegates al presentar y descartar a los controlleres de vista también se puede simplificar con bloques.

La forma recomendada de mostrar y rechazar el esquema estándar es este, donde en VC1 se presenta un nuevo VC2 que el delegado descarta nuevamente en VC1.

VC2 *vc2 = [[VC2 alloc] initWithNibName:@"VC2" bundle:nil ]; vc2.delegate = self; [self presentModalViewController: vc2 animated: YES]; // or however you present your VC2 

Withis VC2 vuelve a vc1:

 [self.delegate vc2HasFinished]; 

Para que esto funcione, uno tiene que crear un protocolo como este: en el file VC2Protocol.h

  @protocol VC2Protocol <NSObject> -(void)vc2HasFinished; @end 

Luego, incluya este VC2Protocol.h en VC1 y VC2. En VC1 uno tiene que definir este método así:

 -(void) weitereRufNrVCDidFinish{ [self dismissModalViewControllerAnimated:YES completion: nil]; } 

Realmente sería bueno si se puede escribir un código más conciso, evitando tener que declarar un protocolo solo por eso.

¡Gracias!

Para el caso de rechazar una presentación vc modalidad, tenga en count que la vc puede desestimarse. Entonces, en lugar de [self.delegate vc2HasFinished]; puede decir [self dismissModalViewControllerAnimated:YES completion: nil]; dentro de vc2

Pero estoy de acuerdo contigo en que bloquea los útiles y los delegates son torpes (y más propensos a errores, especialmente pre-ARC). Entonces, así es como puede replace las devoluciones de llamada de delegado en un vc. Inventemos una situación en la que el vc querría decir que delega algo, por ejemplo, que acaba de search una image …

 // vc2.h @property (nonatomic, copy) void (^whenYouFetchAnImage)(UIImage *); // note, no delegate property here // vc2.m // with your other synthesizes @synthesize whenYouFetchAnImage=_whenYouFetchAnImage; // when the image is fetched self.whenYouFetchAnImage(theFetchedImage); 

El vc de presentación no establece un delegado, pero le da al nuevo vc algún código para ejecutar (en su propio context de ejecución) cuando se busca una image …

 // presenting vc.m VC2 *vc2 = [[VC2 alloc] initWithNibName:@"VC2" bundle:nil]; // say this presenting vc has an image view that will show the image fetched // by vc2. (not totally plausible since this image view will probably be covenetworking by vc2 // when the block is invoked) vc2.whenYouFetchAnImage = ^(UIImage *image) { self.myImageView.image = image; }; 

Los bloques se pueden utilizar como properties / ivars en VC2, por lo que podría tener un completionBlock que VC1 establece para ser lo que quiera que sea y VC2 podría llamarlo completionBlock(); una vez hecho.

Básicamente:

 typedef void (^VC2CompletionBlock)(void); @interface VC2 : UIViewController { VC2CompletionBlock completionBlock; } @property (nonatomic, copy) VC2CompletionBlock completionBlock; @end 

Y luego, en algún lugar de VC2.m, puedes llamar

 ... self.completionBlock(); ... 

Typedefing sus bloques le permite crear un nuevo tipo personalizado de bloques, quizás uno con un valor de retorno o algún otro parámetro, que luego puede ser pasado al bloque por VC2

 typedef void (^VC2CompletionBlock)(BOOL success, NSData data); 

Espero que esto ayude, el uso de bloques es poderoso, ya que el object en sí mismo solo necesita conocer la estructura básica del bloque (es decir, los parameters que es capaz de captar), no necesita ninguna información sobre el bloque o quién lo creó.

Precaución, sin embargo, los bloques pueden causar problemas de memory extraños, así que asegúrese de leer la documentation adecuada en ellos.

Puede resolver esto de forma general, sin tener que crear iVars de bloque específicos en todos los controlleres. Podría crear una class que proporciona el procesamiento de bloque "cuando está hecho", y luego simplemente henetworkingar de ella, y sus controlleres de vista tendrán todas las capacidades "cuando están hechas". Puede configurar la propiedad o proporcionar un método de "conveniencia".

Tenga en count que el código original de esta primera pieza simplemente se rompió, así que lo cambié. – Ugh. Que embarazoso. De todos modos, obtienes la idea (y solo sugiero esto si eres alguien que aborrece las asociaciones).

 // Use this as a base class for your view controllers... typedef void(^WhenDoneWithViewControllerBlock)( UIViewController *viewController, BOOL canceled); @interface BlockDismissingViewController : UIViewController @property (nonatomic, strong) WhenDoneWithViewControllerBlock whenDone; - (void)done:(BOOL)canceled; @end @implementation BlockDismissingViewController - (void)done:(BOOL)canceled { if (self.whenDone) { self.whenDone(self, canceled); } } @end // The "convenience" method should probably be something like this... @implementation UIViewController (BlockDismissingViewController) - (void)presentViewController:(BlockDismissingViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion whenDone:(WhenDoneWithViewControllerBlock)whenDone { viewControllerToPresent.whenDone = whenDone; [self presentViewController:viewControllerToPresent animated:flag completion:completion]; } @end 

O bien, podría hacerlo como una categoría en UIViewController, y ahora todos sus controlleres de vista obtendrán esta funcionalidad. Puede usar el centro de notifications para invocar el bloque apropiado …

 @interface UIViewController (WhenDoneWithViewControllerBlock) - (void)done:(BOOL)canceled; @end @implementation UIViewController (WhenDoneWithViewControllerBlock) - (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion whenDone:(WhenDoneWithViewControllerBlock)doneBlock { if (doneBlock) { __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"DoneWithViewControllerNotification" object:viewControllerToPresent queue:nil usingBlock:^(NSNotification *note) { [[NSNotificationCenter defaultCenter] removeObserver:observer]; doneBlock(viewControllerToPresent, [[note.userInfo objectForKey:@"canceled"] boolValue]); }]; } [self presentViewController:viewControllerToPresent animated:flag completion:completion]; } - (void)done:(BOOL)canceled { [[NSNotificationCenter defaultCenter] postNotificationName:@"DoneWithViewControllerNotification" object:self userInfo:@{ @"canceled" : @(canceled) }]; } @end 

O bien, si aún desea una categoría, pero desea un iVar y omitir el centro de notifications …

 // Using associated objects in a category @interface UIViewController (WhenDoneWithViewControllerBlock) @property (nonatomic, strong) WhenDoneWithViewControllerBlock whenDone; - (void)done:(BOOL)canceled; @end @implementation UIViewController (WhenDoneWithViewControllerBlock) char const kWhenDoneKey[1]; - (WhenDoneWithViewControllerBlock)whenDone { return objc_getAssociatedObject(self, kWhenDoneKey); } - (void)setWhenDone:(WhenDoneWithViewControllerBlock)whenDone { objc_setAssociatedObject(self, kWhenDoneKey, whenDone, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion whenDone:(WhenDoneWithViewControllerBlock)whenDone { viewControllerToPresent.whenDone = whenDone; [self presentViewController:viewControllerToPresent animated:flag completion:completion]; } - (void)done:(BOOL)canceled { if (self.whenDone) { self.whenDone(self, canceled); } } @end 

Por supuesto, estos son solo ejemplos, pero espero que tengas la idea.

Cuando finaliza el controller de vista, solo llama

 [self done:canceledOrSuccess]; 

y el bloque será invocado.

Usar la última categoría es mi favorito, a pesar de que hay un costo de performance tanto en el time como en la memory para los objects asociados. Usted obtiene la conveniencia de un "iVar" que contiene su bloque "cuando está hecho" (puede configurarlo explícitamente), y obtiene el método de "conveniencia" para presentar, y cada controller de vista obtiene automáticamente esta funcionalidad, simplemente agregando la categoría.

La forma recomendada de descartar un controller de vista es dismissViewControllerAnimated: finalización. Entonces aquí está tu bloque que estás buscando.