1

How can I achieve a gradient exactly same as in attached image programmatically?Screenshot

1
  • 1
    To get it exact, you probably need to ask your designer for the exact parameters they used when they created it. You need the start color, end color, and since it looks linear to me, start and end points. If it's actually radial, then you need center and radius. There could also be multiple points -- if so, you need them and their associated colors. Commented Dec 11, 2017 at 19:48

1 Answer 1

2

The answer largely depends on what the designer used to create the gradient. There are some tools such as Invision that provide helpful information to the developer, such as gradient colours and points. If they just created it in Photoshop, however, it's going to be hard to reproduce programmatically, and maybe you should just ask for an image of the gradient.

Another solution would be to use an @IBDesignable gradient view in Xcode; many developers have created them, including me, largely as a result of having to deal with this problem a lot in the past. A major upside of this approach is that it's super-easy to tweak the colours, and even to animate it if you need to.

I have an open-source example available on GitHub, plus I wrote an extended explanation of how it works.

I had a quick attempt to make a gradient like yours using my project, and this is how it looks in Xcode:-

enter image description here

The crucial part is the top of the Attributes Inspector panel, where you can easily set the start and end gradient colours, and also the gradient angle - yours looks angled around 310 degrees.

enter image description here

The main piece of code you need is pasted below. Follow the links above for a walkthrough of how it works.

import UIKit
@IBDesignable
class LDGradientView: UIView {

    // the gradient start colour
    @IBInspectable var startColor: UIColor? {
        didSet {
            updateGradient()
        }
    }

    // the gradient end colour
    @IBInspectable var endColor: UIColor? {
        didSet {
            updateGradient()
        }
    }

    // the gradient angle, in degrees anticlockwise from 0 (east/right)
    @IBInspectable var angle: CGFloat = 270 {
        didSet {
            updateGradient()
        }
    }

    // the gradient layer
    private var gradient: CAGradientLayer?

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

    override init(frame: CGRect) {
        super.init(frame: frame)
        installGradient()
    }

    // Create a gradient and install it on the layer
    private func installGradient() {
        // if there's already a gradient installed on the layer, remove it
        if let gradient = self.gradient {
            gradient.removeFromSuperlayer()
        }
        let gradient = createGradient()
        self.layer.addSublayer(gradient)
        self.gradient = gradient
    }

    // Update an existing gradient
    private func updateGradient() {
        if let gradient = self.gradient {
            let startColor = self.startColor ?? UIColor.clear
            let endColor = self.endColor ?? UIColor.clear
            gradient.colors = [startColor.cgColor, endColor.cgColor]
            let (start, end) = gradientPointsForAngle(self.angle)
            gradient.startPoint = start
            gradient.endPoint = end
        }
    }

    // create gradient layer
    private func createGradient() -> CAGradientLayer {
        let gradient = CAGradientLayer()
        gradient.frame = self.bounds
        return gradient
    }

    // create vector pointing in direction of angle
    private func gradientPointsForAngle(_ angle: CGFloat) -> (CGPoint, CGPoint) {
        // get vector start and end points
        let end = pointForAngle(angle)
        //let start = pointForAngle(angle+180.0)
        let start = oppositePoint(end)
        // convert to gradient space
        let p0 = transformToGradientSpace(start)
        let p1 = transformToGradientSpace(end)
        return (p0, p1)
    }

    // get a point corresponding to the angle
    private func pointForAngle(_ angle: CGFloat) -> CGPoint {
        // convert degrees to radians
        let radians = angle * .pi / 180.0
        var x = cos(radians)
        var y = sin(radians)
        // (x,y) is in terms unit circle. Extrapolate to unit square to get full vector length
        if (fabs(x) > fabs(y)) {
            // extrapolate x to unit length
            x = x > 0 ? 1 : -1
            y = x * tan(radians)
        } else {
            // extrapolate y to unit length
            y = y > 0 ? 1 : -1
            x = y / tan(radians)
        }
        return CGPoint(x: x, y: y)
    }

    // transform point in unit space to gradient space
    private func transformToGradientSpace(_ point: CGPoint) -> CGPoint {
        // input point is in signed unit space: (-1,-1) to (1,1)
        // convert to gradient space: (0,0) to (1,1), with flipped Y axis
        return CGPoint(x: (point.x + 1) * 0.5, y: 1.0 - (point.y + 1) * 0.5)
    }

    // return the opposite point in the signed unit square
    private func oppositePoint(_ point: CGPoint) -> CGPoint {
        return CGPoint(x: -point.x, y: -point.y)
    }

    // ensure the gradient gets initialized when the view is created in IB
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        installGradient()
        updateGradient()
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, This is exactly what I was looking for 😊
Sir, you saved my day!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.