how to make a gradient border of UIView?

后端 未结 6 1108
遇见更好的自我
遇见更好的自我 2020-12-28 17:37

I want to make a gradient border of view like the following picture:

\"Image\"

but I don\'t know how d

6条回答
  •  旧巷少年郎
    2020-12-28 18:26

    Thanx Tiago Mendes for answer.

    I improved functionality:

    • added corner radius.

    And fixed some issues to ensure correct masking and given border width:

    • improved gradient layer frame;
    • improved mask path rounding rect.

    Swift 5

    public extension UIView {
    
        private static let kLayerNameGradientBorder = "GradientBorderLayer"
    
        func gradientBorder(width: CGFloat,
                            colors: [UIColor],
                            startPoint: CGPoint = CGPoint(x: 0.5, y: 0.0),
                            endPoint: CGPoint = CGPoint(x: 0.5, y: 1.0),
                            andRoundCornersWithRadius cornerRadius: CGFloat = 0) {
    
            let existingBorder = gradientBorderLayer()
            let border = existingBorder ?? CAGradientLayer()
            border.frame = CGRect(x: bounds.origin.x, y: bounds.origin.y,
                                  width: bounds.size.width + width, height: bounds.size.height + width)
            border.colors = colors.map { return $0.cgColor }
            border.startPoint = startPoint
            border.endPoint = endPoint
    
            let mask = CAShapeLayer()
            let maskRect = CGRect(x: bounds.origin.x + width/2, y: bounds.origin.y + width/2,
                                  width: bounds.size.width - width, height: bounds.size.height - width)
            mask.path = UIBezierPath(roundedRect: maskRect, cornerRadius: cornerRadius).cgPath
            mask.fillColor = UIColor.clear.cgColor
            mask.strokeColor = UIColor.white.cgColor
            mask.lineWidth = width
    
            border.mask = mask
    
            let exists = (existingBorder != nil)
            if !exists {
                layer.addSublayer(border)
            }
        }
        private func gradientBorderLayer() -> CAGradientLayer? {
            let borderLayers = layer.sublayers?.filter { return $0.name == UIView.kLayerNameGradientBorder }
            if borderLayers?.count ?? 0 > 1 {
                fatalError()
            }
            return borderLayers?.first as? CAGradientLayer
        }
    }
    

    And for more readable declaration of startPoint and endPoint of gradient layer I use this code:

    public extension CGPoint {
    
        enum CoordinateSide {
            case topLeft, top, topRight, right, bottomRight, bottom, bottomLeft, left
        }
    
        static func unitCoordinate(_ side: CoordinateSide) -> CGPoint {
            switch side {
            case .topLeft:      return CGPoint(x: 0.0, y: 0.0)
            case .top:          return CGPoint(x: 0.5, y: 0.0)
            case .topRight:     return CGPoint(x: 1.0, y: 0.0)
            case .right:        return CGPoint(x: 0.0, y: 0.5)
            case .bottomRight:  return CGPoint(x: 1.0, y: 1.0)
            case .bottom:       return CGPoint(x: 0.5, y: 1.0)
            case .bottomLeft:   return CGPoint(x: 0.0, y: 1.0)
            case .left:         return CGPoint(x: 1.0, y: 0.5)
            }
        }
    }
    

    So final usage is:

    view.gradientBorder(width: 3, colors: [.red, .orange], startPoint: .unitCoordinate(.top), endPoint: .unitCoordinate(.bottom), andRoundCornersWithRadius: 12)
    

提交回复
热议问题