UIView 아래에 그림자를 그리는 방법은 무엇입니까?


352

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

.. 그러나 이것은 저에게 효과가 없으며 (a) 다음에 갈 곳과 (b) UIView이 일을하기 위해 해야 할 일이 있다면 조금 붙어 있습니다.

답변:


98

현재 코드 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);
}

789

훨씬 쉬운 방법은 초기화시 뷰의 일부 레이어 속성을 설정하는 것입니다.

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

QuartzCore를 가져와야합니다.

#import <QuartzCore/QuartzCore.h>

4
그러나 이것은 iOS 3.2 이상에서만 작동하므로 앱이 이전 버전에서 작동 해야하는 경우 옵션 인 경우 Christian 솔루션 또는 뷰 뒤에 정적 이미지를 사용해야합니다.
florianbuerger

119
이 솔루션은 또한 #import <QuartzCore/QuartzCore.h>".h 파일에 추가 해야합니다.
MusiGenesis

26
로 설정하면 masksToBounds을 ( NO를) 무효화합니다 cornerRadius.
pixelfreak

4
문제를 해결하려면 레이어에 backgroundColor를 설정하고보기를 투명하게 만들어야합니다.
pixelfreak

@pixelfreak 어떻게합니까? 나는 노력 self.layer.backgroundColor = [[UIColor whiteColor] CGColor];했지만 운이 없다. 어떤 뷰가 투명해야합니까?
Victor Van Hee

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

1
이 최적화는 뷰가 직사각형 인 경우에만 유용합니다.
벤자민 도벨

1
self.layer.shadowPath ... 무엇 대신에? 또는 그냥 추가
Chrizzz

2
다른 것 외에 추가 줄을 추가하십시오.
christophercotton

11
@NathanGaskin-그림자 그리기는 비용이 많이 드는 작업이므로, 예를 들어 응용 프로그램에서 다른 인터페이스 방향을 허용하고 그림자 경로를 명시 적으로 지정하지 않고 장치를 회전하기 시작하는 경우 해당 애니메이션 중에 그림자 경로를 명시 적으로 여러 번 렌더링해야합니다. 모양은 아마 눈에 띄게 애니메이션 느리게
피터 Pajchl

9
@BenjaminDobell이 특정 선은 사각형에만 적용되지만 직사각형이 아닌 경로를 만들 수도 있습니다. 예를 들어 둥근 사각형이있는 경우 bezierPathWithRoundedRect : cornerRadius를 사용할 수 있습니다.
Dan Dyer

160

동일한 솔루션이지만, 생각 나게하는 것 : 스토리 보드에서 직접 그림자를 정의 할 수 있습니다.

전의:

여기에 이미지 설명을 입력하십시오


2
슬프게도, 나는 CGColor가 스토리 보드에서 접근 할 수 없다고 생각합니다. (
Antzi

1
그냥 속성에 대한 래퍼 UIView또는 범주를 정의하십시오 ;)CGLayerUIColorCGColor
DeFrenZ

1
이 링크는 그렇게하는 방법을 설명합니다 : cocoadventures.org/post/104137872754/…
Kamchatka

8
사람들이 쉽게 복사하여 붙여 넣을 수 있도록하기 위해 : layer.masksToBound, layer.shadowOffset, layer.shadowRadius, layer.shadowOpacity. 오타가 당신을 여기서 죽일 것입니다.
etayluz

3
layer.shadow 불투명도 값은 0.5가 아니고 0.5가 아니어야합니다
Desert Rose

43

당신은 이것을 시도 할 수 있습니다 .... 당신은 값을 가지고 놀 수 있습니다. 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

스프레드가있는 예

스프레드가있는 예

기본 그림자를 만들려면

    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의 기본 그림자 예제

산출


19

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에서 사용할 수 있습니다.

유틸리티 패널

이제 그림자를 쉽게 설정할 수 있습니다.

참고 :
-그림자는 런타임에만 IB에 나타나지 않습니다.
-Mazen Kasser가 말했듯이

이 작업을 수행하는데 실패한 사람들에게 [...] Clip Subviews ( clipsToBounds)가 활성화되어 있지 않은지 확인하십시오


정답입니다. 향후 인터페이스를위한 코드를 통한 구성
Crake

이 솔루션을 사용하면 다음과 같은 경고 메시지 (각 속성마다 하나씩)가 작동하지 않습니다.Failed to set (shadowColor) user defined inspected property on (UICollectionViewCell): [<UICollectionViewCell> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key shadowColor.
Xvolks

1
XCode로 만든 UIKit기본 Foundation가져 오기로는 충분하지 않지만 컴파일이 제대로 작동 하려면 가져와야 했습니다 . 전체 소스 코드를 복사 했어야합니다. 이 멋진 솔루션에 대해 Axel에게 감사합니다.
Xvolks

13

나는 이것을 utils의 일부로 사용한다. 이것으로 우리는 그림자를 설정할 수있을뿐만 아니라 둥근 모서리를 얻을 수 있습니다 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

스위프트 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
    }
}

