Làm thế nào để kiểm soát sự lan truyền bóng và làm mờ?


112

Tôi đã thiết kế các phần tử giao diện người dùng trong bản phác thảo và một trong số chúng có bóng với độ mờ 1 và độ lan rộng 0. Tôi đã xem tài liệu để biết thuộc tính lớp chế độ xem và lớp không có bất kỳ thứ gì có tên là trải rộng hoặc mờ hoặc bất kỳ thứ gì tương đương (điều khiển duy nhất là chỉ đơn thuần là shadowOpacity) Làm cách nào để kiểm soát những thứ như mờ và lan rộng?

BIÊN TẬP:

Đây là cài đặt của tôi trong Sketch: Và đây là những gì tôi muốn bóng của mình trông như thế nào:Phác thảo cài đặt bóng


Shadow muốn




Và đây là những gì nó trông như thế này vào lúc này: Lưu ý, bạn phải nhấp vào hình ảnh để thực sự nhìn thấy bóng.

Bóng hiện tại

Mã của tôi như sau:

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
}

Các thẻ ios (nền tảng), thiết kế (sử dụng phần mềm Sketch) và Core-Graphics (có thể sử dụng UIBezierPath để vẽ bóng, bất kỳ có thể có liên quan) đều có liên quan, tôi không hiểu tại sao chúng nên được gỡ bỏ.
Quantaliinuxite

bạn chỉ muốn bóng cho chế độ xem màu trắng phải không?
O-mkar

5
Có vẻ như khung công tác Sketch và CoreAnimation có các chỉ số khác nhau, vì nó luôn trông khác nhau trên iOS và trong Sketch có cùng thông số.
Timur Bernikovich

Ah. Niềm vui khi làm việc với các nhà thiết kế sử dụng các công cụ gần như không giống hoặc không giống với cách hoạt động của iOS. Nếu bạn chuyển sang một cái gì đó như PaintCode thay vì Sketch, nó sẽ không chỉ hoạt động như iOS hoạt động mà còn cung cấp cho bạn mã bạn cần. :-)
Fogmeister

Điều gì sẽ xảy ra nếu bạn đã đặt cả bán kính và độ mờ trong bản phác thảo?
Vlad

Câu trả lời:


256

Dưới đây là cách áp dụng tất cả 6 thuộc tính đổ bóng của Sketch cho lớp của UIView với độ chính xác gần như hoàn hảo:

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

Giả sử chúng tôi muốn trình bày những điều sau:

nhập mô tả hình ảnh ở đây

Bạn có thể dễ dàng thực hiện việc này thông qua:

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

hoặc ngắn gọn hơn:

myView.layer.applySketchShadow(y: 0)

Thí dụ:

nhập mô tả hình ảnh ở đây

Trái: Ảnh chụp màn hình UIView của iPhone 8; phải: Hình chữ nhật phác thảo.

Ghi chú:

  • Khi sử dụng số khác 0 spread, nó mã hóa một đường dẫn dựa trên boundsCALayer. Nếu giới hạn của lớp thay đổi, bạn sẽ muốn gọi lại applySketchShadow()phương thức.

Hừm. Trong Sketch, nếu bạn áp dụng độ lan tỏa, nó sẽ 'di chuyển ra ngoài', nơi bắt đầu làm mờ / chuyển màu của bóng. Có nghĩa là - nó vẫn ở màu bóng không đổi, cho đến khi bắt đầu mờ. Nhưng nếu chúng ta di chuyển shadowLayer xung quanh, bóng đổ sẽ chỉ bắt đầu từ lớp đó, phải không? (Giả sử độ lan rộng là 10, bóng đổ sẽ chỉ bắt đầu sau độ lệch 10, trong đó như trong Sketch, độ mờ / gradient sẽ bắt đầu sau độ lệch là 10). Vì vậy, họ không thực sự khớp, phải không?
Stephan

1
@Senseful Tôi muốn biết làm thế nào bạn đạt được công thức này ?!
Hashem Aboonajmi

2
Bạn quên thiết lập maskToBounds = false.
ScottyBlades

