UIView 的程序化 "fuzzy" 样式背景
Programmatic "fuzzy" style background for UIView
当然,为背景设置纯色是微不足道的:
如今,流行的做法是使用 "fuzzy" 或 "cloudy" 背景作为应用程序的设计特征,而不是使用 "plain gray"。
例如,这里有几个 "fuzzy" 背景 - 它只是一种纯色,可能有一些噪点,也可能有些模糊。
你到处都能看到类似这样的背景,考虑流行的提要应用程序(whassapp 等)。这是我们今天的"fad"。
我想到了,如果你能在 Swift
中的代码中做到这一点,那就太棒了
注意:从 PNG 开始并不是一个优雅的解决方案:
希望可以从头开始以编程方式生成所有内容。
Inspector如果有IBDesignable风格的滑块就好了,"Add faddish 'grainy' background..." - 新时代应该可以!
我们使用了很棒的组件KGNoise。它真的很容易使用。我觉得可以帮到你
KGNoise 将随机黑白像素生成静态 128x128 图像,然后平铺以填充 space。随机像素使用一个看起来最随机的值来播种,这也意味着噪声在应用程序启动之间看起来是一致的。
您可以使用 GPUImage 轻松构建一些东西。它带有大量的模糊、噪声发生器和过滤器。您可以按顺序将它们连接在一起并构建复杂的 GPU 加速效果。
给你一个好的起点。这是一个使用 GPUImage 来做你想做的事情的函数的快速脏原型。如果将 'orUseNoise' 设置为 YES,它将创建一个基于柏林噪声的模糊图像,而不是图像。调整指出的值以更改所需的效果。
- (UIImage *)blurWithGPUImage:(UIImage *)sourceImage orUseNoise:(bool) useNoise {
GPUImagePicture *stillImageSource = [[GPUImagePicture alloc] initWithImage:sourceImage];
GPUImageGaussianBlurFilter *gaussFilter = [[GPUImageGaussianBlurFilter alloc] init];
[gaussFilter setBlurRadiusInPixels:6]; //<<-------TWEAK
[gaussFilter setBlurPasses:1]; //<<-------TWEAK
if(useNoise) {
GPUImagePerlinNoiseFilter* perlinNouse = [[GPUImagePerlinNoiseFilter alloc] init];
[perlinNouse setColorStart:(GPUVector4){1.0, 1.0, 1.0f, 1.0}]; //<<-------TWEAK
[perlinNouse setColorFinish:(GPUVector4){0.5,0.5, 0.5f, 1.0}]; //<<-------TWEAK
[perlinNouse setScale:200]; //<<-------TWEAK
[stillImageSource addTarget:perlinNouse];
[perlinNouse addTarget:gaussFilter];
} else {
[stillImageSource addTarget:gaussFilter];
}
[gaussFilter useNextFrameForImageCapture];
[stillImageSource processImage];
UIImage *outputImage = [gaussFilter imageFromCurrentFramebuffer];
// Set up output context.
UIGraphicsBeginImageContext(self.view.frame.size);
CGContextRef outputContext = UIGraphicsGetCurrentContext();
// Invert image coordinates
CGContextScaleCTM(outputContext, 1.0, -1.0);
CGContextTranslateCTM(outputContext, 0, -self.view.frame.size.height);
// Draw base image.
CGContextDrawImage(outputContext, self.view.frame, outputImage.CGImage);
// Apply tint
CGContextSaveGState(outputContext);
UIColor* tint = [UIColor colorWithWhite:1.0f alpha:0.6]; //<<-------TWEAK
CGContextSetFillColorWithColor(outputContext, tint.CGColor);
CGContextFillRect(outputContext, self.view.frame);
CGContextRestoreGState(outputContext);
// Output image
outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outputImage;
}
这是一个简单的堆栈:
GPUImagePicture -> GPUImagePerlinNoiseFilter -> GPUImageGaussianBlurFilter
..需要一些处理代码才能正确制作成图像。
您可以尝试更改堆栈以使用许多其他过滤器中的一些。
注意:即使您使用噪声而不是图像。在将那部分剪掉之前,您仍然需要提供图片。
虽然问题要求 "programmatic" 解决方案,但我想到您正在尝试做的事情并称为 "fuzzy" 听起来很像 UIBlurEffect
、UIVisualEffectView
和 UIVibrancyEffect
是在 iOS 8.
中引入的
为了使用这些,您可以在 Storyboard 场景上拖动 UIVisualEffectView
以向屏幕的特定部分添加模糊或鲜艳效果。
如果你想让整个场景出现在前一个场景之上的视觉效果,你应该配置如下:
- 将 View Controller 或 presentation segue 设置为 Presentation = Over Current Context 并设置 "fuzzy"
的背景颜色
将呈现的视图控制器的背景颜色设置为clearColor
。
将呈现的视图控制器的全部内容嵌入 UIVisualEffectView
有了它,你可以得到这样的效果:
根据我很久以前写的东西,这会让你开始:
@IBInspectable
属性:
noiseColor
:noise/grain 颜色,应用于视图的 backgroundColor
noiseMinAlpha
: 随机噪声的最小 alpha
noiseMaxAlpha
: 随机噪声的最大 alpha
noisePasses
: 应用多少次噪波,次数越多速度越慢但噪波效果更好
noiseSpacing
: 随机噪音出现的频率,间距越大噪音越少
解释:
当任何可设计的噪音属性发生变化时,视图将被标记为重绘。在 draw 函数中生成了 UIImage
(或者从 NSCache
中提取,如果可用的话)。
在生成方法中,每个像素都被迭代,如果像素应该是噪声(取决于间距参数),噪声颜色将应用随机 alpha 通道。这样做的次数与通过次数一样多。
.
// 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
}
}
我同意关于 GPUImage 的回答,由于您不想提供图像,您可以像这样创建空白图像:
func createNoiseImage(size: CGSize, color: UIColor) -> UIImage {
UIGraphicsBeginImageContext(size)
let context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, color.CGColor)
CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
let filter = GPUImagePerlinNoiseFilter()
return filter.imageByFilteringImage(image)
}
使用 GPUImage 的主要优势是速度。
在 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!
}
}
当然,为背景设置纯色是微不足道的:
如今,流行的做法是使用 "fuzzy" 或 "cloudy" 背景作为应用程序的设计特征,而不是使用 "plain gray"。
例如,这里有几个 "fuzzy" 背景 - 它只是一种纯色,可能有一些噪点,也可能有些模糊。
你到处都能看到类似这样的背景,考虑流行的提要应用程序(whassapp 等)。这是我们今天的"fad"。
我想到了,如果你能在 Swift
中的代码中做到这一点,那就太棒了注意:从 PNG 开始并不是一个优雅的解决方案:
希望可以从头开始以编程方式生成所有内容。
Inspector如果有IBDesignable风格的滑块就好了,"Add faddish 'grainy' background..." - 新时代应该可以!
我们使用了很棒的组件KGNoise。它真的很容易使用。我觉得可以帮到你
KGNoise 将随机黑白像素生成静态 128x128 图像,然后平铺以填充 space。随机像素使用一个看起来最随机的值来播种,这也意味着噪声在应用程序启动之间看起来是一致的。
您可以使用 GPUImage 轻松构建一些东西。它带有大量的模糊、噪声发生器和过滤器。您可以按顺序将它们连接在一起并构建复杂的 GPU 加速效果。
给你一个好的起点。这是一个使用 GPUImage 来做你想做的事情的函数的快速脏原型。如果将 'orUseNoise' 设置为 YES,它将创建一个基于柏林噪声的模糊图像,而不是图像。调整指出的值以更改所需的效果。
- (UIImage *)blurWithGPUImage:(UIImage *)sourceImage orUseNoise:(bool) useNoise {
GPUImagePicture *stillImageSource = [[GPUImagePicture alloc] initWithImage:sourceImage];
GPUImageGaussianBlurFilter *gaussFilter = [[GPUImageGaussianBlurFilter alloc] init];
[gaussFilter setBlurRadiusInPixels:6]; //<<-------TWEAK
[gaussFilter setBlurPasses:1]; //<<-------TWEAK
if(useNoise) {
GPUImagePerlinNoiseFilter* perlinNouse = [[GPUImagePerlinNoiseFilter alloc] init];
[perlinNouse setColorStart:(GPUVector4){1.0, 1.0, 1.0f, 1.0}]; //<<-------TWEAK
[perlinNouse setColorFinish:(GPUVector4){0.5,0.5, 0.5f, 1.0}]; //<<-------TWEAK
[perlinNouse setScale:200]; //<<-------TWEAK
[stillImageSource addTarget:perlinNouse];
[perlinNouse addTarget:gaussFilter];
} else {
[stillImageSource addTarget:gaussFilter];
}
[gaussFilter useNextFrameForImageCapture];
[stillImageSource processImage];
UIImage *outputImage = [gaussFilter imageFromCurrentFramebuffer];
// Set up output context.
UIGraphicsBeginImageContext(self.view.frame.size);
CGContextRef outputContext = UIGraphicsGetCurrentContext();
// Invert image coordinates
CGContextScaleCTM(outputContext, 1.0, -1.0);
CGContextTranslateCTM(outputContext, 0, -self.view.frame.size.height);
// Draw base image.
CGContextDrawImage(outputContext, self.view.frame, outputImage.CGImage);
// Apply tint
CGContextSaveGState(outputContext);
UIColor* tint = [UIColor colorWithWhite:1.0f alpha:0.6]; //<<-------TWEAK
CGContextSetFillColorWithColor(outputContext, tint.CGColor);
CGContextFillRect(outputContext, self.view.frame);
CGContextRestoreGState(outputContext);
// Output image
outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outputImage;
}
这是一个简单的堆栈:
GPUImagePicture -> GPUImagePerlinNoiseFilter -> GPUImageGaussianBlurFilter
..需要一些处理代码才能正确制作成图像。
您可以尝试更改堆栈以使用许多其他过滤器中的一些。
注意:即使您使用噪声而不是图像。在将那部分剪掉之前,您仍然需要提供图片。
虽然问题要求 "programmatic" 解决方案,但我想到您正在尝试做的事情并称为 "fuzzy" 听起来很像 UIBlurEffect
、UIVisualEffectView
和 UIVibrancyEffect
是在 iOS 8.
为了使用这些,您可以在 Storyboard 场景上拖动 UIVisualEffectView
以向屏幕的特定部分添加模糊或鲜艳效果。
如果你想让整个场景出现在前一个场景之上的视觉效果,你应该配置如下:
- 将 View Controller 或 presentation segue 设置为 Presentation = Over Current Context 并设置 "fuzzy" 的背景颜色
将呈现的视图控制器的背景颜色设置为
clearColor
。将呈现的视图控制器的全部内容嵌入
UIVisualEffectView
有了它,你可以得到这样的效果:
根据我很久以前写的东西,这会让你开始:
@IBInspectable
属性:
noiseColor
:noise/grain 颜色,应用于视图的backgroundColor
noiseMinAlpha
: 随机噪声的最小 alphanoiseMaxAlpha
: 随机噪声的最大 alphanoisePasses
: 应用多少次噪波,次数越多速度越慢但噪波效果更好noiseSpacing
: 随机噪音出现的频率,间距越大噪音越少
解释:
当任何可设计的噪音属性发生变化时,视图将被标记为重绘。在 draw 函数中生成了 UIImage
(或者从 NSCache
中提取,如果可用的话)。
在生成方法中,每个像素都被迭代,如果像素应该是噪声(取决于间距参数),噪声颜色将应用随机 alpha 通道。这样做的次数与通过次数一样多。
.
// 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
}
}
我同意关于 GPUImage 的回答,由于您不想提供图像,您可以像这样创建空白图像:
func createNoiseImage(size: CGSize, color: UIColor) -> UIImage {
UIGraphicsBeginImageContext(size)
let context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, color.CGColor)
CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
let filter = GPUImagePerlinNoiseFilter()
return filter.imageByFilteringImage(image)
}
使用 GPUImage 的主要优势是速度。
在 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!
}
}