Of course, it's trivial to set a plain color for a background:
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.
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!
in Swift 3
import UIKit
let noiseImageCache = NSCache<AnyObject, AnyObject>()
@IBDesignable class NoiseView: UIView {
let noiseImageSize = CGSize(width: 128.0, height: 128.0)
@IBInspectable var noiseColor: UIColor = UIColor.black {
didSet { setNeedsDisplay() }
}
@IBInspectable var noiseMinAlpha: CGFloat = 0 {
didSet { setNeedsDisplay() }
}
@IBInspectable var noiseMaxAlpha: CGFloat = 0.5 {
didSet { setNeedsDisplay() }
}
@IBInspectable var noisePasses: Int = 3 {
didSet {
noisePasses = max(0, noisePasses)
setNeedsDisplay()
}
}
@IBInspectable var noiseSpacing: Int = 1 {
didSet {
noiseSpacing = max(1, noiseSpacing)
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
super.draw(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.object(forKey: cacheKey as AnyObject) as? UIImage
if image == nil {
image = generatedUIImage()
#if !TARGET_INTERFACE_BUILDER
noiseImageCache.setObject(image!, forKey: cacheKey as AnyObject)
#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 Int(arc4random()) % noiseSpacing == 0 {
let alpha = (CGFloat(arc4random() % UInt32((noiseMaxAlpha - noiseMinAlpha) * accuracy)) / accuracy) + noiseMinAlpha
noiseColor.withAlphaComponent(alpha).set()
UIRectFill(CGRect(x: x, y: y, width: 1, height: 1))
}
}
}
}
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
This will get you started, based on something I wrote a long time ago:
@IBInspectable
properties:
noiseColor
: the noise/grain color, this is applied over the view's backgroundColor
noiseMinAlpha
: the minimum alpha the randomized noise can benoiseMaxAlpha
: the maximum alpha the randomized noise can benoisePasses
: how many times to apply the noise, more passes will be slower but can result in a better noise effectnoiseSpacing
: how common the randomized noise occurs, higher spacing means the noise will be less frequentExplanation:
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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With