Как нарисовать тень под UIView? - PullRequest
345 голосов
/ 30 апреля 2009

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

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

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

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

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

Ответы [ 14 ]

784 голосов
/ 24 февраля 2011

Гораздо более простой подход - установить некоторые атрибуты слоя представления при инициализации:

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

Вам необходимо импортировать QuartzCore.

#import <QuartzCore/QuartzCore.h>
230 голосов
/ 25 марта 2011
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;
156 голосов
/ 14 марта 2014

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

Ex:

enter image description here

94 голосов
/ 30 апреля 2009

В вашем текущем коде вы сохраняете 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);
}
42 голосов
/ 20 октября 2016

Вы можете попробовать это .... вы можете играть со значениями. 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

Пример со спредом

Example with spread

Для создания базовой тени

    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

19 голосов
/ 13 февраля 2016

Простое и чистое решение с использованием 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
        }
    }
}

Тогда это будет доступно в Интерфейсном Разработчике для каждого представления на Панели утилит> Инспектор атрибутов:

Utilities Panel

Вы можете легко установить тень сейчас.

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

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

13 голосов
/ 24 октября 2011

Я использую это как часть моих утилит. При этом мы можем не только установить тень, но и получить закругленный угол для любого 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];

7 голосов
/ 15 декабря 2016

Свифт 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
    }
}
5 голосов
/ 22 августа 2018

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

Шаг 1. создать расширение

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

шаг 2. Теперь вы можете использовать эти атрибуты в раскадровке storyboard image

3 голосов
/ 08 июля 2015

Тем, кто не смог заставить это работать (как я!), Попробовав все ответы здесь, просто убедитесь, что Клип Subviews не включен в инспекторе атрибутов ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...