Вопрос

Я пытаюсь нарисовать тень под нижним краем UIView в Какао 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 чтобы это сработало?

Это было полезно?

Решение

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

Другие советы

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

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

Вам необходимо импортировать 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;

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

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

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

Бывший:

enter image description here

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

Свифт 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

Свифт 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

Простое и понятное решение с использованием 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 для каждого представления на панели «Утилиты» > «Инспектор атрибутов»:

Utilities Panel

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

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

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

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

Свифт 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
    }
}

Если вы хотите использовать 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

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

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

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

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

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

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

Я изменил свою теневую реализацию для новых разработчиков для более обобщенной формы, например:

/*!
 @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;
}

Для коллег-пользователей Xamarin версия ответа для Xamarin.iOS/C# будет выглядеть следующим образом:

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

Основное отличие состоит в том, что вы приобретаете экземпляр CGContext на котором вы напрямую вызываете соответствующие методы.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top