Skip to content

Instantly share code, notes, and snippets.

@turk-jk
Last active February 7, 2020 06:06
Show Gist options
  • Select an option

  • Save turk-jk/4ecf367296cc190dd795bd8cd340ef18 to your computer and use it in GitHub Desktop.

Select an option

Save turk-jk/4ecf367296cc190dd795bd8cd340ef18 to your computer and use it in GitHub Desktop.
View that creates a border with gradient colours.
//
// YKGradientBorder.swift
// YKGradientBorder
//
// Created by Yacob Kazal on 5/2/20.
// Copyright © 2020 Litt Global. All rights reserved.
//
import UIKit
open class YKGradientBorder: UIView {
// private variables
private var DefaultGradientBorderColors: [UIColor] = [
#colorLiteral(red: 0, green: 0.337254902, blue: 0.4941176471, alpha: 1),
#colorLiteral(red: 0.2208813892, green: 0.6252568753, blue: 0.683234582, alpha: 1),
#colorLiteral(red: 0, green: 0.337254902, blue: 0.4941176471, alpha: 1)// Repeat the first color to make a smooth transition
]
private var DefaultGradientBorderWidth: CGFloat = 4
public var gradientBorderWidth: CGFloat {
get{return DefaultGradientBorderWidth}
}
// change width of the border
public func setWidth(newWidth:CGFloat) {
setupGradientLayer( borderWidth: newWidth)
}
/// Change the colors
public func setColors(newColors: [UIColor]) {
setupGradientLayer(borderColors: newColors)
}
// Set the UIView's layer class to be our CAGradientLayer
override open class var layerClass : AnyClass {
return CAGradientLayer.self
}
// Initialiser
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupGradientLayer()
}
// to make sure the layer is fitted to the new layout
open override func layoutSubviews() {
super.layoutSubviews()
setupGradientLayer()
}
// Custom initialiser
public init(frame: CGRect, borderColors gradientBorderColors: [UIColor]? = nil, borderWidth gradientBorderWidth: CGFloat? = nil) {
super.init(frame: frame)
setupGradientLayer(borderColors: gradientBorderColors, borderWidth: gradientBorderWidth)
}
// Setup or change the border attribute
private func setupGradientLayer(borderColors gradientBorderColors: [UIColor]? = nil, borderWidth gradientBorderWidth: CGFloat? = nil) {
let width = gradientBorderWidth ?? DefaultGradientBorderWidth
let colors = gradientBorderColors ?? DefaultGradientBorderColors
DefaultGradientBorderWidth = width
DefaultGradientBorderColors = colors
// Grab this UIView's layer and cast it as CAGradientLayer
let _layer: CAGradientLayer = self.layer as! CAGradientLayer
// we need to scale it to match the display of the device.
_layer.contentsScale = UIScreen.main.scale
// Set the gradient colors
_layer.colors = colors.map{$0.cgColor}
if #available(iOS 12.0, *) {
_layer.type = .conic
} else {
// Fallback on earlier versions
}
_layer.startPoint = CGPoint(x: 0.5, y: 0.5)
_layer.endPoint = CGPoint(x: 0.5, y: 0)
let rect = CGRect(
x: width/4,
y: width/4,
width: _layer.bounds.width - width,
height: _layer.bounds.height - width )
let shapeLayer = CAShapeLayer()
shapeLayer.frame = rect
shapeLayer.lineWidth = width
shapeLayer.fillColor = nil
shapeLayer.strokeColor = UIColor.white.cgColor
let arcCenter = shapeLayer.position
let radius = shapeLayer.bounds.size.width / 2.0
let startAngle = CGFloat(0.0)
let endAngle = CGFloat(2.0 * .pi)
let clockwise = true
let circlePath = UIBezierPath(arcCenter: arcCenter,
radius: radius,
startAngle: startAngle,
endAngle: endAngle,
clockwise: clockwise)
shapeLayer.path = circlePath.cgPath
_layer.mask = shapeLayer
}
}
@turk-jk
Copy link
Author

turk-jk commented Feb 5, 2020

Example

override func layoutSubviews() {
        super.layoutSubviews()
        

        // targetedImage is the imageView that you want give it a border
        // 
        let borderMargin: CGFloat = profileBorder.borderWidth
        profileBorder.translatesAutoresizingMaskIntoConstraints = false
        let rightConstraint = NSLayoutConstraint(item: profileBorder, attribute: .right, relatedBy: .equal, toItem: targetedImage, attribute: .right, multiplier: 1, constant: borderMargin)
        let leftConstraint = NSLayoutConstraint(item: profileBorder, attribute: .left, relatedBy: .equal, toItem: targetedImage, attribute: .left, multiplier: 1, constant: -borderMargin)
        let topConstraint = NSLayoutConstraint(item: profileBorder, attribute: .top, relatedBy: .equal, toItem: targetedImage, attribute: .top, multiplier: 1, constant: -borderMargin)
        let bottomConstraint = NSLayoutConstraint(item: profileBorder, attribute: .bottom, relatedBy: .equal, toItem: targetedImage, attribute: .bottom, multiplier: 1, constant: borderMargin)
        
        rightConstraint.isActive = true
        leftConstraint.isActive = true
        bottomConstraint.isActive = true
        topConstraint.isActive = true
        
        
        addConstraints([ rightConstraint, leftConstraint, bottomConstraint, topConstraint])
}

lazy var profileBorder : YKGradientBorder = {
 let v = YKGradientBorder(frame: .zero)
 return v
}()

Public methods

You can specify the colours and the border width in the Initialiser. As well as, afterwards.
like so

profileBorder.setWidth(newWidth: 6)
profileBorder.setColors(newColors: DefaultGradientBorderColors)

for best performance is to set the border width and the colours in Initialiser
like so

let DefaultGradientBorderColors: [UIColor] = [
          .red,
          .blue,
          .red // Repeat the first colour to make a smooth transition
         ]
let profileBorder = YKGradientBorder(frame: .zero, borderColors: DefaultGradientBorderColors, borderWidth: 6)

Colours Array

The array needs to end with the same colour of the beginning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment