그림자 확산 및 흐림을 제어하는 ​​방법은 무엇입니까?


113

저는 스케치에서 UI 요소를 디자인했으며 그중 하나에는 블러 1과 스프레드 0이있는 그림자가 있습니다. 뷰 레이어 속성에 대한 문서를 살펴 보았고 레이어에 스프레드 또는 블러라는 이름의 어떤 것도 포함되지 않았습니다 (유일한 컨트롤은 단순히 shadowOpacity) 흐림 및 확산과 같은 것을 어떻게 제어 할 수 있습니까?

편집하다:

Sketch의 내 설정은 다음과 같습니다. 그리고 여기에 내 그림자를 원하는 모양이 있습니다.스케치 그림자 설정


그림자 구함




그리고 지금의 모습입니다. 참고, 실제로 그림자를 보려면 사진을 클릭해야합니다.

현재 그림자

내 코드는 다음과 같습니다.

func setupLayer(){
    view.layer.cornerRadius = 2
    view.layer.shadowColor = Colors.Shadow.CGColor
    view.layer.shadowOffset = CGSize(width: 0, height: 1)
    view.layer.shadowOpacity = 0.9
    view.layer.shadowRadius = 5
}

태그 ios (플랫폼), 디자인 (소프트웨어 Sketch 사용) 및 Core-Graphics (UIBezierPath를 사용하여 그림자를 그릴 수 있지만 관련성이있을 수 있음)는 모두 관련성이 있습니다. 제거됩니다.
Quantaliinuxite 2015

당신은 그 흰색보기에 대한 그림자를 원하십니까?
O-mkar

5
Sketch와 CoreAnimation 프레임 워크는 동일한 매개 변수를 사용하여 iOS와 Sketch에서 항상 다르게 보이기 때문에 메트릭이 다른 것 같습니다.
Timur Bernikovich

아. iOS 작동 방식과 거의 또는 전혀 닮지 않은 도구를 사용하는 디자이너와 함께 작업하는 즐거움. Sketch 대신 PaintCode와 같은 것으로 이동하면 iOS처럼 작동 할뿐만 아니라 필요한 코드도 제공됩니다. :-)
Fogmeister

스케치에서 반경과 블러를 모두 설정했다면?
Vladimir

답변:


257

거의 완벽한 정확도로 6 개의 Sketch 그림자 속성을 UIView의 레이어에 모두 적용하는 방법은 다음과 같습니다.

extension CALayer {
  func applySketchShadow(
    color: UIColor = .black,
    alpha: Float = 0.5,
    x: CGFloat = 0,
    y: CGFloat = 2,
    blur: CGFloat = 4,
    spread: CGFloat = 0)
  {
    shadowColor = color.cgColor
    shadowOpacity = alpha
    shadowOffset = CGSize(width: x, height: y)
    shadowRadius = blur / 2.0
    if spread == 0 {
      shadowPath = nil
    } else {
      let dx = -spread
      let rect = bounds.insetBy(dx: dx, dy: dx)
      shadowPath = UIBezierPath(rect: rect).cgPath
    }
  }
}

다음을 나타내고 싶다고 가정 해 보겠습니다.

여기에 이미지 설명 입력

다음을 통해 쉽게 수행 할 수 있습니다.

myView.layer.applySketchShadow(
  color: .black, 
  alpha: 0.5, 
  x: 0, 
  y: 0, 
  blur: 4, 
  spread: 0)

또는 더 간결하게 :

myView.layer.applySketchShadow(y: 0)

예:

여기에 이미지 설명 입력

왼쪽 : iPhone 8 UIView 스크린 샷; 오른쪽 : 스케치 직사각형.

노트 :

  • 0이 아닌을 사용 하면 CALayer를 spread기반으로 경로를 하드 코딩합니다 bounds. 레이어의 경계가 변경되면 applySketchShadow()메서드를 다시 호출해야 합니다.

흠. Sketch에서 스프레드를 적용하면 그림자의 흐림 / 그라데이션이 시작되는 '이동'됩니다. 즉, 흐림이 시작될 때까지 일정한 그림자 색상으로 유지됩니다. 하지만 shadowLayer를 이동하면 그림자는 해당 레이어에서만 시작됩니다. (스프레드가 10이라고 말하면 그림자는 오프셋 10 이후에만 시작되며 Sketch에서와 같이 흐림 / 그라데이션은 오프셋 10 이후에 시작됩니다). 그래서 그들은 실제로 일치하지 않습니다.
스테판

1
@Senseful 나는 당신 이이 공식을 어떻게 달성했는지 알고 싶습니까?!
Hashem Aboonajmi

2
설정하는 것을 잊었습니다 maskToBounds = false.
ScottyBlades

1
위에서 ScottyBlades에서 언급했듯이 maskToBounds = false. 실패하는 예는이 그림자를 UIButton.
José

5
이것은 shadowRadius = blur / UIScreen.main.scale하드 코딩과 반대 되어야합니까 blur / 2.0? 아니면 2로 나눌 또 다른 이유가 있습니까? @matchifang 이걸 알아 냈나요?
JWK

59

당신은 이것을 시도 할 수 있습니다 .... 당신은 가치를 가지고 놀 수 있습니다. 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의 기본 그림자 예제

산출


2
정보가 잘못되었고 오프셋은 스프레드를 제어하지 않고 x와 y를 제어합니다.
Quantaliinuxite

내가 실수로 내가 대답 업데이트 한 의견 교환 @Quantaliinuxite
O-mkar

