Search code examples
iosswiftuikitviewcontroller

Two separate background colors for top and bottom view controller


Problem:

I would like to set two different background colors for a view controller programmatically.

Code:

self.view.backgroundColor = .systemBackground

or

func setGradientBackground() {
        
        let colorTop = UIColor.black.cgColor
        let colorBottom = backgroundGray.cgColor
                    
        let gradientLayer = CAGradientLayer()
        gradientLayer.colors = [colorTop, colorBottom]
        gradientLayer.startPoint = CGPoint(x: 0, y: 0)
        gradientLayer.endPoint = CGPoint(x: 1, y: -1)
        //gradientLayer.locations = [0.0, 1.0]
        gradientLayer.frame = self.view.bounds
                
        self.view.layer.insertSublayer(gradientLayer, at:0)
    }

Do I need to use a gradient system to obtain this? The gradient system is well gradient and I would like more of a solid top and bottom color.

How can I obtain the desired output of the image below enter image description here


Solution

  • So, based on this answer How to Apply Gradient to background view of iOS Swift App

    I modified the CAGradientLayer#locations and CAGradientLayer#colors to provide a "hard" stop between the colors

    enter image description here

    import UIKit
    import PlaygroundSupport
    
    PlaygroundPage.current.needsIndefiniteExecution = true
    
    var greeting = "Hello, playground"
    
    class Gradient: UIView {
        var startColor:   UIColor = .white { didSet { updateColors() }}
        var endColor:     UIColor = .red { didSet { updateColors() }}
        
        override class var layerClass: AnyClass { CAGradientLayer.self }
        
        var gradientLayer: CAGradientLayer { layer as! CAGradientLayer }
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            commonInit()
        }
    
        required init?(coder: NSCoder) {
            super.init(coder: coder)
            commonInit()
        }
    
        private func commonInit() {
            updatePoints()
            updateLocations()
            updateColors()
        }
        
        func updatePoints() {
            gradientLayer.startPoint = diagonalMode ? .init(x: 1, y: 0) : .init(x: 0, y: 0.5)
            gradientLayer.endPoint   = diagonalMode ? .init(x: 0, y: 1) : .init(x: 1, y: 0.5)
        }
    
        func updateLocations() {
            gradientLayer.locations = [0, 0.5, 0.5, 1.0]
        }
    
        func updateColors() {
            gradientLayer.colors = [startColor.cgColor, startColor.cgColor, endColor.cgColor, endColor.cgColor]
        }
    }
    
    let view = Gradient(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
    

    I might also consider devising a composite/compund view, one which had two views to manage the background color and a third which was laid over the top of them, which acted as the "content" view. It would require a little bit more juggling though.