Pergunta

Eu estou tentando desenhar uma sombra sob a borda inferior de um UIView em Cocoa Touch. Eu entendo que eu deveria usar CGContextSetShadow() para desenhar a sombra, mas o guia de programação Quartz 2D é um pouco vago:

  1. Salvar o estado gráficos.
  2. Chame a função CGContextSetShadow, passando os valores apropriados.
  3. Realizar todo o desenho para o qual você deseja aplicar sombras.
  4. Restaurar o estado gráficos

Eu tentei o seguinte em uma subclasse UIView:

- (void)drawRect:(CGRect)rect {
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextSaveGState(currentContext);
    CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
    CGContextRestoreGState(currentContext);
    [super drawRect: rect];
}

.. mas isso não funciona para mim e eu estou um pouco preso sobre (a) para onde ir e (b) se há alguma coisa que eu preciso fazer para o meu UIView para fazer este trabalho?

Foi útil?

Solução

Em seu código atual, você salvar o GState do contexto atual, configure-o para desenhar uma sombra .. eo restaurá-lo para o que era antes você configurou-lo para desenhar uma sombra. Então, finalmente, você chamar a implementação da superclasse de drawRect:.

Qualquer desenho que deve ser afetado pelas necessidades de ajuste de sombra para acontecer depois

CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);

e antes

CGContextRestoreGState(currentContext);

Então, se você quer drawRect: da superclasse ser 'embrulhado' em uma sombra, então como sobre se você reorganizar o seu código como este?

- (void)drawRect:(CGRect)rect {
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextSaveGState(currentContext);
    CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
    [super drawRect: rect];
    CGContextRestoreGState(currentContext);
}

Outras dicas

A pela abordagem muito mais fácil é definir alguns atributos da camada de vista sobre a inicialização:

self.layer.masksToBounds = NO;
self.layer.shadowOffset = CGSizeMake(-15, 20);
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = 0.5;

Você precisa importar QuartzCore.

#import <QuartzCore/QuartzCore.h>
self.layer.masksToBounds = NO;
self.layer.cornerRadius = 8; // if you like rounded corners
self.layer.shadowOffset = CGSizeMake(-15, 20);
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = 0.5;

Isto irá desacelerar a aplicação. Adicionando a seguinte linha pode melhorar o desempenho, enquanto o seu ponto de vista é visivelmente retangular:

self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;

A mesma solução, mas apenas para lembrá-lo:. Você pode definir a sombra diretamente no storyboard

Ex:

enter descrição da imagem aqui

Você pode tentar isso .... você pode jogar com os valores. O shadowRadius dita a quantidade de desfocagem. ditames shadowOffset onde a sombra vai.

Swift 2.0

let radius: CGFloat = demoView.frame.width / 2.0 //change it to .height if you need spread for height
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 2.1 * radius, height: demoView.frame.height))
//Change 2.1 to amount of spread you need and for height replace the code for height

demoView.layer.cornerRadius = 2
demoView.layer.shadowColor = UIColor.blackColor().CGColor
demoView.layer.shadowOffset = CGSize(width: 0.5, height: 0.4)  //Here you control x and y
demoView.layer.shadowOpacity = 0.5
demoView.layer.shadowRadius = 5.0 //Here your control your blur
demoView.layer.masksToBounds =  false
demoView.layer.shadowPath = shadowPath.CGPath

Swift 3.0

let radius: CGFloat = demoView.frame.width / 2.0 //change it to .height if you need spread for height 
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 2.1 * radius, height: demoView.frame.height)) 
//Change 2.1 to amount of spread you need and for height replace the code for height

demoView.layer.cornerRadius = 2
demoView.layer.shadowColor = UIColor.black.cgColor
demoView.layer.shadowOffset = CGSize(width: 0.5, height: 0.4)  //Here you control x and y
demoView.layer.shadowOpacity = 0.5
demoView.layer.shadowRadius = 5.0 //Here your control your blur
demoView.layer.masksToBounds =  false
demoView.layer.shadowPath = shadowPath.cgPath

Exemplo de propagação

 Exemplo com spread

