Boanta Ionut
Boanta Ionut

Reputation: 402

Swift 3 put 2 colors in the same UIView

I want to achieve this effect on UIViews and UIImageViews:

UIImageView

UIView

On UIView I know I can put 2 inside of it with different colors, but I pretty much think there must be a better way and I don't know how to do it in the UIImageVIew. Some sort of pod would be really useful because I couldn't find one.

Upvotes: 3

Views: 6612

Answers (4)

Gopal Krishna Reddy
Gopal Krishna Reddy

Reputation: 21

Improvement to above answer with direction

extension UIView {

enum GradientDirection {
    case horizontal
    case vertical
}

func fillColors(_ colors: [UIColor], withPercentage percentages: [Double], direction: GradientDirection = .horizontal) {
    let gradientLayer = CAGradientLayer()
    gradientLayer.frame = bounds
    var colorsArray: [CGColor] = []
    var locationsArray: [NSNumber] = []
    var total = 0.0
    locationsArray.append(0.0)
    for (index, color) in colors.enumerated() {
        // append same color twice
        colorsArray.append(color.cgColor)
        colorsArray.append(color.cgColor)
        // Calculating locations w.r.t Percentage of each
        if index + 1 < percentages.count {
            total += percentages[index]
            let location = NSNumber(value: total / 100)
            locationsArray.append(location)
            locationsArray.append(location)
        }
    }
    locationsArray.append(1.0)
    if direction == .horizontal {
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
        gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0)
    } else {
        gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
        gradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0)
    }
    gradientLayer.colors = colorsArray
    gradientLayer.locations = locationsArray
    backgroundColor = .clear
    layer.addSublayer(gradientLayer)
}
    }

Upvotes: 2

ajay_nasa
ajay_nasa

Reputation: 2298

It can be generalized more to accept any number of colors, with their respective percentage

extension UIView {
    func addColors(colors: [UIColor], withPercentage percentages: [Double]) {
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = self.bounds
        var colorsArray: [CGColor] = []
        var locationsArray: [NSNumber] = []
        var total = 0.0
        locationsArray.append(0.0)
        for (index, color) in colors.enumerated() {
            // append same color twice
            colorsArray.append(color.cgColor)
            colorsArray.append(color.cgColor)
            // Calculating locations w.r.t Percentage of each
            if index+1 < percentages.count{
                total += percentages[index]
                let location: NSNumber = NSNumber(value: total/100)
                locationsArray.append(location)
                locationsArray.append(location)
            }
        }
        locationsArray.append(1.0)
        gradientLayer.colors = colorsArray
        gradientLayer.locations = locationsArray
        self.backgroundColor = .clear
        self.layer.addSublayer(gradientLayer)
    }
}

Usage:

let colors: [UIColor] = [.red, .green, .blue, .yellow, .purple]
let percentages: [Double] = [10, 30, 20, 5, 35]
testView.addColors(colors: colors, withPercentage: percentages)

Result:

enter image description here

Thanks to @Au Ris's answer

Upvotes: 1

black_pearl
black_pearl

Reputation: 2699

a more static and objective way in Swift 4,

class ColouredView: UIView {
    override class var layerClass : AnyClass {
        return ColouredLayer.self
    }
}


class ColouredLayer: CAGradientLayer{

    override init() {
        super.init()

        let colors = [UIColor.red, UIColor.blue]
        addColors(colors)
    }

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


}


extension CAGradientLayer{

    func addColors(_ colors: [UIColor]){
        var colorsArray: [CGColor] = []
        var locationsArray: [NSNumber] = []
        for (index, color) in colors.enumerated() {
            // append same color twice
            colorsArray.append(color.cgColor)
            colorsArray.append(color.cgColor)
            locationsArray.append(NSNumber(value: (1.0 / Double(colors.count)) * Double(index)))
            locationsArray.append(NSNumber(value: (1.0 / Double(colors.count)) * Double(index + 1)))
        }

        self.colors = colorsArray
        locations = locationsArray
    }
}

just set and assign the ColouredView

Thanks to @Au Ris's answer

Upvotes: 2

Au Ris
Au Ris

Reputation: 4669

You could add a gradient layer where instead of making a transition from one color to another you would go from a color to the same color until the middle point, and the same with the second half. Check the example:

let twoColorView = UIView(frame: CGRect(x: 40, y: 100, width: 200, height: 100))
let gradientLayer = CAGradientLayer()
gradientLayer.frame = twoColorView.bounds
gradientLayer.colors = [UIColor.red.cgColor, UIColor.red.cgColor, UIColor.blue.cgColor, UIColor.blue.cgColor]
gradientLayer.locations = [NSNumber(value: 0.0), NSNumber(value: 0.5), NSNumber(value: 0.5), NSNumber(value: 1.0)]
twoColorView.layer.addSublayer(gradientLayer)

and of course you can style that view further, such as:

twoColorView.layer.cornerRadius = twoColorView.bounds.height / 2
twoColorView.layer.masksToBounds = true

It results in this:

enter image description here

EDIT:

It can be generalized to accept any number of colors. Create a UIView extension and add your logic there. In this way the colors can be applied to any UIView and its subclasses, such as UILabel, UIButton, UIImageView, etc.

extension UIView {
    func addColors(colors: [UIColor]) {
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = self.bounds

        var colorsArray: [CGColor] = []
        var locationsArray: [NSNumber] = []
        for (index, color) in colors.enumerated() {
            // append same color twice
            colorsArray.append(color.cgColor)
            colorsArray.append(color.cgColor)
            locationsArray.append(NSNumber(value: (1.0 / Double(colors.count)) * Double(index)))
            locationsArray.append(NSNumber(value: (1.0 / Double(colors.count)) * Double(index + 1)))
        }

        gradientLayer.colors = colorsArray
        gradientLayer.locations = locationsArray

        self.backgroundColor = .clear
        self.layer.addSublayer(gradientLayer)

        // This can be done outside of this funciton
        self.layer.cornerRadius = self.bounds.height / 2
        self.layer.masksToBounds = true
    }
}

And adding colors:

    let colorView = UIImageView(frame: CGRect(x: 40, y: 100, width: 200, height: 100))
    colorView.addColors(colors: [.red, .green, .blue])
    view.addSubview(colorView)

This is the result:

enter image description here

Be careful not to call this function multiple times in the lifecycle of the view, because it will add sublayers on top of each other. So either call it once or remove the sublayers before you call addColors again. So of course there is room for improvement.

Upvotes: 11

Related Questions