-1

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

  • A gradient gives a smooth transition between colors. You want two extra subviews each with the desired background color. – HangarRash Jun 05 '23 at 21:55
  • https://stackoverflow.com/questions/24380535/how-to-apply-gradient-to-background-view-of-ios-swift-app might give you a better idea – MadProgrammer Jun 05 '23 at 21:57

1 Answers1

2

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.

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • 1
    Clever use of a gradient. But seems so inefficient compared to just adding two view with the desired background colors. – HangarRash Jun 05 '23 at 22:27
  • "because the stops are based on percentile position, it's possible that a large enough height might cause line between the two colors to fade". You can use booth stop points at 0.5. – Leo Dabus Jun 05 '23 at 22:32
  • @HangarRash I have an improved version of my code above where I can set the direction angle – Leo Dabus Jun 05 '23 at 22:34
  • @HangarRash I don't disagree, in fact I even suggested making a compound/composite view instead. Expect (in my mind) it becomes a little more complicated as you need to constantly manage those views so they are always in the background - half a dozen of this, half a dozen of that – MadProgrammer Jun 05 '23 at 23:29
  • @LeoDabus Hmm, that curious, I'm sure it use to complain about the two stops been equal, will update – MadProgrammer Jun 05 '23 at 23:31
  • @MadProgrammer - you can omit `traitCollectionDidChange(...)` -- it doesn't change any of the properties, so it's not needed. You may also want to implement `init` so the view will display properly with its defaults: https://pastebin.com/4JZD3MTd – DonMag Jun 06 '23 at 17:42
  • @DonMag I just took the example from the previous question. I could also omit `horizontalMode` and `diagonalMode` which would better handled as an `enum` and I think the original example made use `IBDesignable` and related elements, so I think the intention was to make it usable in story boards – MadProgrammer Jun 06 '23 at 20:37