Para criar uma sombra básica

    demoView.layer.cornerRadius = 2
    demoView.layer.shadowColor = UIColor.blackColor().CGColor
    demoView.layer.shadowOffset = CGSizeMake(0.5, 4.0); //Here your control your spread
    demoView.layer.shadowOpacity = 0.5 
    demoView.layer.shadowRadius = 5.0 //Here your control your blur

exemplo Sombra Básica em Swift 2.0

 OUTPUT

Uma solução simples e limpo usando Interface Builder

Adicionar um arquivo chamado UIView.swift em seu projeto (ou apenas cole este em qualquer arquivo):

import UIKit

@IBDesignable extension UIView {

    /* The color of the shadow. Defaults to opaque black. Colors created
    * from patterns are currently NOT supported. Animatable. */
    @IBInspectable var shadowColor: UIColor? {
        set {
            layer.shadowColor = newValue!.CGColor
        }
        get {
            if let color = layer.shadowColor {
                return UIColor(CGColor:color)
            }
            else {
                return nil
            }
        }
    }

    /* The opacity of the shadow. Defaults to 0. Specifying a value outside the
    * [0,1] range will give undefined results. Animatable. */
    @IBInspectable var shadowOpacity: Float {
        set {
            layer.shadowOpacity = newValue
        }
        get {
            return layer.shadowOpacity
        }
    }

    /* The shadow offset. Defaults to (0, -3). Animatable. */
    @IBInspectable var shadowOffset: CGPoint {
        set {
            layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
        }
        get {
            return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
        }
    }

    /* The blur radius used to create the shadow. Defaults to 3. Animatable. */
    @IBInspectable var shadowRadius: CGFloat {
        set {
            layer.shadowRadius = newValue
        }
        get {
            return layer.shadowRadius
        }
    }
}

Então este estará disponível na Interface Builder para cada exibição no Painel de Utilidades> Atributos Inspector:

 Painel Utilities

Você pode facilmente definir a sombra agora.

Notas:
- A sombra não aparecerá no IB apenas em tempo de execução
. - Como Mazen Kasser disse

Para aqueles que falharam em conseguir isso para o trabalho [...] certificar-se de Clipe subvisualizações (clipsToBounds) não está habilitado

Eu uso isso como parte do meu utils. Com isso, não só pode definir sombra, mas também pode obter um canto arredondado para qualquer UIView. Além disso, você pode definir o que sombra cor que você preferir. Normalmente preto é preferível, mas às vezes, quando o fundo é não-branco que você pode querer outra coisa. Aqui está o que eu uso -

in utils.m
+ (void)roundedLayer:(CALayer *)viewLayer 
              radius:(float)r 
              shadow:(BOOL)s
{
    [viewLayer setMasksToBounds:YES];
    [viewLayer setCornerRadius:r];        
    [viewLayer setBorderColor:[RGB(180, 180, 180) CGColor]];
    [viewLayer setBorderWidth:1.0f];
    if(s)
    {
        [viewLayer setShadowColor:[RGB(0, 0, 0) CGColor]];
        [viewLayer setShadowOffset:CGSizeMake(0, 0)];
        [viewLayer setShadowOpacity:1];
        [viewLayer setShadowRadius:2.0];
    }
    return;
}

Para usar isso, precisamos chamar isso - [utils roundedLayer:yourview.layer radius:5.0f shadow:YES];

Swift 3

extension UIView {
    func installShadow() {
        layer.cornerRadius = 2
        layer.masksToBounds = false
        layer.shadowColor = UIColor.black.cgColor
        layer.shadowOffset = CGSize(width: 0, height: 1)
        layer.shadowOpacity = 0.45
        layer.shadowPath = UIBezierPath(rect: bounds).cgPath
        layer.shadowRadius = 1.0
    }
}

Se você gostaria de usar StoryBoard e não gostaria de continuar digitando em atributos de tempo de execução, você pode facilmente criar uma extensão para vistas e torná-los utilizáveis ??em storyboard.

Passo 1. criar extensão

