Para qualquer pessoa que esteja se perguntando como desenhar uma sombra interna usando Core Graphics de acordo com a sugestão de Costique, faça o seguinte: (no iOS ajuste conforme necessário)
Em seu 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);
Então, essencialmente, existem as seguintes etapas:
- Crie seu caminho
- Defina a cor de preenchimento desejada, adicione este caminho ao contexto e preencha o contexto
- Agora crie um retângulo maior que pode limitar o caminho visível. Antes de fechar este caminho, adicione o caminho visível. Em seguida, feche o caminho para criar uma forma com o caminho visível subtraído dele. Você pode querer investigar os métodos de preenchimento (enrolamento diferente de zero de par / ímpar) dependendo de como você criou esses caminhos. Em essência, para fazer com que os subcaminhos "subtraiam" quando você os soma, você precisa desenhá-los (ou melhor, construí-los) em direções opostas, um no sentido horário e outro no sentido anti-horário.
- Em seguida, você precisa definir seu caminho visível como o caminho de recorte no contexto, para que você não desenhe nada fora dele na tela.
- Em seguida, configure a sombra no contexto, que inclui o deslocamento, desfoque e cor.
- Em seguida, preencha a forma grande com o orifício. A cor não importa, porque se você fez tudo certo, você não verá esta cor, apenas a sombra.
Sei que estou atrasado para essa festa, mas isso teria me ajudado a descobrir no início de minhas viagens ...
Para dar crédito a quem o crédito é devido, esta é essencialmente uma modificação da elaboração de Daniel Thorpe sobre a solução de Costique de subtrair uma região menor de uma região maior. Esta versão é para quem usa composição de camada em vez de substituir
-drawRect:
A
CAShapeLayer
classe pode ser usada para obter o mesmo efeito:Nesse ponto, se sua camada pai não estiver mascarando até os limites, você verá a área extra da camada de máscara ao redor das bordas da camada. Isso terá 42 pixels de preto se você apenas copiou o exemplo diretamente. Para se livrar dele, você pode simplesmente usar outro
CAShapeLayer
com o mesmo caminho e defini-lo como a máscara da camada de sombra:Eu mesmo não fiz o benchmarking, mas suspeito que usar essa abordagem em conjunto com a rasterização tem mais desempenho do que substituição
-drawRect:
.fonte
[[UIBezierPath pathWithRect:[shadowLayer bounds]] CGPath]
sendo a escolha mais simples.É possível desenhar uma sombra interna com Core Graphics criando um grande caminho retângulo fora dos limites, subtraindo um caminho retângulo do tamanho dos limites e preenchendo o caminho resultante com uma sombra "normal".
No entanto, como você precisa combiná-lo com uma camada de gradiente, acho que uma solução mais fácil é criar uma imagem PNG transparente de 9 partes da sombra interna e esticá-la para o tamanho certo. A imagem sombreada em 9 partes ficaria assim (seu tamanho é 21x21 pixels):
Em seguida, defina a moldura de innerShadowLayer e ela deve esticar a sombra corretamente.
fonte
Uma versão simplificada usando apenas CALayer, em Swift:
Observe que a camada de sombra interna não deve ter uma cor de fundo opaca, pois isso será renderizado na frente da sombra.
fonte
let innerShadow = CALayer(); innerShadow.frame = bounds
. Sem os limites adequados, não atrairia a sombra adequada. Obrigado de qualquer maneiralayoutSubviews()
para mantê-lo sincronizadolayoutSubviews()
ou emdraw(_ rect)
É um pouco circular, mas evita ter que usar imagens (leia-se: fácil mudar cores, raio de sombra, etc.) e são apenas algumas linhas de código.
Adicione um UIImageView como a primeira subvisualização do UIView no qual deseja colocar a sombra. Eu uso IB, mas você pode fazer o mesmo programaticamente.
Supondo que a referência ao UIImageView seja 'innerShadow'
`
Advertência: você precisa ter uma borda, ou então a sombra não aparecerá. [UIColor clearColor] não funciona. No exemplo, eu uso uma cor diferente, mas você pode mexer com ela para que fique com a mesma cor do início da sombra. :)
Veja o comentário do bbrame abaixo sobre a
UIColorFromRGB
macro.fonte
Antes tarde do que nunca...
Aqui está outra abordagem, provavelmente não melhor do que as já postadas, mas é agradável e simples -
fonte
Em vez de desenhar a sombra interna por drawRect ou adicionar UIView à vista. Você pode adicionar CALayer diretamente à borda, por exemplo: se eu quiser efeito de sombra interna na parte inferior do UIView V.
Isso adiciona uma sombra interna inferior para o UIView de destino
fonte
Aqui está uma versão do swift, change
startPoint
eendPoint
to make it em cada lado.fonte
Esta é a sua solução, que exportei do PaintCode :
fonte
Estou muito atrasado para a festa, mas gostaria de retribuir à comunidade .. Este é um método que escrevi para remover a imagem de fundo UITextField porque estava fornecendo uma biblioteca estática e NENHUM recurso ... Usei isso para uma tela de entrada de PIN de quatro instâncias de UITextField que podem exibir Um caractere bruto ou (BOOL) [self isUsingBullets] ou (BOOL) [self usingAsterisks] em ViewController. O aplicativo é para iPhone / iPhone retina / iPad / iPad Retina, então não preciso fornecer quatro imagens ...
Espero que isso ajude alguém, pois este fórum me ajudou.
fonte
Confira o ótimo artigo de Inner Shadows in Quartz de Chris Emery que explica como as sombras internas são desenhadas pelo PaintCode e fornece um trecho de código limpo e organizado:
fonte
Aqui está minha solução no Swift 4.2. Você gostaria de tentar?
fonte
let ctx = UIGraphicsGetCurrentContext
UIGraphicsGetCurrentContext
para buscar quando algumas visualizações colocam seu contexto na pilha.Solução escalável usando CALayer em Swift
Com o descrito
InnerShadowLayer
você também pode ativar sombras internas apenas para bordas específicas, excluindo outras. (por exemplo, você pode ativar sombras internas apenas nas bordas esquerda e superior de sua visualização)Você pode então adicionar um
InnerShadowLayer
à sua visualização usando:InnerShadowLayer
implementaçãofonte
este código funcionou para mim
fonte
Há algum código aqui que pode fazer isso por você. Se você alterar a camada em sua visualização (substituindo
+ (Class)layerClass
), para JTAInnerShadowLayer, então você pode definir a sombra interna na camada de indentação em seu método init e ele fará o trabalho para você. Se você também deseja desenhar o conteúdo original, certifique-se de chamarsetDrawOriginalImage:yes
a camada de recuo. Há uma postagem no blog sobre como isso funciona aqui .fonte
Usando a camada de gradiente:
fonte
Existe uma solução simples - basta desenhar a sombra normal e girar, assim
fonte