如何正确生成随机渐变背景?

How to properly generate a random gradient background?

我一直在尝试创建一个函数,每次出现新的视图控制器时都会生成随机渐变背景,如下面提供的代码所示,但我无法让它正常工作。问题是 "random" 颜色始终相同并且按相同顺序生成。

import Foundation

import UIKit

extension UIView {

    func setGradientBackground() {

        let redValue = CGFloat(drand48())
        let greenValue = CGFloat(drand48())
        let blueValue = CGFloat(drand48())

        let redValue2 = CGFloat(drand48())
        let greenValue2 = CGFloat(drand48())
        let blueValue2 = CGFloat(drand48())

        let random = UIColor(red: redValue, green: greenValue, blue: blueValue, alpha: 1).cgColor
        let random2 = UIColor(red: redValue2, green: greenValue2, blue: blueValue2, alpha: 1).cgColor

        let gradient = CAGradientLayer()
        gradient.frame = bounds
        gradient.colors = [random, random2]

        return layer.insertSublayer(gradient, at: 0)
    }
}

如果您使用 drand48,则需要为其设置种子(例如使用 srand48)。快速而肮脏的解决方案是使用从某个参考日期或类似日期以来经过的秒数的时间派生的一些值作为种子。

更好的是,我建议改用 CGFloat.random(in: 0...1),它不需要任何播种。

extension UIView {
    func addGradientBackground() {
        let random = UIColor.random().cgColor
        let random2 = UIColor.random().cgColor

        let gradient = CAGradientLayer()
        gradient.frame = bounds
        gradient.colors = [random, random2]

        layer.insertSublayer(gradient, at: 0)
    }
}

extension UIColor {
    static func random() -> UIColor {
        let red = CGFloat.random(in: 0...1)
        let green = CGFloat.random(in: 0...1)
        let blue = CGFloat.random(in: 0...1)
        return UIColor(red: red, green: green, blue: blue, alpha: 1)
    }
}

对于它的价值,我们应该认识到,如果视图改变大小(例如,您旋转设备等),这种添加子层的技术会出现问题。您必须手动连接到 viewDidLayoutSubviewslayoutSubviews 并调整此渐变层 frame。相反,我可能建议实际上有一个 UIView subclass 来执行此渐变层。

@IBDesignable
class RandomGradientView: UIView {
    override static var layerClass: AnyClass { return CAGradientLayer.self }
    var gradientLayer: CAGradientLayer { return layer as! CAGradientLayer }

    override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        configure()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        gradientLayer.colors = [UIColor.blue.cgColor, UIColor.red.cgColor]
    }
}

private extension RandomGradientView {
    func configure() {
        let random = UIColor.random().cgColor
        let random2 = UIColor.random().cgColor
        gradientLayer.colors = [random, random2]
    }
}

因为这个 UIView subclass 实际上指定了图层 class 用于主背景图层,并且因为该主图层总是随着视图大小的改变而自动调整大小,然后它将优雅地处理任何尺寸变化(包括动画尺寸)。

综上所述,我很欣赏扩展的便利(您可以将其应用于现有的 UIView 子 classes),但您必须用更麻烦的方式来抵消它处理帧大小变化的过程。这是你的选择,但我想提供替代方案。