extension UIView {

@IBInspectable var shadowRadius: CGFloat {
    get {
        return layer.shadowRadius
    }
    set {
        layer.shadowRadius = newValue
    }
}

@IBInspectable var shadowOpacity: Float {
    get {
        return layer.shadowOpacity
    }
    set {
        layer.shadowOpacity = newValue
    }
}

@IBInspectable var shadowOffset: CGSize {
    get {
        return layer.shadowOffset
    }
    set {
        layer.shadowOffset = newValue
    }
}

@IBInspectable var maskToBound: Bool {
    get {
        return layer.masksToBounds
    }
    set {
        layer.masksToBounds = newValue
    }
}
}

passo 2. agora você pode usar esses atributos no storyboard imagem storyboard

Para aqueles que falharam em conseguir isso para o trabalho (como eu!) Depois de tentar todas as respostas aqui, apenas certifique-se Clipe subvisualizações não está habilitado para o inspetor Atributos ...

Swift 3

self.paddingView.layer.masksToBounds = false
self.paddingView.layer.shadowOffset = CGSize(width: -15, height: 10)
self.paddingView.layer.shadowRadius = 5
self.paddingView.layer.shadowOpacity = 0.5

Você pode usar a minha função de utilidade criada para sombra e raio de canto como abaixo:

- (void)addShadowWithRadius:(CGFloat)shadowRadius withShadowOpacity:(CGFloat)shadowOpacity withShadowOffset:(CGSize)shadowOffset withShadowColor:(UIColor *)shadowColor withCornerRadius:(CGFloat)cornerRadius withBorderColor:(UIColor *)borderColor withBorderWidth:(CGFloat)borderWidth forView:(UIView *)view{

    // drop shadow
    [view.layer setShadowRadius:shadowRadius];
    [view.layer setShadowOpacity:shadowOpacity];
    [view.layer setShadowOffset:shadowOffset];
    [view.layer setShadowColor:shadowColor.CGColor];

    // border radius
    [view.layer setCornerRadius:cornerRadius];

    // border
    [view.layer setBorderColor:borderColor.CGColor];
    [view.layer setBorderWidth:borderWidth];
}

Hope vai ajudá-lo !!!

Todos Resposta tudo bem mas gostaria de acrescentar mais um ponto

Se você encontrar um problema quando você tem células da tabela, Deque uma nova célula há uma incompatibilidade na sombra portanto, neste caso, você precisa colocar o seu código de sombra em um método layoutSubviews para que ele irá se comportar muito bem em todas as condições.

-(void)layoutSubviews{
    [super layoutSubviews];

    [self.contentView setNeedsLayout];
    [self.contentView layoutIfNeeded];
    [VPShadow applyShadowView:self];
}

ou em ViewControllers para código de sombra específica vista lugar dentro do método seguinte para que seja de trabalho bem

-(void)viewDidLayoutSubviews{
    [super viewDidLayoutSubviews];

    [self.viewShadow layoutIfNeeded];
    [VPShadow applyShadowView:self.viewShadow];
}

Eu modifiquei minha implementação sombra para novos devs para mais generalizada forma ex:

/*!
 @brief Add shadow to a view.

 @param layer CALayer of the view.

 */
+(void)applyShadowOnView:(CALayer *)layer OffsetX:(CGFloat)x OffsetY:(CGFloat)y blur:(CGFloat)radius opacity:(CGFloat)alpha RoundingCorners:(CGFloat)cornerRadius{
    UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:layer.bounds cornerRadius:cornerRadius];
    layer.masksToBounds = NO;
    layer.shadowColor = [UIColor blackColor].CGColor;
    layer.shadowOffset = CGSizeMake(x,y);// shadow x and y
    layer.shadowOpacity = alpha;
    layer.shadowRadius = radius;// blur effect
    layer.shadowPath = shadowPath.CGPath;
}

Para companheiros Xamarians, o Xamarin.iOS / C # versão da resposta seria parecido com o seguinte:

public override void DrawRect(CGRect area, UIViewPrintFormatter formatter)
{
    CGContext currentContext = UIGraphics.GetCurrentContext();
    currentContext.SaveState();
    currentContext.SetShadow(new CGSize(-15, 20), 5);
    base.DrawRect(area, formatter);
    currentContext.RestoreState();                
}

A principal diferença é que você adquirir uma instância de CGContext em que você chamar diretamente os métodos apropriados.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top