Как рисовать тень под UIView?

Я пытаюсь нарисовать тень под нижним краем UIView в Cocoa Touch. Я понимаю, что я должен использовать CGContextSetShadow() для рисования тени, но руководство по программированию 2D кварца немного расплывчато:

  • Сохранить состояние графики.
  • Вызвать функцию CGContextSetShadow, передав соответствующие значения.
  • Выполните весь чертеж, к которому вы хотите применить тени.
  • Восстановить состояние графики

Я пробовал следующее в подклассе UIView:

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

.. но это не работает для меня, и я немного застрял (а), куда идти дальше и (б) если мне что-то нужно сделать для моего UIView, чтобы сделать эту работу?

318
задан Fraser Speirs 30 апр. '09 в 11:07
источник поделиться

12 ответов

В вашем текущем коде вы сохраняете GState текущего контекста, настраиваете его на рисование тени.. и восстанавливаете его до того, что было до того, как вы настроили его, чтобы нарисовать тень. Затем, наконец, вы вызываете реализацию суперкласса drawRect:.

Любой рисунок, на который должен влиять параметр тени, должен произойти после

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

но до

CGContextRestoreGState(currentContext);

Итак, если вы хотите, чтобы суперкласс drawRect: был "завернут" в тень, то как насчет того, измените ли вы свой код следующим образом?

- (void)drawRect:(CGRect)rect {
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextSaveGState(currentContext);
    CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
    [super drawRect: rect];
    CGContextRestoreGState(currentContext);
}
89
ответ дан Christian Brunschen 30 апр. '09 в 15:48
источник поделиться

А гораздо проще использовать несколько атрибутов уровня представления при инициализации:

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

Вам нужно импортировать QuartzCore.

#import <QuartzCore/QuartzCore.h>
747
ответ дан ollie 24 февр. '11 в 2:15
источник поделиться
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;

Это замедлит работу приложения. Добавление следующей строки может повысить производительность, если ваше представление явно прямоугольное:

self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;
221
ответ дан ZYiOS 25 марта '11 в 6:03
источник поделиться

То же решение, но только для того, чтобы напомнить вам: вы можете определить тень прямо в раскадровке.

Пример:

enter image description here

138
ответ дан Antzi 14 марта '14 в 8:26
источник поделиться

Вы можете попробовать это... вы можете играть со значениями. shadowRadius определяет количество размытия. shadowOffset определяет, куда идет тень.

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

Пример с расширением

Пример с расширением

Чтобы создать базовую тень

    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

Пример базовой тени в Swift 2.0

OUTPUT

28
ответ дан O-mkar 20 окт. '16 в 11:24
источник поделиться

Простое и чистое решение с помощью Interface Builder

Добавьте в проект файл UIView.swift(или просто вставьте его в любой файл):

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
        }
    }
}

Затем это будет доступно в Interface Builder для каждого вида в панели "Утилиты" > "Attributes Inspector":

Панель Утилиты

Теперь вы можете легко установить тень.

Примечания:
 - Тень не появится в IB, только во время выполнения.
 - Как сказал Мазен Кассер

Для тех, кому не удалось заставить это работать [...], убедитесь, что Clip Subviews (clipsToBounds) не включен

15
ответ дан Axel Guilmin 13 февр. '16 в 0:53
источник поделиться

Я использую это как часть моих утилов. При этом мы можем не только установить тень, но и получить закругленный угол для любого UIView. Также вы можете установить, какую цветовую тень вы предпочитаете. Обычно черный предпочтительнее, но иногда, когда фон не белый, вам может понадобиться что-то другое. Вот что я использую -

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;
}

Чтобы использовать это, нам нужно назвать это - [utils roundedLayer:yourview.layer radius:5.0f shadow:YES];

12
ответ дан Srikar Appalaraju 24 окт. '11 в 9:00
источник поделиться

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
    }
}
4
ответ дан neoneye 15 дек. '16 в 2:15
источник поделиться

Для тех, кому не удалось заставить это работать (как я!) после того, как вы попробовали все ответы здесь, просто убедитесь, что Подвижность Clip > не включена в инспекторе атрибутов...

3
ответ дан Mazen Kasser 08 июля '15 в 7:55
источник поделиться

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
0
ответ дан Josh O'Connor 11 авг. '17 в 20:38
источник поделиться

Вы можете использовать мою функцию утилиты, созданную для радиуса тени и угла, как показано ниже:

- (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];
}

Надеюсь, это поможет вам.

0
ответ дан Sandy 13 сент. '17 в 14:09
источник поделиться

Все Ответьте всем хорошо, но я хочу добавить еще одну точку

Если вы столкнулись с проблемой, когда у вас есть ячейки таблицы, Deque новая ячейка имеет несоответствие в тени, поэтому в этом случае вам нужно разместить теневой код в методе layoutSubviews, чтобы он хорошо себя вести во всех условиях.

-(void)layoutSubviews{
    [super layoutSubviews];

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

или в ViewControllers для определенного вида разместите теневой код внутри следующего метода, чтобы он работал хорошо

-(void)viewDidLayoutSubviews{
    [super viewDidLayoutSubviews];

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

Я изменил свою теневую реализацию для новых разработчиков для более обобщенной формы 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;
}
0
ответ дан Vishal16 13 сент. '17 в 15:52
источник поделиться

Другие вопросы по меткам