맞습니다. 이제 제 질문의 핵심이됩니다. 스프레드를 어떻게 제어합니까?
Quantaliinuxite

지금 당신이 필요 확산의 양을 2.1으로 변경 업데이트 코드를 @Quantaliinuxite
O-mkar

답변 해주셔서 감사합니다. 여기서 나는 shadowOpacity를 이해할 수 없습니다 (왜 사용되는지).
Sunita 2016

11

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

산출

데모 출력

사용 방법

데모


layer.shadowRadius * 2
ShadowBlur

7

이 코드는 저에게 매우 잘 작동했습니다.

yourView.layer.shadowOpacity = 0.2 // opacity, 20%
yourView.layer.shadowColor = UIColor.black.cgColor
yourView.layer.shadowRadius = 2 // HALF of blur
yourView.layer.shadowOffset = CGSize(width: 0, height: 2) // Spread x, y
yourView.layer.masksToBounds = false

Sketch의 흐림 값이 Core Animation의 그림자 반경과 일치 하지 않습니다 .
CIFilter

우리에게 충분히 가깝게 작동하는 것은 Sketch for Core Animation의 그림자 반경에서 흐림 값을 절반으로 줄이는 것이 었습니다.
CIFilter

2

미리 정의 된 경로에 그림자를 적용하려는 사람들을 위해 (예 : 원형보기와 같이) 다음과 같은 결과를 얻었습니다.

extension CALayer {
    func applyShadow(color: UIColor = .black,
                     alpha: Float = 0.5,
                     x: CGFloat = 0,
                     y: CGFloat = 2,
                     blur: CGFloat = 4,
                     spread: CGFloat = 0,
                     path: UIBezierPath? = nil) {
        shadowColor = color.cgColor
        shadowOpacity = alpha
        shadowRadius = blur / 2
        if let path = path {
            if spread == 0 {
                shadowOffset = CGSize(width: x, height: y)
            } else {
                let scaleX = (path.bounds.width + (spread * 2)) / path.bounds.width
                let scaleY = (path.bounds.height + (spread * 2)) / path.bounds.height

                path.apply(CGAffineTransform(translationX: x + -spread, y: y + -spread).scaledBy(x: scaleX, y: scaleY))
                shadowPath = path.cgPath
            }
        } else {
            shadowOffset = CGSize(width: x, height: y)
            if spread == 0 {
                shadowPath = nil
            } else {
                let dx = -spread
                let rect = bounds.insetBy(dx: dx, dy: dx)
                shadowPath = UIBezierPath(rect: rect).cgPath
            }
        }
        shouldRasterize = true
        rasterizationScale = UIScreen.main.scale
    }
}

나중에 몇 가지 예제를 게시 할 것이지만 이것은 저에게 원형 뷰에 대해 제대로 작동했습니다.


이 "거의"작동하지만 1with x및 의 스프레드를 사용하면 스프레드가 잘못되었습니다 y 0 shapeLayer.applyShadow(color: .black, alpha: 1, x: 0, y: 0, blur: 0, spread: 1, path: path). 이 경우 그림자에는 오프셋이 있지만 그렇지 않아야합니다. 여기서 삼각형은 모든 방향으로 1px로 펼쳐진 그림자를 가져야합니다. ibb.co/YjwRb9B
Jan

1

이 게시물을 기반으로 한 내 솔루션은 다음과 같이 답합니다. (Swift 3)

let shadowPath = UIBezierPath(rect: CGRect(x: -1,
                                           y: -2,
                                           width: target.frame.width + 2,
                                           height: target.frame.height + 2))

target.layer.shadowColor = UIColor(hexString: shadowColor).cgColor
target.layer.shadowOffset = CGSize(width: CGFloat(shadowOffsetX), height: CGFloat(shadowOffsetY))
target.layer.masksToBounds = false
target.layer.shadowOpacity = Float(shadowOpacity)
target.layer.shadowPath = shadowPath.cgPath

0

여기에 게시 된 답변 과 의견의 제안이 정말 마음 듭니다 . 이것이 내가 그 솔루션을 수정 한 방법입니다.

extension UIView {
  func applyShadow(color: UIColor, alpha: Float, x: CGFloat, y: CGFloat, blur: CGFloat, spread: CGFloat) {
    layer.masksToBounds = false
    layer.shadowColor = color.cgColor
    layer.shadowOpacity = alpha
    layer.shadowOffset = CGSize(width: x, height: y)
    layer.shadowRadius = blur / UIScreen.main.scale
    if spread == 0 {
      layer.shadowPath = nil
    } else {
      let dx = -spread
      let rect = bounds.insetBy(dx: dx, dy: dx)
      layer.shadowPath = UIBezierPath(rect: rect).cgPath
    }
  }
}

용법:

myButton.applyShadow(color: .black, alpha: 0.2, x: 0, y: 1, blur: 2, spread: 0)

-1

역사상 약간 파헤쳐 졌을 수도 있지만 일부는 동일한 문제를 가지고있을 수도 있습니다. 수락 된 답변의 코드 샘플을 사용했습니다. 그러나 효과는 상당히 다릅니다.-y 값은 스케치의 동일한 값에 비해 절반 정도 여야합니다.-내비게이션 막대에 그림자를 적용하려고했는데 효과가 크게 다릅니다. 스케치와 동일한 값을 사용하면서 거의 보이지 않습니다.

따라서이 방법은 스케치 매개 변수를 완전히 반영하지 않는 것 같습니다. 힌트가 있습니까?


이것은 답이 아닙니다-댓글은 댓글로 이동합니다!
Dave Y
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.