92 votes

Effet ombre interne sur UIView couche?

J'ai le texte suivant CALayer:

CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = CGRectMake(8, 57, 296, 30);
gradient.cornerRadius = 3.0f;
gradient.colors = [NSArray arrayWithObjects:(id)[RGB(130, 0, 140) CGColor], (id)[RGB(108, 0, 120) CGColor], nil];
[self.layer insertSublayer:gradient atIndex:0];

Je voudrais ajouter une ombre interne effet, mais je ne suis pas tout à fait sûr de savoir comment le faire. Je suppose que je serait nécessaire de tirer dans drawRect, cependant, ce serait ajouter la couche au-dessus des autres UIView objets, puisqu'il est censé être derrière la barre des boutons, donc je suis à une perte pour quoi faire?

Je pourrais ajouter une autre couche, mais encore une fois, vous ne savez pas comment faire pour obtenir l'effet ombre interne (comme ceci:

enter image description here

Aide appréciée...

110voto

Daniel Thorpe Points 2413

Pour quelqu'un d'autre en se demandant comment dessiner une ombre interne à l'aide de Graphiques de Base comme par Costique suggestion, alors c'est comment: (sur iOS ajuster au besoin)

Dans votre drawRect: méthode...

CGRect bounds = [self bounds];
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat radius = 0.5f * CGRectGetHeight(bounds);


// Create the "visible" path, which will be the shape that gets the inner shadow
// In this case it's just a rounded rect, but could be as complex as your want
CGMutablePathRef visiblePath = CGPathCreateMutable();
CGRect innerRect = CGRectInset(bounds, radius, radius);
CGPathMoveToPoint(visiblePath, NULL, innerRect.origin.x, bounds.origin.y);
CGPathAddLineToPoint(visiblePath, NULL, innerRect.origin.x + innerRect.size.width, bounds.origin.y);
CGPathAddArcToPoint(visiblePath, NULL, bounds.origin.x + bounds.size.width, bounds.origin.y, bounds.origin.x + bounds.size.width, innerRect.origin.y, radius);
CGPathAddLineToPoint(visiblePath, NULL, bounds.origin.x + bounds.size.width, innerRect.origin.y + innerRect.size.height);
CGPathAddArcToPoint(visiblePath, NULL,  bounds.origin.x + bounds.size.width, bounds.origin.y + bounds.size.height, innerRect.origin.x + innerRect.size.width, bounds.origin.y + bounds.size.height, radius);
CGPathAddLineToPoint(visiblePath, NULL, innerRect.origin.x, bounds.origin.y + bounds.size.height);
CGPathAddArcToPoint(visiblePath, NULL,  bounds.origin.x, bounds.origin.y + bounds.size.height, bounds.origin.x, innerRect.origin.y + innerRect.size.height, radius);
CGPathAddLineToPoint(visiblePath, NULL, bounds.origin.x, innerRect.origin.y);
CGPathAddArcToPoint(visiblePath, NULL,  bounds.origin.x, bounds.origin.y, innerRect.origin.x, bounds.origin.y, radius);
CGPathCloseSubpath(visiblePath);

// Fill this path
UIColor *aColor = [UIColor redColor];
[aColor setFill];
CGContextAddPath(context, visiblePath);
CGContextFillPath(context);


// Now create a larger rectangle, which we're going to subtract the visible path from
// and apply a shadow
CGMutablePathRef path = CGPathCreateMutable();
//(when drawing the shadow for a path whichs bounding box is not known pass "CGPathGetPathBoundingBox(visiblePath)" instead of "bounds" in the following line:)
//-42 cuould just be any offset > 0
CGPathAddRect(path, NULL, CGRectInset(bounds, -42, -42));

// Add the visible path (so that it gets subtracted for the shadow)
CGPathAddPath(path, NULL, visiblePath);
CGPathCloseSubpath(path);

// Add the visible paths as the clipping path to the context
CGContextAddPath(context, visiblePath); 
CGContextClip(context);         


// Now setup the shadow properties on the context
aColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f];
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 1.0f), 3.0f, [aColor CGColor]);   

// Now fill the rectangle, so the shadow gets drawn
[aColor setFill];   
CGContextSaveGState(context);   
CGContextAddPath(context, path);
CGContextEOFillPath(context);

// Release the paths
CGPathRelease(path);    
CGPathRelease(visiblePath);

Donc, il y a essentiellement les étapes suivantes:

  1. Créer votre chemin
  2. Définissez la couleur que vous voulez, ajoutez le chemin d'accès au contexte, et de remplir le contexte
  3. Maintenant, créez un grand rectangle qui peuvent lié visible de chemin. Avant la fermeture de ce chemin, ajoutez le visible de chemin. Puis fermer le chemin, de sorte que vous créez une forme avec le visible chemin soustrait. Vous pourriez vouloir étudier les méthodes de remplissage (enroulement non nul de pair/impair) en fonction de la façon dont vous avez créé ces chemins. En substance, pour obtenir les sous-chemins de "soustraire" lorsque vous ajoutez-les ensemble, vous avez besoin de dessiner ou plutôt de les construire) dans des directions opposées, l'une dans le sens horaire et anti-horaire.
  4. Ensuite, vous devez définir votre visible de chemin que le chemin de détourage sur le contexte, de sorte que vous n'avez pas dessiner quoi que ce soit de l'extérieur à l'écran.
  5. Puis l'installation de l'ombre sur le contexte, qui comprend le décalage, le flou et la couleur.
  6. Puis remplissez la grande forme avec le trou. La couleur n'a pas d'importance, parce que si vous avez tout fait correctement, vous ne verrez pas cette couleur, juste l'ombre.

49voto

Matt Wilding Points 12931

