Less Blur with `Visual Effect View with Blur`?

后端 未结 11 1288
死守一世寂寞
死守一世寂寞 2020-12-08 10:11

Title pretty much asks it all...

I\'m playing with the iOS8 Visual Effect View with Blur. It\'s over a UIImageView that shows a user choosa

相关标签:
11条回答
  • 2020-12-08 10:40

    This answer is based on Mitja Semolic's excellent earlier answer. I've converted it to swift 3, added an explanation to what's happening in coments, made it an extension of a UIViewController so any VC can call it at will, added an unblurred view to show selective application, and added a completion block so that the calling view controller can do whatever it wants at the completion of the blur.

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

    I've confirmed that it works with both iOS 10.3.1 and iOS 11

    0 讨论(0)
  • 2020-12-08 10:46

    It's a pity that Apple did not provide any options for blur effect. But this workaround worked for me - animating the blur effect and pausing it before completion.

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

    and the UIView extensions for pausing (with a delay) and resuming view's animation

    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
        }
    }
    
    0 讨论(0)
  • 2020-12-08 10:46

    Similar to @mitja13's solution, but uses UIViewPropertyAnimator, slightly more succinct:

    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
     
    }
    
    0 讨论(0)
  • 2020-12-08 10:48

    In order to use blur with level of blur - use my extension below:

    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)
          }
        }
      }
    }
    

    How to use. Run it when frame if view already done. For example in viewDidAppear:

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    
        myView.applyBlur(level: 5)
    }
    
    0 讨论(0)
  • 2020-12-08 10:55

    This works for me.

    I put UIVisualEffectView in an UIView before add to my view.

    I make this function to use easier. You can use this function to make blur any area in your view.

    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)
    }
    

    For example, you can make blur all of your view by calling:

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

    You can change Dark to your desired blur style and 0.8 to your desired alpha value

    0 讨论(0)
  • 2020-12-08 10:59

    The reason you're getting heavy blur is that the blur effect style affects the brightness level of the image, not the amount of blur applied.

    enter image description here

    Unfortunately, although Apple clearly has the ability to control the amount of blur applied programmatically--try dragging down slowly on the launchpad to watch the Spotlight blurring transition--I don't see any public API to pass a blur amount to UIBlurEffect.

    This post claims that adjusting the background color alpha will drive the blur amount. It's worth a try, but I don't see where that is documented: How to fade a UIVisualEffectView and/or UIBlurEffect in and out?

    0 讨论(0)
提交回复
热议问题