1
Như đã đề cập ở trên bởi ScottyBlades, điều này sẽ không hoạt động nếu không có maskToBounds = false. Một ví dụ mà nó sẽ không thành công là khi áp dụng bóng này cho một UIButton.
José

5
Điều này có nên shadowRadius = blur / UIScreen.main.scaletrái ngược với mã hóa cứng blur / 2.0? Hoặc, có một lý do nào khác để chia cho 2? @matchifang Bạn có tìm ra điều này không?
JWK

59

Bạn có thể thử điều này .... bạn có thể chơi với các giá trị. Lệnh shadowRadiuschỉ ra lượng mờ. shadowOffsetra lệnh cho bóng đi đâu.

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

Ví dụ với spread

Ví dụ với spread

Để tạo một bóng đổ cơ bản

    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

Ví dụ về Shadow cơ bản trong Swift 2.0

ĐẦU RA


2
Thông tin sai, bù đắp không phải là kiểm soát của spread, mà là kiểm soát của x và y.
Quantaliinuxite,

@Quantaliinuxite tôi vô tình đổi chổ lẫn nhau các ý kiến tôi đã cập nhật các câu trả lời
O-mkar

Đúng, và bây giờ là cốt lõi của câu hỏi của tôi: làm cách nào để kiểm soát mức chênh lệch?
Quantaliinuxite vào

@Quantaliinuxite đang cập nhật hiện thay đổi 2.1 cho lượng lây lan bạn cần
O-mkar

Cảm ơn vì câu trả lời. Ở đây tôi không thể hiểu shadowOpacity (tại sao nó được sử dụng).
Sunita

11

Sketch Shadow bằng IBDesignable và IBInspectable trong Swift 4

SKETCH VÀ XCODE THEO MẶT

Shadow Exampl

CODE

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

ĐẦU RA

ĐẦU RA DEMO

LÀM THẾ NÀO ĐỂ SỬ DỤNG NÓ

BẢN GIỚI THIỆU


Không nên bạn quay trở lại layer.shadowRadius * 2cho shadowBlur getter
berkuqo

7

Mã này hoạt động rất tốt đối với tôi:

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

Giá trị mờ trong Sketch không khớp với bán kính bóng của Core Animation.
CIFilter

Những gì kết thúc làm việc đủ gần với chúng tôi là giảm một nửa giá trị mờ trong bán kính bóng của Sketch for Core Animation.
CIFilter

2

Đối với những người đang cố gắng áp dụng bóng đổ cho một đường dẫn được xác định trước (chẳng hạn như đối với chế độ xem hình tròn), đây là những gì tôi đã kết thúc:

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

Tôi sẽ đăng một số ví dụ sau, nhưng điều này đã có tác dụng đối với các chế độ xem hình tròn đối với tôi.


Điều này "gần như" hoạt động nhưng spread sai khi bạn sử dụng spread 1với xy 0 shapeLayer.applyShadow(color: .black, alpha: 1, x: 0, y: 0, blur: 0, spread: 1, path: path). Trong trường hợp này, bóng đổ có độ lệch trong khi không phải. Ở đây, hình tam giác sẽ có bóng trải rộng 1px theo mọi hướng ibb.co/YjwRb9B
Ngày

1

Giải pháp của tôi dựa trên câu trả lời bài đăng này: (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

Tôi thực sự thích câu trả lời được đăng ở đây và đề xuất trong các bình luận. Đây là cách tôi sửa đổi giải pháp đó:

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

SỬ DỤNG:

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

-1

Nó có thể là một chút sai lầm trong lịch sử, nhưng có thể một số đã gặp vấn đề tương tự. Tôi đã sử dụng mẫu mã từ câu trả lời được chấp nhận. Tuy nhiên, các hiệu ứng khá khác nhau: - giá trị y phải bằng một nửa so với giá trị tương tự trong bản phác thảo - Tôi đã thử áp dụng bóng đổ trên thanh điều hướng và hiệu ứng rất khác biệt - hầu như không nhìn thấy khi sử dụng cùng giá trị mà bản phác thảo có.

Vì vậy, có vẻ như phương pháp này hoàn toàn không phản ánh các thông số phác thảo. Bất kỳ gợi ý?


Đây không phải là một câu trả lời - nhận xét đi trong nhận xét!
Dave Y
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.