Je sais je suis en retard pour cette partie, mais cela m'aurait aidée à trouver au début de mes voyages...

Pour donner du crédit lorsque le crédit est dû, c'est essentiellement une modification de Daniel Thorpe élaboration sur Costique de la solution de soustraire une petite région à partir d'un ensemble de la région. Cette version est pour ceux qui utilisent la composition de la couche au lieu d'annuler -drawRect:

L' CAShapeLayer classe peut être utilisée pour obtenir le même effet:

CAShapeLayer* shadowLayer = [CAShapeLayer layer];
[shadowLayer setFrame:[self bounds]];

// Standard shadow stuff
[shadowLayer setShadowColor:[[UIColor colorWithWhite:0 alpha:1] CGColor]];
[shadowLayer setShadowOffset:CGSizeMake(0.0f, 0.0f)];
[shadowLayer setShadowOpacity:1.0f];
[shadowLayer setShadowRadius:5];

// Causes the inner region in this example to NOT be filled.
[shadowLayer setFillRule:kCAFillRuleEvenOdd];

// Create the larger rectangle path.
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectInset(bounds, -42, -42));

// Add the inner path so it's subtracted from the outer path.
// someInnerPath could be a simple bounds rect, or maybe
// a rounded one for some extra fanciness.
CGPathAddPath(path, NULL, someInnerPath);
CGPathCloseSubpath(path);

[shadowLayer setPath:path];
CGPathRelease(path);

[[self layer] addSublayer:shadowLayer];

À ce stade, si votre calque parent n'est pas pour masquer ses limites, vous verrez la zone supplémentaire de la couche de masque sur les bords de la couche. Ce sera de 42 pixels de noir si vous venez de copier l'exemple directement. Pour se débarrasser de lui, vous pouvez tout simplement utiliser un autre CAShapeLayer avec le même chemin et de le définir comme le masque de l'ombre de la couche:

CAShapeLayer* maskLayer = [CAShapeLayer layer];
[maskLayer setPath:someInnerPath];
[shadowLayer setMask:maskLayer];

Je n'ai pas comparé ça moi, mais je soupçonne que l'utilisation de cette approche en collaboration avec le tramage est plus performant que primordial -drawRect:.

35voto

Costique Points 16058

Il est possible de dessiner une ombre interne avec des Graphiques de Base en faisant un grand rectangle tracé à l'extérieur des limites, en soustrayant les limites de la taille d'un rectangle de chemin d'accès et le remplissage de la trajectoire résultante avec un "normal" de l'ombre.

Cependant, puisque vous avez besoin de le combiner avec un calque de dégradé, je pense qu'une solution plus simple est de créer un 9-partie image au format PNG transparent de l'intérieur de l'ombre et de l'étirer à la bonne taille. Les 9-la part d'ombre de l'image devrait ressembler à ceci (sa taille est de 21x21 pixels):

alt text

CALayer *innerShadowLayer = [CALayer layer];
innerShadowLayer.contents = (id)[UIImage imageNamed: @"innershadow.png"].CGImage;
innerShadowLayer.contentsCenter = CGRectMake(10.0f/21.0f, 10.0f/21.0f, 1.0f/21.0f, 1.0f/21.0f);

Puis définissez innerShadowLayer du cadre et il devrait s'étirer l'ombre correctement.

24voto

jinglesthula Points 940

Peu d'un rond-point, mais il évite d'avoir à utiliser des images (lire: facile de changer les couleurs, l'ombre de rayon, etc.) et c'est seulement quelques lignes de code.

  1. Ajouter une UIImageView que le premier sous-vue de la UIView vous voulez le dropshadow. J'utilise de l'IB, mais vous pouvez faire la même programmation.

  2. En supposant que la référence à la UIImageView est "innerShadow'

`

[[innerShadow layer] setMasksToBounds:YES];
[[innerShadow layer] setCornerRadius:12.0f];        
[[innerShadow layer] setBorderColor:[UIColorFromRGB(180, 180, 180) CGColor]];
[[innerShadow layer] setBorderWidth:1.0f];
[[innerShadow layer] setShadowColor:[UIColorFromRGB(0, 0, 0) CGColor]];
[[innerShadow layer] setShadowOffset:CGSizeMake(0, 0)];
[[innerShadow layer] setShadowOpacity:1];
[[innerShadow layer] setShadowRadius:2.0];

Mise en garde: Vous devez avoir une frontière, ou bien l'ombre n'apparaît pas. [UIColor clearColor] ne fonctionne pas. Dans l'exemple, j'utilise une couleur différente, mais vous pouvez jouer avec ça pour l'obtenir de la même couleur que le début de l'ombre. :)

17voto

SomaMan Points 2215

Mieux vaut tard que jamais...

Voici une autre approche, sans doute pas mieux que ceux qui ont déjà posté, mais c'est agréable et simple -

-(void)drawInnerShadowOnView:(UIView *)view
{
    UIImageView *innerShadowView = [[UIImageView alloc] initWithFrame:view.bounds];

    innerShadowView.contentMode = UIViewContentModeScaleToFill;
    innerShadowView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    [view addSubview:innerShadowView];

    [innerShadowView.layer setMasksToBounds:YES];

    [innerShadowView.layer setBorderColor:[UIColor lightGrayColor].CGColor];
    [innerShadowView.layer setShadowColor:[UIColor blackColor].CGColor];
    [innerShadowView.layer setBorderWidth:1.0f];

    [innerShadowView.layer setShadowOffset:CGSizeMake(0, 0)];
    [innerShadowView.layer setShadowOpacity:1.0];

    // this is the inner shadow thickness
    [innerShadowView.layer setShadowRadius:1.5];
}

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