Dessiner une forme arrondie UIView avec gradient et de l'ombre portée
EDIT:
J'ai enfin trouvé une vraie solution simple à ce problème, en utilisant le CAGradientLayer de classe, et l'CALayer fonctionnalités de dessin.
Ole Begemann a publié un grand UIView wrapper pour CAGradientLayer classe nommée OBGradientView.
Cette classe permet de créer facilement un gradient de UIView dans votre application.
Ensuite, vous utilisez la CALayer fonctionnalités de dessin pour ajouter des coins arrondis et de l'ombre portée des valeurs :
//Create the gradient view
OBGradientView *gradient = [[OBGradientView alloc] initWithFrame:someRect];
NSArray *colors = [NSArray arrayWithObjects:[UIColor redColor], [UIColor yellowColor], nil];
gradient.colors = colors;
//Set rounded corners and drop shadow
gradient.layer.cornerRadius = 5.0;
gradient.layer.shadowColor = [UIColor grayColor].CGColor;
gradient.layer.shadowOpacity = 1.0;
gradient.layer.shadowOffset = CGSizeMake(2.0, 2.0);
gradient.layer.shadowRadius = 3.0;
[self.view addSubview:gradient];
[gradient release];
N'oubliez pas d'ajouter le QuartzCore cadre de votre projet.
QUESTION D'ORIGINE:
Je travaille sur un contrôle personnalisé qui est un rectangle arrondi bouton, rempli avec un dégradé linéaire, et d'avoir une ombre portée.
J'ai rempli les deux premières étapes à l'aide de cette réponse : texte du lien
Mon problème est maintenant d'ajouter une ombre portée sous la forme obtenue.
En fait, le contexte a été découpée à la forme arrondie rect chemin, de sorte que lorsque j'utilise le CGContextSetShadow fonction, il n'a pas le dessiner.
J'ai essayé de résoudre ce problème par le dessin de la arrondies rect deux fois, d'abord avec une couleur unie, de sorte qu'il attire l'ombre, puis redessiner avec le remplissage en dégradé.
Il un peu travaillé, mais je peut encore voir quelques pixels à l'un des coins de la forme résultant de la première dessiner avec une couleur unie, comme vous pouvez le voir sur cette version agrandie :
http://img269.imageshack.us/img269/6489/capturedcran20100701192.png
C'est presque bon, mais pas encore parfait...
Ici est ma -drawRect: mise en œuvre :
static void addRoundedRectToPath(CGContextRef context, CGRect rect, float ovalWidth, float ovalHeight)
{
float fw, fh;
if (ovalWidth == 0 || ovalHeight == 0) {
CGContextAddRect(context, rect);
return;
}
CGContextSaveGState(context);
CGContextTranslateCTM (context, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGContextScaleCTM (context, ovalWidth, ovalHeight);
fw = CGRectGetWidth (rect) / ovalWidth;
fh = CGRectGetHeight (rect) / ovalHeight;
CGContextMoveToPoint(context, fw, fh/2);
CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);
CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1);
CGContextClosePath(context);
CGContextRestoreGState(context);
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGSize shadowOffset = CGSizeMake(10.0, 10.0);
CGFloat blur = 5.0;
rect.size.width -= shadowOffset.width + blur;
rect.size.height -= shadowOffset.height + blur;
CGContextSaveGState(context);
addRoundedRectToPath(context, rect, _radius, _radius);
CGContextSetShadow (context, shadowOffset, blur);
CGContextDrawPath(context, kCGPathFill);
CGContextRestoreGState(context);
addRoundedRectToPath(context, rect, _radius, _radius);
CGContextClip(context);
CGFloat colors[] =
{
_gradientStartColor.red, _gradientStartColor.green, _gradientStartColor.blue, _gradientStartColor.alpha,
_gradientEndColor.red, _gradientEndColor.green, _gradientEndColor.blue, _gradientEndColor.alpha
};
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(rgb, colors, locations, num_locations);
CGRect currentBounds = self.bounds;
CGPoint gStartPoint = CGPointMake(CGRectGetMidX(currentBounds), 0.0f);
CGPoint gEndPoint = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMaxY(currentBounds));
CGContextDrawLinearGradient(context, gradient, gStartPoint, gEndPoint, 0);
CGColorSpaceRelease(rgb);
CGGradientRelease(gradient);
}
Des idées sur la façon de le faire d'une autre manière ?
Merci !
OriginalL'auteur Thomas Desert | 2010-07-01
Vous devez vous connecter pour publier un commentaire.
Afin de créer un arrondi d'angle de vue avec un arrière-plan en dégradé et de l'ombre portée, voici ce qu'il a fait:
La première partie est très similaire à ce qui est prévu dans la question, il crée un arrondi le rectangle tracé à l'aide de CGPathAddArcToPoint comme le décrit très bien dans cet article. Voici une photo pour m'aider à comprendre:
La deuxième partie se déroule comme suit:
Permettre l'ombrage sur le contexte graphique, ajoutez le chemin d'accès a été défini, puis de remplir ce chemin. Vous ne pouvez pas appliquer de l'ombre à juste le chemin lui-même (les chemins ne font pas partie de l'état graphique), de sorte que vous devez remplir la voie de l'ombre à apparaître (je suppose que le chemin d'un contour peut également travailler?). Vous ne pouvez pas il suffit d'appliquer de l'ombre à un gradient puisqu'il n'est pas vraiment un standard de remplissage (voir ce post pour plus d'info).
Une fois que vous avez rempli arrondie rect qui crée de l'ombre, vous avez besoin de tirer le dégradé sur le dessus de cela. Ajoutez le chemin d'accès un deuxième temps, afin de régler la saturation de la zone, puis tracez le dégradé à l'aide de CGContextDrawLinearGradient. Je ne pense pas que vous pouvez facilement "remplir" un chemin avec un dégradé comme vous pourrait avec le plus haut standard-étape de remplissage, de sorte qu'au lieu de vous remplir la zone de dessin avec le dégradé et puis le clip rectangle arrondi de la zone qui vous intéresse.
Pouvez-vous expliquer comment les tangentes à la courbe et les points sont créés à partir d'arguments? Je ne comprends pas comment vous pouvez créer deux lignes et de deux points à partir de 4 arguments...
quelqu'un peut m'expliquer comment cela fonctionne? J'ai sous-classes d'une coutume UIView et fait de cette fonction c'est drawRect remplacer mais j'ai toujours pas de coins arrondis ou de l'ombre portée?
OriginalL'auteur beno
J'ai une solution qui n'a pas besoin de pré-remplir de la voie. L'avantage(?) c'est que l'ombre peut utiliser des effets de transparence de la pente (c'est à dire si le gradient est d'opaque à trasparent, l'ombre sera partiellement transparent) et est plus simple.
Il dit plus ou moins:
Je suppose que c'est parce CGContextBeginTransparencyLayer/CGContextEndTransparencyLayer est à l'extérieur de l'élément et de l'ombre est appliquée à la couche (qui contient remplis de chemin d'accès). Au moins, cela semble fonctionner pour moi.
OriginalL'auteur Jan Slupski
Pour les ombres, vous pouvez utiliser
CGContextSetShadow()
Ce code permet de dessiner quelque chose avec une ombre:
OriginalL'auteur Eiko
Votre (original) problème est que vous avez été à nouveau dessin de une ombre quand vous avez appelé le gradient. Cette ombre avait un (0,0) de décalage et un peu de flou, qui ne brille à travers dans les virages. Dans la ligne avant CGContextDrawLinearGradient(...), ajoutez la ligne suivante:
Le NUL de la valeur de couleur désactive les effets d'ombre et supprimera le coin en effet.
OriginalL'auteur Jared Crawford