Добавление внутренней тени к вершине UIView

Я поднял глаза, но не смог найти, как добавить внутреннюю тень в UIView, только верхнюю (сверху вниз) для Swift. Каков наилучший способ добавить внутренний круг в Свифт?

Изменить: я нашел несколько вопросов и ответов на SO, но они либо в obj-c, либо выглядят настолько сложными. Я просто искал более Swifty, если есть

Чего я хочу достичь:

введите описание изображения здесь

Ответ 1

Вот чистая версия Swift, которую я взбивал:

public class EdgeShadowLayer: CAGradientLayer {

    public enum Edge {
        case Top
        case Left
        case Bottom
        case Right
    }

    public init(forView view: UIView,
                edge: Edge = Edge.Top,
                shadowRadius radius: CGFloat = 20.0,
                toColor: UIColor = UIColor.white,
                fromColor: UIColor = UIColor.black) {
        super.init()
        self.colors = [fromColor.cgColor, toColor.cgColor]
        self.shadowRadius = radius

        let viewFrame = view.frame

        switch edge {
            case .Top:
                startPoint = CGPoint(x: 0.5, y: 0.0)
                endPoint = CGPoint(x: 0.5, y: 1.0)
                self.frame = CGRect(x: 0.0, y: 0.0, width: viewFrame.width, height: shadowRadius)
            case .Bottom:
                startPoint = CGPoint(x: 0.5, y: 1.0)
                endPoint = CGPoint(x: 0.5, y: 0.0)
                self.frame = CGRect(x: 0.0, y: viewFrame.height - shadowRadius, width: viewFrame.width, height: shadowRadius)
            case .Left:
                startPoint = CGPoint(x: 0.0, y: 0.5)
                endPoint = CGPoint(x: 1.0, y: 0.5)
                self.frame = CGRect(x: 0.0, y: 0.0, width: shadowRadius, height: viewFrame.height)
            case .Right:
                startPoint = CGPoint(x: 1.0, y: 0.5)
                endPoint = CGPoint(x: 0.0, y: 0.5)
                self.frame = CGRect(x: viewFrame.width - shadowRadius, y: 0.0, width: shadowRadius, height: viewFrame.height)
        }
    }

    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }   
}

Чтобы использовать его,

let topShadow = EdgeShadowLayer(forView: targetView, edge: .Top)
view.layer.addSublayer(topShadow)

Обратите внимание, что по умолчанию используется черно-белый градиент глубиной 20 пикселей.

Полный код с образцом UIViewController, который позволяет вам переключать тени во всех четырех углах представления, можно найти в https://github.com/jrtibbetts/Tenebrae. Я также подробно документировал EdgeShadowLayer.

Ответ 2

Я использовал внутреннюю тень для UIView, используя Objective-C. Я пытаюсь перевести код в быстрый. Пожалуйста, простите меня за мой слабый синтаксис

вы можете вызвать функцию ниже в UIView.didMoveToSuperview

func drawShadow() {
    if nil == self.shadowLayer {
        let size = self.frame.size
        self.clipsToBounds = true
        let layer: CALayer = CALayer()
        layer.backgroundColor = UIColor.lightGrayColor().CGColor
        layer.position = CGPointMake(size.width / 2, -size.height / 2 + 0.5)
        layer.bounds = CGRectMake(0, 0, size.width, size.height)
        layer.shadowColor = UIColor.darkGrayColor().CGColor
        layer.shadowOffset = CGSizeMake(0.5, 0.5)
        layer.shadowOpacity = 0.8
        layer.shadowRadius = 5.0
        self.shadowLayer = layer

        self.layer.addSublayer(layer)
    }
}

Ответ 3

Я переписал решение @NRitH на Swift 3, также немного реорганизовал его:

final class SideShadowLayer: CAGradientLayer {
    enum Side {
        case top,
        bottom,
        left,
        right
    }