이 방법을 가고 있다면, 그래서 당신이 값을 조정할 수 있도록 내가 매개 변수를 추가하는 것을 고려 것이다 동적
조쉬 오코너

그것은 나에게 둥근 모서리를주고 있지 않습니다. 내가 잘못하고 있습니까?
Nikhil Manapure

함수가 있기 때문에 @NikhilManapure 아무것도 일어나고,이 수 installShadow()에서 호출되지 viewDidLoad()또는viewWillAppear()
neoneye

오버로드 draw된 관점 에서 호출했습니다 . 그림자가 올바르게오고 있지만 모서리가 둥글 지 않습니다.
Nikhil Manapure

@NikhilManapure 둥근 테두리 UIStackView가 없습니다.보기는 레이아웃 만하고보기가 없기 때문일 수 있습니다 . UIView모든 것을 위해 정규 컨테이너를 컨테이너로 삽입해야 할 수도 있습니다 .
neoneye

6

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 단계. 스토리 보드에서이 속성을 사용할 수 있습니다.스토리 보드 이미지


3

여기에 모든 답변을 시도한 후에 (나 자신처럼!)이 작업을 수행하지 못한 사람들에게는 속성 관리자에서 클립 하위보기 가 활성화되어 있지 않은지 확인하십시오 ...


1

스위프트 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

1

그림자 및 모서리 반경에 대해 작성된 유틸리티 기능을 다음과 같이 사용할 수 있습니다.

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

희망이 당신을 도울 것입니다 !!!


1

모두 잘 대답하지만 포인트를 하나 더 추가하고 싶습니다

테이블 셀이있을 때 문제가 발생하면 새 셀을 빼십시오. 그림자가 일치하지 않으므로이 경우 그림자 코드를 layoutSubviews 메서드에 배치하여 모든 조건에서 잘 작동하도록해야합니다.

-(void)layoutSubviews{
    [super layoutSubviews];

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

또는 ViewViews에서 특정 뷰 섀도우 코드를 다음 메소드 내에 배치하여 잘 작동합니다.

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

1

동료 Xamarian의 경우 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적절한 메소드를 직접 호출 하는 인스턴스를 얻는 것 입니다.


1

이것을 사용 Extension하여 그림자를 추가 할 수 있습니다

extension UIView {

    func addShadow(offset: CGSize, color: UIColor, radius: CGFloat, opacity: Float)
    {
        layer.masksToBounds = false
        layer.shadowOffset = offset
        layer.shadowColor = color.cgColor
        layer.shadowRadius = radius
        layer.shadowOpacity = opacity

        let backgroundCGColor = backgroundColor?.cgColor
        backgroundColor = nil
        layer.backgroundColor =  backgroundCGColor
    }
}

당신은 그것을처럼 부를 수 있습니다

your_Custom_View.addShadow(offset: CGSize(width: 0, height: 1), color: UIColor.black, radius: 2.0, opacity: 1.0)

0

Swift 4에서 IBDesignable 및 IBInspectable을 사용하여 그림자 스케치

사용 방법

데모

측면에서 스케치 및 XCODE

그림자 시험

암호

@IBDesignable class ShadowView: UIView {

    @IBInspectable var shadowColor: UIColor? {
        get {
            if let color = layer.shadowColor {
                return UIColor(cgColor: color)
            }
            return nil
        }
        set {
            if let color = newValue {
                layer.shadowColor = color.cgColor
            } else {
                layer.shadowColor = nil
            }
        }
    }

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

    @IBInspectable var shadowOffset: CGPoint {
        get {
            return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
        }
        set {
            layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
        }

     }

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

    @IBInspectable var shadowSpread: CGFloat = 0 {
        didSet {
            if shadowSpread == 0 {
                layer.shadowPath = nil
            } else {
                let dx = -shadowSpread
                let rect = bounds.insetBy(dx: dx, dy: dx)
                layer.shadowPath = UIBezierPath(rect: rect).cgPath
            }
        }
    }
}

산출

데모 출력


속성에서 그림자 접두사를 제거하는 코드를 향상시킬 수 있습니다. ShadowView의 목표는 그림자 작업에 대해 분명합니다. 이 클래스에 코너 반경을 추가 할 수 있습니다. (오늘, 대부분의 그림자 뷰에는 코너 반경이
포함됨
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.