Para cualquiera que se pregunte cómo dibujar una sombra interior usando Core Graphics según la sugerencia de Costique, entonces así es como: (en iOS, ajuste según sea necesario)
En su método drawRect: ...
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);
Entonces, esencialmente existen los siguientes pasos:
- Crea tu camino
- Establezca el color de relleno que desee, agregue esta ruta al contexto y complete el contexto
- Ahora cree un rectángulo más grande que pueda delimitar la ruta visible. Antes de cerrar esta ruta, agregue la ruta visible. Luego cierre la ruta, de modo que cree una forma con la ruta visible restada. Es posible que desee investigar los métodos de relleno (devanado distinto de cero de par / impar) dependiendo de cómo haya creado estas rutas. En esencia, para que los subtrazados se "resten" cuando los suma, debe dibujarlos (o más bien construirlos) en direcciones opuestas, una en sentido horario y otra en sentido antihorario.
- Luego, debe establecer su ruta visible como la ruta de recorte en el contexto, para que no dibuje nada fuera de ella en la pantalla.
- Luego, configure la sombra en el contexto, que incluye el desplazamiento, el desenfoque y el color.
- Luego llene la forma grande con el agujero. El color no importa, porque si has hecho todo bien, no verás este color, solo la sombra.
Sé que llego tarde a esta fiesta, pero esto me habría ayudado a encontrar temprano en mis viajes ...
Para dar crédito a quien se debe el crédito, esto es esencialmente una modificación de la elaboración de Daniel Thorpe sobre la solución de Costique de restar una región más pequeña de una región más grande. Esta versión es para aquellos que usan la composición de capas en lugar de anular
-drawRect:
La
CAShapeLayer
clase se puede utilizar para lograr el mismo efecto:En este punto, si su capa principal no se enmascara hasta sus límites, verá el área adicional de la capa de máscara alrededor de los bordes de la capa. Estos serán 42 píxeles de negro si copia el ejemplo directamente. Para deshacerse de él, simplemente puede usar otro
CAShapeLayer
con el mismo camino y configurarlo como la máscara de la capa de sombra:No lo he comparado yo mismo, pero sospecho que usar este enfoque junto con la rasterización es más eficaz que anular
-drawRect:
.fuente
[[UIBezierPath pathWithRect:[shadowLayer bounds]] CGPath]
siendo la opción más sencilla.Es posible dibujar una sombra interior con Core Graphics haciendo una ruta rectangular grande fuera de los límites, restando una ruta rectangular del tamaño de los límites y llenando la ruta resultante con una sombra "normal".
Sin embargo, dado que necesita combinarlo con una capa de degradado, creo que una solución más fácil es crear una imagen PNG transparente de 9 partes de la sombra interior y estirarla al tamaño correcto. La imagen de sombra de 9 partes se vería así (su tamaño es de 21x21 píxeles):
Luego configure el marco de innerShadowLayer y debería estirar la sombra correctamente.
fuente
Una versión simplificada usando solo un CALayer, en Swift:
Tenga en cuenta que la capa innerShadow no debe tener un color de fondo opaco, ya que se representará frente a la sombra.
fuente
let innerShadow = CALayer(); innerShadow.frame = bounds
. Sin los límites adecuados, no dibujaría la sombra adecuada. Gracias de todos modoslayoutSubviews()
para mantenerlo sincronizadolayoutSubviews()
o endraw(_ rect)
Un poco indirecto, pero evita tener que usar imágenes (léase: colores fáciles de cambiar, radio de sombra, etc.) y son solo unas pocas líneas de código.
Agregue un UIImageView como la primera subvista de UIView en la que desea la sombra. Yo uso IB, pero puedes hacer lo mismo mediante programación.
Suponiendo que la referencia a UIImageView es 'innerShadow'
'
Advertencia: debe tener un borde, o de lo contrario la sombra no aparece. [UIColor clearColor] no funciona. En el ejemplo, uso un color diferente, pero puedes jugar con él para que tenga el mismo color que el comienzo de la sombra. :)
Vea el comentario de bbrame a continuación sobre la
UIColorFromRGB
macro.fuente
Mejor tarde que nunca...
Aquí hay otro enfoque, probablemente no mejor que los que ya se han publicado, pero es agradable y simple:
fuente
En lugar de dibujar una sombra interna con drawRect o agregar UIView a la Vista. Puede Agregar directamente CALayer al borde, por ejemplo: si quiero un efecto de sombra interna en la parte inferior de UIView V.
Esto agrega una sombra interior inferior para el objetivo UIView
fuente
Aquí hay una versión de swift, change
startPoint
yendPoint
para hacerlo en cada lado.fuente
Esta es su solución, que he exportado desde PaintCode :
fuente
Llego muy tarde a la fiesta, pero me gustaría retribuir a la comunidad ... Este es un método que escribí para eliminar la imagen de fondo de UITextField ya que estaba proporcionando una biblioteca estática y SIN recursos ... Lo usé para una pantalla de entrada de PIN de cuatro instancias de UITextField que podrían mostrar Un carácter sin formato o (BOOL) [self isUsingBullets] o (BOOL) [self usingAsterisks] en ViewController. La aplicación es para iPhone / iPhone retina / iPad / iPad Retina, por lo que no tengo que proporcionar cuatro imágenes ...
Espero que esto ayude a alguien como este foro me ha ayudado.
fuente
Consulte el gran artículo de Inner Shadows in Quartz de Chris Emery que explica cómo PaintCode dibuja las sombras internas y proporciona un fragmento de código limpio y ordenado:
fuente
Aquí está mi solución en Swift 4.2. ¿Quieres intentarlo?
fuente
let ctx = UIGraphicsGetCurrentContext
UIGraphicsGetCurrentContext
para buscar cuando algunas vistas empujan su contexto a la pila.Solución escalable usando CALayer en Swift
Con lo descrito
InnerShadowLayer
también puede habilitar sombras internas solo para bordes específicos, excluyendo otros. (por ejemplo, puede habilitar sombras internas solo en los bordes izquierdo y superior de su vista)Luego puede agregar un
InnerShadowLayer
a su vista usando:InnerShadowLayer
implementaciónfuente
este código funcionó para mí
fuente
Hay un cierto código de aquí que puede hacer esto para usted. Si cambia la capa en su vista (anulando
+ (Class)layerClass
), a JTAInnerShadowLayer, entonces puede configurar la sombra interna en la capa de sangría en su método de inicio y hará el trabajo por usted. Si también desea dibujar el contenido original, asegúrese de llamarsetDrawOriginalImage:yes
a la capa de sangría. Hay una publicación de blog sobre cómo funciona esto aquí .fuente
Usando la capa de degradado:
fuente
Hay una solución simple: simplemente dibuje la sombra normal y gírela, así
fuente