    init(frame: CGRect, side: Side, shadowWidth: CGFloat,
         fromColor: UIColor = .black,
         toColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0),
         opacity: Float = 0.5) {
        super.init()

        colors = [fromColor.cgColor, toColor.cgColor]
        self.opacity = opacity

        switch side {
        case .bottom:
            startPoint = CGPoint(x: 0.5, y: 1.0)
            endPoint = CGPoint(x: 0.5, y: 0.0)
            self.frame = CGRect(x: 0, y: frame.height - shadowWidth, width: frame.width, height: shadowWidth)

        case .top:
            startPoint = CGPoint(x: 0.5, y: 0.0)
            endPoint = CGPoint(x: 0.5, y: 1.0)
            self.frame = CGRect(x: 0, y: 0, width: frame.width, height: shadowWidth)

        case .left:
            startPoint = CGPoint(x: 0.0, y: 0.5)
            endPoint = CGPoint(x: 1.0, y: 0.5)
            self.frame = CGRect(x: 0, y: 0, width: shadowWidth, height: frame.height)

        case .right:
            startPoint = CGPoint(x: 1.0, y: 0.5)
            endPoint = CGPoint(x: 0.0, y: 0.5)
            self.frame = CGRect(x: frame.width - shadowWidth, y: 0, width: shadowWidth, height: frame.height)
        }
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

Ответ 4

Я обновил @NRitH ответ и сделал расширение из него также изменено так, что вы можете манипулировать несколькими ребрами за один раз

Использование

myview.addShadow(to: [.top,.bottom], radius: 15.0)

extension UIView{

    func addShadow(to edges:[UIRectEdge], radius:CGFloat){

        let toColor = UIColor(colorLiteralRed: 235.0/255.0, green: 235.0/255.0, blue: 235.0/255.0, alpha: 1.0)
        let fromColor = UIColor(colorLiteralRed: 188.0/255.0, green: 188.0/255.0, blue: 188.0/255.0, alpha: 1.0)
        // Set up its frame.
        let viewFrame = self.frame
        for edge in edges{
            let gradientlayer          = CAGradientLayer()
            gradientlayer.colors       = [fromColor.cgColor,toColor.cgColor]
            gradientlayer.shadowRadius = radius

            switch edge {
            case UIRectEdge.top:
                gradientlayer.startPoint = CGPoint(x: 0.5, y: 0.0)
                gradientlayer.endPoint = CGPoint(x: 0.5, y: 1.0)
                gradientlayer.frame = CGRect(x: 0.0, y: 0.0, width: viewFrame.width, height: gradientlayer.shadowRadius)
            case UIRectEdge.bottom:
                gradientlayer.startPoint = CGPoint(x: 0.5, y: 1.0)
                gradientlayer.endPoint = CGPoint(x: 0.5, y: 0.0)
                gradientlayer.frame = CGRect(x: 0.0, y: viewFrame.height - gradientlayer.shadowRadius, width: viewFrame.width, height: gradientlayer.shadowRadius)
            case UIRectEdge.left:
                gradientlayer.startPoint = CGPoint(x: 0.0, y: 0.5)
                gradientlayer.endPoint = CGPoint(x: 1.0, y: 0.5)
                gradientlayer.frame = CGRect(x: 0.0, y: 0.0, width: gradientlayer.shadowRadius, height: viewFrame.height)
            case UIRectEdge.right:
                gradientlayer.startPoint = CGPoint(x: 1.0, y: 0.5)
                gradientlayer.endPoint = CGPoint(x: 0.0, y: 0.5)
                gradientlayer.frame = CGRect(x: viewFrame.width - gradientlayer.shadowRadius, y: 0.0, width: gradientlayer.shadowRadius, height: viewFrame.height)
            default:
                break
            }
            self.layer.addSublayer(gradientlayer)
        }

    }

    func removeAllSublayers(){
        if let sublayers = self.layer.sublayers, !sublayers.isEmpty{
            for sublayer in sublayers{
                sublayer.removeFromSuperlayer()
            }
        }
    }

}

Тень

Ответ 5

Используйте следующую библиотеку:

https://github.com/nlampi/UIView-InnerShadow

// Add a shadow to the top of the view only
[exampleView addInnerShadowWithRadius:3.0f
                             andColor:[UIColor colorWithWhite:0 alpha:0.45f]
                          inDirection:NLInnerShadowDirectionTop];

Я сделал все возможное, чтобы преобразовать расширение библиотеки в Swift (это непроверено, поэтому я не могу гарантировать, что он будет работать). Это должно сделать вас на правильном пути, хотя.

import UIKit

extension UIView {

    struct NLInnerShadowDirection: OptionSetType {
        let rawValue: Int

        static let None = NLInnerShadowDirection(rawValue: 0)
        static let Left = NLInnerShadowDirection(rawValue: 1 << 0)
        static let Right = NLInnerShadowDirection(rawValue: 1 << 1)
        static let Top = NLInnerShadowDirection(rawValue: 1 << 2)
        static let Bottom = NLInnerShadowDirection(rawValue: 1 << 3)
        static let All = NLInnerShadowDirection(rawValue: 15)
    }

    func removeInnerShadow() {
        for view in self.subviews {
            if (view.tag == 2639) {
                view.removeFromSuperview()
                break
            }
        }
    }

    func addInnerShadow() {
        let c = UIColor()
        let color = c.colorWithAlphaComponent(0.5)

        self.addInnerShadowWithRadius(3.0, color: color, inDirection: NLInnerShadowDirection.All)
    }

    func addInnerShadowWithRadius(radius: CGFloat, andAlpha: CGFloat) {
        let c = UIColor()
        let color = c.colorWithAlphaComponent(alpha)

        self.addInnerShadowWithRadius(radius, color: color, inDirection: NLInnerShadowDirection.All)
    }

    func addInnerShadowWithRadius(radius: CGFloat, andColor: UIColor) {
        self.addInnerShadowWithRadius(radius, color: andColor, inDirection: NLInnerShadowDirection.All)
    }

    func addInnerShadowWithRadius(radius: CGFloat, color: UIColor, inDirection: NLInnerShadowDirection) {
        self.removeInnerShadow()

        let shadowView = self.createShadowViewWithRadius(radius, andColor: color, direction: inDirection)

        self.addSubview(shadowView)
    }

    func createShadowViewWithRadius(radius: CGFloat, andColor: UIColor, direction: NLInnerShadowDirection) -> UIView {
        let shadowView = UIView(frame: CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height))
        shadowView.backgroundColor = UIColor.clearColor()
        shadowView.tag = 2639

        let colorsArray: Array = [ andColor.CGColor, UIColor.clearColor().CGColor ]

        if direction.contains(.Top) {
            let xOffset: CGFloat = 0.0
            let topWidth = self.bounds.size.width

            let shadow = CAGradientLayer()
            shadow.colors = colorsArray
            shadow.startPoint = CGPointMake(0.5, 0.0)
            shadow.endPoint = CGPointMake(0.5, 1.0)
            shadow.frame = CGRectMake(xOffset, 0, topWidth, radius)
            shadowView.layer.insertSublayer(shadow, atIndex: 0)
        }

        if direction.contains(.Bottom) {
            let xOffset: CGFloat = 0.0
            let bottomWidth = self.bounds.size.width

            let shadow = CAGradientLayer()
            shadow.colors = colorsArray
            shadow.startPoint = CGPointMake(0.5, 1.0)
            shadow.endPoint = CGPointMake(0.5, 0.0)
            shadow.frame = CGRectMake(xOffset, self.bounds.size.height - radius, bottomWidth, radius)
            shadowView.layer.insertSublayer(shadow, atIndex: 0)
        }

        if direction.contains(.Left) {
            let yOffset: CGFloat = 0.0
            let leftHeight = self.bounds.size.height

            let shadow = CAGradientLayer()
            shadow.colors = colorsArray
            shadow.frame = CGRectMake(0, yOffset, radius, leftHeight)
            shadow.startPoint = CGPointMake(0.0, 0.5)
            shadow.endPoint = CGPointMake(1.0, 0.5)
            shadowView.layer.insertSublayer(shadow, atIndex: 0)
        }

        if direction.contains(.Right) {
            let yOffset: CGFloat = 0.0
            let rightHeight = self.bounds.size.height

            let shadow = CAGradientLayer()
            shadow.colors = colorsArray
            shadow.frame = CGRectMake(self.bounds.size.width - radius, yOffset, radius, rightHeight)
            shadow.startPoint = CGPointMake(1.0, 0.5)
            shadow.endPoint = CGPointMake(0.0, 0.5)
            shadowView.layer.insertSublayer(shadow, atIndex: 0)
        }

        return shadowView
    }
}