iosswiftimage-processinggpuimagedynamic-image-generation

Programmatic "fuzzy" style background for UIView


Of course, it's trivial to set a plain color for a background:

enter image description here

These days, instead of using "plain gray", it is popular to use a "fuzzy" or "cloudy" background, as a design feature in apps.

For example, here's a couple "fuzzy" backgrounds - it's just a plain color with perhaps some noise and maybe blur on that.

You can see backgrounds something like this all over, consider popular feed apps (whassapp etc). It's a "fad" of our day.

enter image description here

enter image description here

It occurred to me, it would be fantastic if you could do this in code in Swift

Note: starting with a PNG is not an elegant solution:

Hopefully it is possible to generate everything programmatically from scratch.

It would be great if the Inspector had a slider in the IBDesignable style, "Add faddish 'grainy' background..." - Should be possible in the new era!


Solution

  • This will get you started, based on something I wrote a long time ago:

    enter image description here

    @IBInspectable properties:

    Explanation:

    When any of the designable noise properties change the view is flagged for redraw. In the draw function the UIImage is generated (or pulled from NSCache if available).

    In the generation method each pixel is iterated over and if the pixel should be noise (depending on the spacing parameter), the noise color is applied with a randomized alpha channel. This is done as many times as the number of passes.

    .

    // NoiseView.swift
    import UIKit
    
    let noiseImageCache = NSCache()
    
    @IBDesignable class NoiseView: UIView {
    
        let noiseImageSize = CGSizeMake(128, 128)
    
        @IBInspectable var noiseColor: UIColor = UIColor.blackColor() {
            didSet { setNeedsDisplay() }
        }
        @IBInspectable var noiseMinAlpha: CGFloat = 0 {
            didSet { setNeedsDisplay() }
        }
        @IBInspectable var noiseMaxAlpha: CGFloat = 1 {
            didSet { setNeedsDisplay() }
        }
        @IBInspectable var noisePasses: Int = 1 {
            didSet {
                noisePasses = max(0, noisePasses)
                setNeedsDisplay()
            }
        }
        @IBInspectable var noiseSpacing: Int = 1 {
            didSet {
                noiseSpacing = max(1, noiseSpacing)
                setNeedsDisplay()
            }
        }
    
        override func drawRect(rect: CGRect) {
            super.drawRect(rect)
    
            UIColor(patternImage: currentUIImage()).set()
            UIRectFillUsingBlendMode(bounds, .Normal)
        }
    
        private func currentUIImage() -> UIImage {
    
            //  Key based on all parameters
            let cacheKey = "\(noiseImageSize),\(noiseColor),\(noiseMinAlpha),\(noiseMaxAlpha),\(noisePasses)"
    
            var image = noiseImageCache.objectForKey(cacheKey) as! UIImage!
    
            if image == nil {
                image = generatedUIImage()
    
                #if !TARGET_INTERFACE_BUILDER
                    noiseImageCache.setObject(image, forKey: cacheKey)
                #endif
            }
    
            return image
        }
    
        private func generatedUIImage() -> UIImage {
    
            UIGraphicsBeginImageContextWithOptions(noiseImageSize, false, 0)
    
            let accuracy: CGFloat = 1000.0
    
            for _ in 0..<noisePasses {
                for y in 0..<Int(noiseImageSize.height) {
                    for x in 0..<Int(noiseImageSize.width) {
                        if random() % noiseSpacing == 0 {
                            let alpha = (CGFloat(random() % Int((noiseMaxAlpha - noiseMinAlpha) * accuracy)) / accuracy) + noiseMinAlpha
                            noiseColor.colorWithAlphaComponent(alpha).set()
                            UIRectFill(CGRectMake(CGFloat(x), CGFloat(y), 1, 1))
                        }
                    }
                }
            }
    
            let image = UIGraphicsGetImageFromCurrentImageContext() as UIImage
    
            UIGraphicsEndImageContext()
    
            return image
        }
    }