【问题标题】:Less Blur with `Visual Effect View with Blur`?使用“带有模糊的视觉效果视图”减少模糊?
【发布时间】:2015-06-12 11:31:56
【问题描述】:

标题几乎要求一切......

我正在使用 iOS8 Visual Effect View with Blur。它在UIImageView 上方,显示用户可选择的背景照片。放置在 contentView 本身的视图是我自己的自定义视图,显示了一种图形/日历。大多数所说的日历图是透明的。它应用于它背后的照片的模糊真的很重。我想让更多的细节泄露出去。但苹果似乎只给出了三个罐头值:

typedef enum {
    UIBlurEffectStyleExtraLight,
    UIBlurEffectStyleLight,
    UIBlurEffectStyleDark 
} UIBlurEffectStyle;

我一直在使用 UIVisualEffectView 的不同 alpha 和背景颜色(尽管文档警告不要这样做),但这并没有做任何事情,只会让情况变得更糟。

【问题讨论】:

    标签: ios objective-c ios8 uivisualeffectview uiblureffect


    【解决方案1】:

    很遗憾苹果没有提供任何模糊效果的选项。但这个解决方法对我有用 - 为模糊效果设置动画并在完成前暂停它。

    func blurEffectView(enable enable: Bool) {
        let enabled = self.blurView.effect != nil
        guard enable != enabled else { return }
    
        switch enable {
        case true:
            let blurEffect = UIBlurEffect(style: .ExtraLight)
            UIView.animateWithDuration(1.5) {
                self.blurView.effect = blurEffect
            }
    
            self.blurView.pauseAnimation(delay: 0.3)
        case false:
            self.blurView.resumeAnimation()
    
            UIView.animateWithDuration(0.1) {
                self.blurView.effect = nil
            }
        }
    }
    

    以及用于暂停(延迟)和恢复视图动画的 UIView 扩展

    extension UIView {
    
        public func pauseAnimation(delay delay: Double) {
            let time = delay + CFAbsoluteTimeGetCurrent()
            let timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, time, 0, 0, 0, { timer in
                let layer = self.layer
                let pausedTime = layer.convertTime(CACurrentMediaTime(), fromLayer: nil)
                layer.speed = 0.0
                layer.timeOffset = pausedTime
            })
            CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes)
        }
    
        public func resumeAnimation() {
            let pausedTime  = layer.timeOffset
    
            layer.speed = 1.0
            layer.timeOffset = 0.0
            layer.beginTime = layer.convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime
        }
    }
    

    【讨论】:

    • 对于 Apple 应该首先包含的内容,这是一个非常有创意的解决方案(尤其是因为它不使用私有 API)。
    • 这可能有效,但并不完整。多次调用 blurEffectView 会导致多次调用 pauseAnimation 方法,这会在每次调用时添加一个新计时器并且不会删除它。而且,如果当前的run loop已经包含了一个runLoopTimer,CFRunLoopAddTimer就不会再添加一个新的定时器了。
    • 似乎不再起作用了——或者至少对我来说不起作用。我只是没有得到任何模糊效果。对于所有这些解决方案,我要么完全没有模糊,要么在此处其他地方提到崩溃。
    【解决方案2】:

    您得到严重模糊的原因是模糊效果样式会影响图像的亮度级别,而不是应用的模糊量。

    不幸的是,尽管 Apple 显然有能力以编程方式控制应用的模糊量 - 尝试在启动板上缓慢向下拖动以观察 Spotlight 模糊过渡 - 我没有看到任何公共 API 可以将模糊量传递给UIBlurEffect.

    这篇文章声称调整背景颜色 alpha 将驱动模糊量。值得一试,但我看不到记录在哪里:How to fade a UIVisualEffectView and/or UIBlurEffect in and out?

    【讨论】:

    • 我只是建议您不要更改 alpha 值。正如 Apple 所说:“使用 UIVisualEffectView 类时,请避免小于 1 的 alpha 值。(...) UIVisualEffectView 对象需要组合为它们所叠加的内容的一部分,以便看起来正确。设置视觉效果视图或其任何超级视图上小于 1 的 alpha 会导致许多效果看起来不正确或根本不显示。”参考:developer.apple.com/library/ios/documentation/UIKit/Reference/…
    • 嗨 @stilesCrisis 你能帮我解决这个问题吗
    • 正是我想要了解为什么我的模糊不是模糊的原因。谢谢!
    • 只要看看这个,还有更多的模糊样式。我发现 .systemUltraThinMaterial 效果相当不错:
    • 调整 alpha 只会让您看起来像是丢了一只隐形眼镜。您将看到模糊的图像叠加在其后面仍然清晰的图像之上。我认为这不是任何人想要的。
    【解决方案3】:

    这对我有用。

    在添加到我的视图之前,我将UIVisualEffectView 放在UIView 中。

    我让这个功能更容易使用。您可以使用此功能对视图中的任何区域进行模糊处理。

    func addBlurArea(area: CGRect, style: UIBlurEffectStyle) {
        let effect = UIBlurEffect(style: style)
        let blurView = UIVisualEffectView(effect: effect)
    
        let container = UIView(frame: area)
        blurView.frame = CGRect(x: 0, y: 0, width: area.width, height: area.height)
        container.addSubview(blurView)
        container.alpha = 0.8
        self.view.insertSubview(container, atIndex: 1)
    }
    

    例如,您可以通过调用使所有视图模糊:

    addBlurArea(self.view.frame, style: UIBlurEffectStyle.Dark)
    

    您可以将Dark 更改为您想要的模糊样式,将0.8 更改为您想要的alpha 值

    【讨论】:

    • 这完全消除了模糊。
    • 它现在仍然对我有用。您可以查看 iOS 版本、XCode 版本...我当前的版本:iOS 9.2.1 (13D15) XCode 7.2 beta (7C62b)
    • 我逐字复制了你的代码,它产生了同样的结果:一个没有模糊的部分不透明视图的视图,我想这在技术上是“不那么模糊”。 iOS 9.2 (13C75) Xcode 7.2.1 (7C1002)
    • 我意识到,在我将 blurView 添加到 containerView 后,视图不再保持模糊,我还缺少什么吗?
    • 不要使用 alpha,如果苹果建议不要做某事,那值得一试。
    【解决方案4】:

    与此处的一些类似但更简单的解决方案是使用 UIViewPropertyAnimator (iOS 10+) 并将其 fractionComplete 属性设置为介于 0 和 1 之间的某个值。

        // add blur view to image view
        let imgBlur = UIVisualEffectView()
        imgView.addSubview(imgBlur)
        imgBlur.frame = imgView.bounds
    
        // create animator to control blur strength
        let imgBlurAnimator = UIViewPropertyAnimator()
        imgBlurAnimator.addAnimations {
            imgBlur.effect = UIBlurEffect(style: .dark)
        }
    
        // 50% blur
        imgBlurAnimator.fractionComplete = 0.5
    

    请注意,如果您打算根据平移手势、滚动视图、滑块等更改 fractionComplete,则需要设置 pausesOnCompletion = true (iOS 11+)。

    【讨论】:

    • 我收到此错误“*** 由于未捕获的异常 'NSInternalInconsistencyException' 而终止应用程序,原因:'释放暂停或停止的属性动画师是错误的。属性动画师必须完成动画或在它们被释放之前明确停止并完成。'"
    • 需要添加[pa stopAnimation:YES]; [pa finishAnimationAtPosition:UIViewAnimatingPositionCurrent];但它并没有改变模糊值
    • 在设置 imgBlurAnimator.stopAnimation(true) 时不适用于 iOS 13 Beta。没有模糊效果。
    【解决方案5】:

    类似于@mitja13 的解决方案,但使用UIViewPropertyAnimator,稍微简洁一些:

    var animator: UIViewPropertyAnimator!
    
    viewDidLoad() {
       super.viewDidLoad()
    
       let blurEffectView = UIVisualEffectView()
       yourViewToBeBlurred.addSubview(blurEffectView)
       blurEffectView.fillSuperview() // This my custom method that anchors to the superview using auto layout. Write your own
    
       animator = UIViewPropertyAnimator(duration: 1, curve: .linear, animations: {
           blurEffectView.effect = UIBlurEffect(style: .regular)
       })
    
       animator.fractionComplete = 0.6 // Adjust to the level of blur you want
     
    }
    

    【讨论】:

    • 我只是为了测试而测试它。漂亮干净的解决方案。谢谢。投票赞成
    【解决方案6】:

    您可以在图像上添加 UIBlurEffect。这样就可以了。

    这是一个带有模糊效果的 UIImageView 示例。记得给 UIImageView 添加一个 Image。

    使用 blurEffectView.alpha = 0.8(从 0 到 1)调整模糊量

    import UIKit
    
    class BlurEffectImageView: UIImageView {
    
    override func awakeFromNib() {
        super.awakeFromNib()
        addBlurEffect()
    }
    
    private func addBlurEffect() {
        let blurEffect = UIBlurEffect(style: .light)
        let blurEffectView = UIVisualEffectView(effect: blurEffect)
        blurEffectView.alpha = 0.8
        
        blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        
        blurEffectView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(blurEffectView)
        
        NSLayoutConstraint(item: blurEffectView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1.0, constant: 0).isActive = true
        NSLayoutConstraint(item: blurEffectView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0).isActive = true
        NSLayoutConstraint(item: blurEffectView, attribute: .height,  relatedBy: .equal, toItem: self, attribute: .height,  multiplier: 1.0, constant: 0).isActive = true
        NSLayoutConstraint(item: blurEffectView, attribute: .width,   relatedBy: .equal, toItem: self, attribute: .width,   multiplier: 1.0, constant: 0).isActive = true
      }
    
    }
    

    【讨论】:

    • 这也完全消除了模糊效果。
    • 我不确定你的意思。这个想法只是添加模糊效果,“完全删除”是什么意思?
    • @alegelos - 这意味着它背后的图像不再模糊。我发现效果与您使用标准 UIView 的效果相同,例如带有 alpha 集的黑色背景。内容可见但变暗;但是,它并没有模糊。
    【解决方案7】:

    将 BlurEffectView 添加到具有视图 alpha

    func addBlurEffectView() -> Void {
        if !UIAccessibilityIsReduceTransparencyEnabled() {
            let viewContainer = UIView()
            viewContainer.frame = self.view.bounds
            viewContainer.alpha = 0.5
    
            let blurEffect = UIBlurEffect(style: .dark)
            let blurEffectView = UIVisualEffectView(effect: blurEffect)
            blurEffectView.layer.zPosition = -0.5;
            blurEffectView.frame = self.view.bounds;
            blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    
            viewContainer.addSubview(blurEffectView)
    
            self.view.addSubview(viewContainer)
            self.view.sendSubview(toBack: viewContainer)
        }
    }
    

    【讨论】:

      【解决方案8】:

      我像这样使用 UIVisualEffectView 来获得可调节的模糊圆圈。模糊级别由控制 Alpha 的滑块控制。我也会在下面包含滑块处理程序。模糊圆圈的大小可通过捏散动作进行调整。我也会包括在内。你可以拖动模糊圈。我将把它作为练习留给读者。如果您想要一个模糊矩形,请不要圆角。要查看这个模糊圆圈设计的实际效果,请加载 MemeSoEasy 应用(免费),添加一张照片(您可以在上面放置一个模糊圆圈),然后添加一个模糊圆圈。

      UIVisualEffectView *blurVisualEffectView;
      
      UIVisualEffect *blurEffect;
      blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
      blurVisualEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
      blurVisualEffectView.frame = lastChosenBlurCircleRect;
      blurVisualEffectView.center = CGPointMake(halfScreenX, halfScreenY);
      [self.view addSubview:blurVisualEffectView];
      CGFloat blurCornerRadius = blurVisualEffectView.bounds.size.width/2;
      [[blurVisualEffectView layer]setCornerRadius:blurCornerRadius];
      [[blurVisualEffectView layer]setMasksToBounds:YES];
      [[blurVisualEffectView layer] setBorderWidth:4.0f];
      [[blurVisualEffectView layer] setBorderColor:[UIColor blueColor].CGColor];
      blurVisualEffectView.userInteractionEnabled = NO;
      blurVisualEffectView.alpha = 0.97;
      [blurArray addObject:blurVisualEffectView];
      

      滑块处理程序:

      请注意,我将模糊对象存储在一个数组中,因此我可以让用户创建任意数量的对象。滑块处理程序适用于数组中的最后一个对象。滑块的最小值和最大值分别为 0.0 和 1.0

      UISlider *slider_ = (UISlider *)sender;
      CGFloat ourSliderValue = slider_.value;
      UIVisualEffectView *currentBlurObject =
      [blurArray objectAtIndex:blurArray.count - 1];
      currentBlurObject.alpha = ourSliderValue;
      

      捏散的大小更改处理程序

      int changeInWidth = 0; // one pixel at a time
      
      if (pinchGesture.scale > 1.0) {
          changeInWidth++;
      }
      if (pinchGesture.scale < 1.0) {
          changeInWidth--;
      }
      
      UIVisualEffectView *currentBlurObject =
      [blurArray objectAtIndex:blurArray.count - 1];
      
      CGPoint oldCenter = currentBlurObject.center;
      
      currentBlurObject.frame = CGRectMake(0, 0, currentBlurObject.frame.size.width + changeInWidth, currentBlurObject.frame.size.width + changeInWidth);
      
      currentBlurObject.center = oldCenter;
      
      lastChosenBlurCircleRect = currentBlurObject.frame;
      
      CGFloat blurCornerRadius = currentBlurObject.frame.size.width/2;
      [[currentBlurObject layer]setCornerRadius:blurCornerRadius];
      

      【讨论】:

        【解决方案9】:

        为了使用模糊级别的模糊 - 使用下面的我的扩展:

        public extension UIView {
          func applyBlur(level: CGFloat) {
            let context = CIContext(options: nil)
            self.makeBlurredImage(with: level, context: context, completed: { processedImage in
              let imageView = UIImageView(image: processedImage)
              imageView.translatesAutoresizingMaskIntoConstraints = false
              self.addSubview(imageView)
              NSLayoutConstraint.activate([
                imageView.topAnchor.constraint(equalTo: self.topAnchor),
                imageView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
                imageView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
                imageView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
              ])
            })
          }
        
          private func makeBlurredImage(with level: CGFloat, context: CIContext, completed: @escaping (UIImage) -> Void) {
            // screen shot
            UIGraphicsBeginImageContextWithOptions(self.frame.size, false, 1)
            self.layer.render(in: UIGraphicsGetCurrentContext()!)
            let resultImage = UIGraphicsGetImageFromCurrentImageContext()!
            UIGraphicsEndImageContext()
        
            let beginImage = CIImage(image: resultImage)
        
            // make blur
            let blurFilter = CIFilter(name: "CIGaussianBlur")!
            blurFilter.setValue(beginImage, forKey: kCIInputImageKey)
            blurFilter.setValue(level, forKey: kCIInputRadiusKey)
        
            // extend source image na apply blur to it
            let cropFilter = CIFilter(name: "CICrop")!
            cropFilter.setValue(blurFilter.outputImage, forKey: kCIInputImageKey)
            cropFilter.setValue(CIVector(cgRect: beginImage!.extent), forKey: "inputRectangle")
        
            let output = cropFilter.outputImage
            var cgimg: CGImage?
            var extent: CGRect?
        
            let global = DispatchQueue.global(qos: .userInteractive)
        
            global.async {
              extent = output!.extent
              cgimg = context.createCGImage(output!, from: extent!)!
              let processedImage = UIImage(cgImage: cgimg!)
        
              DispatchQueue.main.async {
                completed(processedImage)
              }
            }
          }
        }
        

        如何使用。如果视图已经完成,则在框架中运行它。例如在 viewDidAppear 中:

        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
        
            myView.applyBlur(level: 5)
        }
        

        【讨论】:

        • 不错的答案。 (已投票。)不过,我看到的一个问题是原始图像的内容何时发生变化。您无法删除和替换旧的模糊视图。也许在模糊视图上使用标签,并在添加另一个视图之前使用它来查找删除任何以前的视图?或者,您可以将模糊作为关联值附加到视图(尽管这不是很 Swifty)
        【解决方案10】:

        此答案基于Mitja Semolic's excellent earlier answer。我已将其转换为 swift 3,在评论中添加了对正在发生的事情的解释,使其成为 UIViewController 的扩展,因此任何 VC 都可以随意调用它,添加了一个不模糊的视图以显示选择性应用程序,并添加了一个完成块,以便调用视图控制器可以在完成模糊时做任何事情。

            import UIKit
        //This extension implements a blur to the entire screen, puts up a HUD and then waits and dismisses the view.
            extension UIViewController {
                func blurAndShowHUD(duration: Double, message: String, completion: @escaping () -> Void) { //with completion block
                    //1. Create the blur effect & the view it will occupy
                    let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.light)
                    let blurEffectView = UIVisualEffectView()//(effect: blurEffect)
                    blurEffectView.frame = self.view.bounds
                    blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        
                //2. Add the effect view to the main view
                    self.view.addSubview(blurEffectView)
                //3. Create the hud and add it to the main view
                let hud = HudView.getHUD(view: self.view, withMessage: message)
                self.view.addSubview(hud)
                //4. Begin applying the blur effect to the effect view
                UIView.animate(withDuration: 0.01, animations: {
                    blurEffectView.effect = blurEffect
                })
                //5. Halt the blur effects application to achieve the desired blur radius
                self.view.pauseAnimationsInThisView(delay: 0.004)
                //6. Remove the view (& the HUD) after the completion of the duration
                DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
                    blurEffectView.removeFromSuperview()
                    hud.removeFromSuperview()
                    self.view.resumeAnimationsInThisView()
                    completion()
                }
            }
        }
        
        extension UIView {
            public func pauseAnimationsInThisView(delay: Double) {
                let time = delay + CFAbsoluteTimeGetCurrent()
                let timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, time, 0, 0, 0, { timer in
                    let layer = self.layer
                    let pausedTime = layer.convertTime(CACurrentMediaTime(), from: nil)
                    layer.speed = 0.0
                    layer.timeOffset = pausedTime
                })
                CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, CFRunLoopMode.commonModes)
            }
            public func resumeAnimationsInThisView() {
                let pausedTime  = layer.timeOffset
        
                layer.speed = 1.0
                layer.timeOffset = 0.0
                layer.beginTime = layer.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
            }
        }
        

        我已确认它适用于 iOS 10.3.1 和 iOS 11

        【讨论】:

          【解决方案11】:

          非常感谢mitja13,我制作了 Objective-C 版本。

          NS_ASSUME_NONNULL_BEGIN
          
          @interface UIView (Gaoding)
          
          - (void)gd_pauseAnimationsWithDelay:(double)delay;
          - (void)gd_resumeAnimations;
          
          @end
          
          NS_ASSUME_NONNULL_END
          
          @implementation UIView (Gaoding)
          
          - (void)gd_pauseAnimationsWithDelay:(double)delay {
              double time = delay + CFAbsoluteTimeGetCurrent();
              __block CALayer *layer = self.layer;
          
              CFRunLoopRef runloopRef = CFRunLoopGetCurrent();
              CFRunLoopAddTimer(runloopRef, CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, time, 0, 0, 0, ^(CFRunLoopTimerRef timer) {
                  double pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
                  layer.speed = 0;
                  layer.timeOffset = pausedTime;
                  layer = nil;
                  CFRunLoopRemoveTimer(runloopRef, timer, kCFRunLoopCommonModes);
                  CFRelease(timer);
                  timer = NULL;
              }), kCFRunLoopCommonModes);
          }
          
          - (void)gd_resumeAnimations {
              CALayer *layer = self.layer;
              double pausedTime = layer.timeOffset;
              layer.speed = 1;
              layer.timeOffset = 0.0;
              layer.beginTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
          }
          
          @end
          

          如何使用:

          /// SHOW IT
          
          UIVisualEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
          UIVisualEffectView *blurEffectView = UIVisualEffectView.new;
          
          // .... something other
          
          [UIView animateWithDuration:0.35 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
              blurEffectView.effect = effect;
          }];
          [blurEffectView gd_pauseAnimationsWithDelay:0.1]; // 0.1/0.35 = 28.57% blur of UIBlurEffectStyleLight
          
          // .... something other
          
          /// HIDE IT
          [blurEffectView gd_resumeAnimations];
          [UIView animateWithDuration:0.35 delay:0 options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState animations:^{
              blurEffectView.effect = nil;
          }];
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-03-17
            • 1970-01-01
            • 2018-08-20
            • 2017-10-05
            • 2017-03-28
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多