【问题标题】:Programmatic "fuzzy" style background for UIViewUIView 的程序化“模糊”样式背景
【发布时间】:2015-11-07 18:02:51
【问题描述】:

当然,为背景设置纯色是微不足道的:

如今,使用“模糊”或“多云”背景作为应用中的一种设计特征,而不是使用“纯灰色”。

例如,这里有几个“模糊”的背景——它只是一种普通的颜色,可能有一些噪点,也可能是模糊的。

你可以看到类似这样的背景,考虑流行的提要应用程序(whassapp 等)。这是我们这个时代的“时尚”。

我突然想到,如果你能在 Swift 的代码中做到这一点,那就太棒了

注意:从 PNG 开始并不是一个优雅的解决方案:

希望可以从头开始以编程方式生成所有内容。

如果 Inspector 有一个 IBDesignable 风格的滑块,那就太好了,“添加时尚的'粒状'背景...” - 在新时代应该可以实现!

【问题讨论】:

    标签: ios swift image-processing gpuimage dynamic-image-generation


    【解决方案1】:

    我们使用了很棒的组件KGNoise。它真的很容易使用。我觉得可以帮到你

    KGNoise 将随机黑白像素生成为静态 128x128 图像,然后平铺以填充空间。随机像素以一个被选择为看起来最随机的值作为种子,这也意味着在应用启动之间噪声看起来会保持一致。

    【讨论】:

      【解决方案2】:

      您可以使用GPUImage 轻松构建一些东西。它带有大量的模糊、噪声生成器和过滤器。您可以将它们按顺序连接在一起,并构建复杂的 GPU 加速效果。

      给你一个好的起点。这是一个使用 GPUImage 执行您想要的操作的函数的快速脏原型。如果将“orUseNoise”设置为 YES,它将基于 perlin 噪声 INSTEAD 创建模糊图像。调整指出的值以更改所需的效果。

      - (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

      ..用一些处理代码来正确地制作图像。

      您可以尝试更改堆栈以使用许多其他过滤器。

      注意:即使您使用噪点代替图像。在您剪掉该部分之前,您仍然需要提供图片。

      【讨论】:

      • @JoeBlow 我添加了一个快速代码示例。它有带和不带图像。
      • 即使您使用噪音而不是图像,应用程序也会崩溃,因为没有处理案例。在这种情况下,您将需要替换 GPUImagePicture。因此,即使您没有通过“orUseNoise”使用,也请始终提供图像。
      • 我添加了更多描述答案。降低 setBlurRadiusInPixels 以查看噪声在做什么。更高的 gaussFilter 数字将渲染得更慢。 GPUImage 是性能的安全赌注。您无需将其制成 UIImage 即可使用。查看所有 GPUImage 文档,希望这有助于作为一个良好的起点。
      • 如果将 'orUseNoise' 设置为 YES,则不需要从图像开始。您只需将任何图像放在那里,这样它就不会崩溃,但图像不会被使用,因为它会完全被噪声覆盖。我忙于工作,只是没有时间清理那个案子:)
      • 嗯,好的,您只需将任何图像放在那里,这样它就不会崩溃现在明白了..
      【解决方案3】:

      虽然问题要求提供“程序化”解决方案,但您会想到,您正在尝试做的事情并称为“模糊”听起来很像 UIBlurEffectUIVisualEffectViewUIVibrancyEffect,它们在iOS 8。

      为了使用这些,您可以在 Storyboard 场景中拖动 UIVisualEffectView 以向屏幕的特定部分添加模糊或活力效果。

      如果您想让整个场景出现在前一个场景之上的视觉效果,您应该配置以下内容:

      1. 将 View Controller 或演示文稿 segue 设置为 Presentation = Over Current Context 并使“模糊”的背景颜色

      1. 将呈现的视图控制器的背景颜色设置为clearColor

      2. 将呈现的视图控制器的全部内容嵌入UIVisualEffectView

      这样,你可以得到这样的效果:

      【讨论】:

      • 这些是在 iOS 8 中引入的,而不是 iOS 7
      • 你是对的,我已经更新了我的答案以反映这一点。
      【解决方案4】:

      根据我很久以前写的内容,这将使您入门:

      @IBInspectable 属性:

      • noiseColor:噪点/颗粒颜色,应用于视图的backgroundColor
      • noiseMinAlpha: 随机噪声的最小 alpha 值
      • noiseMaxAlpha: 随机噪声的最大 alpha 值
      • noisePasses: 加多少次噪点,越多越慢,但可以产生更好的噪点效果
      • noiseSpacing:随机噪声发生的频率有多高,间距越大,噪声就越少

      说明:

      当任何可设计的噪声属性发生变化时,视图会被标记为重绘。在绘图函数中生成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
          }
      }
      

      【讨论】:

      • 漂亮的 IBDesignable 解决方案。
      • 这种方法不是最快的。您可以分配缓冲区,然后使用该缓冲区创建位图上下文并直接填充它,而不是调用 UIRectFill。
      • 嗨 SomeGuy,我们能否将此模糊效果设置为 CALayer 而不是 UIView。我有一个CAGradientLayer,我想展示它的模糊效果。
      【解决方案5】:

      我同意关于 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 的主要优势是速度。

      【讨论】:

      • 非常正确——在某种程度上这是最完整的答案!
      【解决方案6】:

      在 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!
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-24
        • 2013-11-26
        • 2013-10-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多