34 votes

Utiliser CALayer Delegate

J'ai une UIView dont les couches ont des sous-couches. J'aimerais attribuer des délégués pour chacune de ces sous-couches, de sorte que le délégué méthode peut dire la couche de quoi dessiner. Ma question est:

Que dois-je fournir comme CALayer du délégué? La documentation dit de ne pas utiliser le UIView les couches de résidence, tel que cela est réservé pour les principaux CALayer de la vue. Mais, la création d'une autre classe juste pour être le délégué de la CALayers je créer défaites le but de ne pas sous-classement CALayer. Ce sont des personnes généralement en utilisant en tant que délégué pour CALayer? Ou devrais-je simplement une sous-classe?

Aussi, pourquoi est-ce que la classe implémentant le délégué méthodes n'ont pas à se conformer à une sorte de CALayer protocole? C'est une vaste question générale, je ne comprends pas très bien. Je pensais que toutes les classes nécessitant la mise en œuvre de déléguer les méthodes de spécification de protocole pour les agents à se conformer.

30voto

Dave Lee Points 2324

Préférant garder la couche délégué méthodes dans mon sous-classe UIView, j'utilise une base de re-la délégation de délégué de classe. Cette classe peut être réutilisé sans personnalisation, en évitant le recours à la sous-classe CALayer ou de la création d'un délégué de classe juste pour la couche de dessin.

@interface LayerDelegate : NSObject
- (id)initWithView:(UIView *)view;
@end

avec cette mise en œuvre:

@interface LayerDelegate ()
@property (nonatomic, weak) UIView *view;
@end

@implementation LayerDelegate

- (id)initWithView:(UIView *)view {
    self = [super init];
    if (self != nil) {
        _view = view;
    }
    return self;
}

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context {
    NSString *methodName = [NSString stringWithFormat:@"draw%@Layer:inContext:", layer.name];
    SEL selector = NSSelectorFromString(methodName);
    if ([self.view respondsToSelector:selector] == NO) {
        selector = @selector(drawLayer:inContext:);
    }

    void (*drawLayer)(UIView *, SEL, CALayer *, CGContextRef) = (__typeof__(drawLayer))objc_msgSend;
    drawLayer(self.view, selector, layer, context);
}

@end

Le nom de la couche est utilisé pour permettre couche par couche personnalisée méthodes draw. Par exemple, si vous avez attribué un nom à votre couche, dire layer.name = @"Background";, alors vous pouvez mettre en œuvre une méthode comme ceci:

- (void)drawBackgroundLayer:(CALayer *)layer inContext:(CGContextRef)context;

Remarque, vous aurez besoin d'un solide de référence de l'instance de cette classe, et il peut être utilisé en tant que délégué pour n'importe quel nombre de couches.

layerDelegate = [[LayerDelegate alloc] initWithView:self];
layer1.delegate = layerDelegate;
layer2.delegate = layerDelegate;

28voto

Felixyz Points 10705

La solution la plus légère serait de créer une petite classe d'assistance dans le fichier sous le nom UIView utilisant la couche CALayer:

Dans MyView.h

 @interface MyLayerDelegate : NSObject
. . .
@end
 

Dans MyView.m

 @implementation MyLayerDelegate
- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)ctx
{
. . .
}
@end
 

Il suffit de placer ceux-ci en haut de votre fichier, immédiatement sous les directives #import. De cette façon, vous aurez plus l'impression d'utiliser une "classe privée" pour gérer le dessin (bien que ce ne soit pas le cas - la classe déléguée peut être instanciée par n'importe quel code qui importe l'en-tête).

9voto

Matt Long Points 16701

Jetez un oeil à la docs sur formel vs informel protocoles. Le CALayer est la mise en œuvre d'un protocole informel qui signifie que vous pouvez définir n'importe quel objet à son délégué, et il permettra de déterminer si elle peut envoyer des messages à ce délégué par la vérification de la délégué pour un sélecteur (c-à -respondsToSelector).

En général, je utiliser mon point de vue, contrôleur en tant que délégué de la couche en question.

3voto

Dale Points 73

Une remarque concernant "l'aide" des classes pour l'utiliser comme une couche délégué (avec ARC):au moins

Assurez-vous de stocker un "fort" en référence à votre allocation/init avait de la classe helper (comme dans une propriété). Tout simplement l'attribution de l'allocation/init avait de la classe helper pour le délégué semble provoquer des accidents pour moi, sans doute parce que mylayer.déléguer est une référence faible à votre classe d'aide (comme la plupart des délégués sont), donc la classe helper obtient libéré avant que la couche de pouvoir l'utiliser.

Si je attribuer la classe helper pour un bien, puis l'assigner à la délégué, mes étranges accidents de disparaître, et les choses se comportent comme prévu.

2voto

James Points 94

Personnellement, j'ai voté pour Dave Lee solution ci-dessus comme étant les plus de l'encapsulation, en particulier dans le cas de plusieurs couches. Cependant, lorsque je l'ai essayé sur IOS 6 avec l'ARC j'ai eu des erreurs sur cette ligne et de suggérer que j'ai besoin d'une passerelle en fonte

// [_view performSelector: selector withObject: layer withObject: (id)context];

J'ai donc modifié Dave Lee drawLayer méthode de re-la délégation de délégué de classe à employer NSInvocation comme ci-dessous. Toute utilisation et les fonctions auxiliaires sont identiques à ceux de Dave Lee a posté sur son antérieures excellente suggestion.

-(void) drawLayer: (CALayer*) layer inContext: (CGContextRef) context
{
    NSString* methodName = [NSString stringWithFormat: @"draw%@Layer:inContext:", layer.name];
    SEL selector = NSSelectorFromString(methodName);

    if ( ![ _view respondsToSelector: selector])
    {
        selector = @selector(drawLayer:inContext:);   
    }

    NSMethodSignature * signature = [[_view class] instanceMethodSignatureForSelector:selector];
    NSInvocation * invocation = [NSInvocation invocationWithMethodSignature:signature];

    [invocation setTarget:_view];             // Actually index 0    
    [invocation setSelector:selector];        // Actually index 1    

    [invocation setArgument:&layer atIndex:2];
    [invocation setArgument:&context atIndex:3];

    [invocation invoke];

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X