Pour quiconque se demande comment dessiner une ombre intérieure en utilisant Core Graphics selon la suggestion de Costique, voici comment: (sur iOS, ajustez au besoin)
Dans votre drawRect: method ...
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:
- Créez votre chemin
- Définissez la couleur de remplissage souhaitée, ajoutez ce chemin au contexte et remplissez le contexte
- Créez maintenant un rectangle plus grand qui peut délimiter le chemin visible. Avant de fermer ce chemin, ajoutez le chemin visible. Fermez ensuite le tracé afin de créer une forme dont le tracé visible en est soustrait. Vous voudrez peut-être étudier les méthodes de remplissage (enroulement non nul de pair / impair) en fonction de la façon dont vous avez créé ces chemins. Essentiellement, pour obtenir les sous-chemins à «soustraire» lorsque vous les additionnez, vous devez les dessiner (ou plutôt les construire) dans des directions opposées, l'une dans le sens des aiguilles d'une montre et l'autre dans le sens inverse.
- Ensuite, vous devez définir votre chemin visible comme chemin de détourage sur le contexte, afin de ne rien dessiner en dehors de celui-ci à l'écran.
- Ensuite, configurez l'ombre sur le contexte, qui comprend le décalage, le flou et la couleur.
- Remplissez ensuite la grande forme avec le trou dedans. La couleur n'a pas d'importance, car si vous avez tout fait correctement, vous ne verrez pas cette couleur, juste l'ombre.
Je sais que je suis en retard à cette soirée, mais cela m'aurait aidé à trouver tôt dans mes voyages ...
Pour donner du crédit là où le mérite est dû, il s'agit essentiellement d'une modification de l'élaboration de Daniel Thorpe sur la solution de Costique consistant à soustraire une région plus petite d'une région plus grande. Cette version est destinée à ceux qui utilisent la composition des calques au lieu de remplacer
-drawRect:
La
CAShapeLayer
classe peut être utilisée pour obtenir le même effet:À ce stade, si votre calque parent ne masque pas ses limites, vous verrez la zone supplémentaire du calque de masque autour des bords du calque. Ce sera 42 pixels de noir si vous venez de copier directement l'exemple. Pour vous en débarrasser, vous pouvez simplement en utiliser un autre
CAShapeLayer
avec le même chemin et le définir comme masque du calque d'ombre:Je n'ai pas évalué cela moi-même, mais je soupçonne que l'utilisation de cette approche en conjonction avec la rastérisation est plus performante que la substitution
-drawRect:
.la source
[[UIBezierPath pathWithRect:[shadowLayer bounds]] CGPath]
étant le choix le plus simple.Il est possible de dessiner une ombre intérieure avec Core Graphics en créant un grand chemin rectangle en dehors des limites, en soustrayant un chemin rectangle de la taille des limites et en remplissant le chemin résultant avec une ombre "normale" activée.
Cependant, comme vous devez le combiner avec un calque de dégradé, je pense qu'une solution plus simple consiste à créer une image PNG transparente en 9 parties de l'ombre intérieure et à l'étirer à la bonne taille. L'image d'ombre en 9 parties ressemblerait à ceci (sa taille est de 21x21 pixels):
Ensuite, définissez le cadre de innerShadowLayer et il devrait étirer correctement l'ombre.
la source
Une version simplifiée utilisant juste un CALayer, dans Swift:
Notez que le calque innerShadow ne doit pas avoir une couleur d'arrière-plan opaque, car elle sera rendue devant l'ombre.
la source
let innerShadow = CALayer(); innerShadow.frame = bounds
. Sans limites appropriées, il ne dessinerait pas l'ombre appropriée. Merci quand mêmelayoutSubviews()
pour le garder synchronisélayoutSubviews()
ou dansdraw(_ rect)
Un peu rond, mais cela évite d'avoir à utiliser des images (lire: facile à changer les couleurs, le rayon de l'ombre, etc.) et ce n'est que quelques lignes de code.
Ajoutez un UIImageView en tant que première sous-vue de l'UIView sur laquelle vous voulez que le dropshadow. J'utilise IB, mais vous pouvez faire la même chose par programmation.
En supposant que la référence à UIImageView est 'innerShadow'
»
Attention: vous devez avoir une bordure, sinon l'ombre n'apparaît pas. [UIColor clearColor] ne fonctionne pas. Dans l'exemple, j'utilise une couleur différente, mais vous pouvez la modifier pour qu'elle ait la même couleur que le début de l'ombre. :)
Voir le commentaire de bbrame ci-dessous sur la
UIColorFromRGB
macro.la source
Mieux vaut tard que jamais...
Voici une autre approche, probablement pas meilleure que celles déjà publiées, mais c'est sympa et simple -
la source
Au lieu de dessiner une ombre intérieure par drawRect ou ajoutez UIView à la vue. Vous pouvez directement Ajouter CALayer à la bordure, par exemple: si je veux un effet d'ombre intérieure sur le bas de UIView V.
Cela ajoute une ombre intérieure inférieure pour UIView cible
la source
Voici une version de swift, changez
startPoint
etendPoint
faites-le de chaque côté.la source
Voici votre solution, que j'ai exportée depuis PaintCode :
la source
Je suis très en retard à la fête mais je voudrais redonner à la communauté .. C'est une méthode que j'ai écrite pour supprimer l'image d'arrière-plan UITextField car je fournissais une bibliothèque statique et AUCUNE ressource ... J'ai utilisé ceci pour un écran de saisie de code PIN de quatre instances UITextField pouvant afficher un caractère brut ou (BOOL) [self isUsingBullets] ou (BOOL) [self usingAsterisks] dans ViewController. L'application est pour iPhone / iPhone Retina / iPad / iPad Retina donc je n'ai pas à fournir quatre images ...
J'espère que cela aide quelqu'un car ce forum m'a aidé.
la source
Consultez l'excellent article de Inner Shadows in Quartz de Chris Emery qui explique comment les ombres intérieures sont dessinées par PaintCode et donne un extrait de code propre et soigné:
la source
Voici ma solution dans Swift 4.2. Voudriez-vous essayer?
la source
let ctx = UIGraphicsGetCurrentContext
UIGraphicsGetCurrentContext
à récupérer lorsque certaines vues poussent leur contexte sur la pile.Solution évolutive utilisant CALayer dans Swift
Avec le décrit
InnerShadowLayer
vous pouvez également activer les ombres intérieures pour des arêtes spécifiques uniquement, à l'exclusion des autres. (par exemple, vous pouvez activer les ombres intérieures uniquement sur les bords gauche et supérieur de votre vue)Vous pouvez ensuite ajouter un
InnerShadowLayer
à votre vue en utilisant:InnerShadowLayer
la mise en oeuvrela source
ce code a fonctionné pour moi
la source
Il y a du code ici qui peut le faire pour vous. Si vous changez le calque dans votre vue (en
+ (Class)layerClass
remplaçant), en JTAInnerShadowLayer, vous pouvez définir l'ombre interne sur le calque de retrait dans votre méthode init et il fera le travail pour vous. Si vous souhaitez également dessiner le contenu d'origine, assurez-vous d'appelersetDrawOriginalImage:yes
le calque de retrait. Il y a un article de blog sur la façon dont cela fonctionne ici .la source
Utilisation de la couche de dégradé:
la source
Il existe une solution simple: dessinez simplement l'ombre normale et faites-la pivoter, comme ceci
la source