创建一个模糊的覆盖层视图

399
在新的iOS音乐应用中,我们可以看到一个模糊了的视图后面是一个专辑封面。如何实现这样的效果呢?我已经阅读了文档,但没有找到相关内容。
25个回答

5

采纳的答案是正确的,但这里缺少一个重要的步骤,在这种情况下,你想要模糊背景的视图是使用以下代码呈现的:

[self presentViewController:vc animated:YES completion:nil]

默认情况下,这会取消模糊效果,因为UIKit会移除正在模糊的 presenter 视图。为了避免被移除,请在上一行代码之前添加以下行:

vc.modalPresentationStyle = UIModalPresentationOverFullScreen;

或使用其他 Over 样式。


3

目标-C

UIVisualEffect *blurEffect;
blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
UIVisualEffectView *visualEffectView;
visualEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
visualEffectView.frame = self.accessImageView.bounds;
[self.accessImageView addSubview:visualEffectView];

SWIFT 3.0

let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.dark)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = view.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(blurEffectView)

来自:https://dev59.com/QWAf5IYBdhLWcg3w_W9j#24083728


2

使用UIImageEffects

如果你想要更多的控制权,可以使用苹果公司提供的UIImageEffects示例代码。

你可以从苹果开发者文库中复制UIImageEffects的代码:模糊和着色图像

下面是应用它的方法:

#import "UIImageEffects.h"
...

self.originalImageView.image = [UIImageEffects imageByApplyingLightEffectToImage:[UIImage imageNamed:@"yourImage.png"]];

我该如何在Swift中使用这个? - devjme

2
func blurBackgroundUsingImage(image: UIImage)
{
    var frame                   = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height)
    var imageView               = UIImageView(frame: frame)
    imageView.image             = image
    imageView.contentMode       = .ScaleAspectFill
    var blurEffect              = UIBlurEffect(style: .Light)
    var blurEffectView          = UIVisualEffectView(effect: blurEffect)
    blurEffectView.frame        = frame
    var transparentWhiteView    = UIView(frame: frame)
    transparentWhiteView.backgroundColor = UIColor(white: 1.0, alpha: 0.30)
    var viewsArray              = [imageView, blurEffectView, transparentWhiteView]

    for index in 0..<viewsArray.count {
        if let oldView = self.view.viewWithTag(index + 1) {
            var oldView         = self.view.viewWithTag(index + 1)
            // Must explicitly unwrap oldView to access its removeFromSuperview() method as of Xcode 6 Beta 5
            oldView!.removeFromSuperview()
        }
        var viewToInsert        = viewsArray[index]
        self.view.insertSubview(viewToInsert, atIndex: index + 1)
        viewToInsert.tag        = index + 1
    }
}

1

@Joey的回答的一个重要补充

这适用于您想要呈现带有UINavigationController的模糊背景UIViewController的情况。

// suppose you've done blur effect with your presented view controller
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController];

// this is very important, if you don't do this, the blur effect will darken after view did appeared
// the reason is that you actually present navigation controller, not presented controller
// please note it's "OverFullScreen", not "OverCurrentContext"
nav.modalPresentationStyle = UIModalPresentationOverFullScreen;

UIViewController *presentedViewController = [[UIViewController alloc] init]; 
// the presented view controller's modalPresentationStyle is "OverCurrentContext"
presentedViewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;

[presentingViewController presentViewController:nav animated:YES completion:nil];

享受!


1

我偶然发现了这个,它给我带来了非常好的结果(与苹果公司的几乎相似),并使用了加速框架。-- http://pastebin.com/6cs6hsyQ *不是我写的


8
这实际上是苹果在2013年WWDC发布的代码,但版权声明有误。 - Shmidt
WWDC的代码不是受版权保护的吗?只有付费订阅会员才能访问吧? - SAFAD
1
可能,但我在Google上找到了上面的代码。我没有更改版权,并且我认为(仍然认为)它具有正确的版权声明。如果苹果不同意,他们应该努力将其撤下。我看不到相关性。 - Jake

1
这个答案基于Mitja Semolic早期的优秀答案。我将其转换为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上运行


1

将Kev的答案转换为Swift 3版本以返回模糊图像 -

func blurBgImage(image: UIImage) -> UIImage? {
        let radius: CGFloat = 20;
        let context = CIContext(options: nil);
        let inputImage = CIImage(cgImage: image.cgImage!);
        let filter = CIFilter(name: "CIGaussianBlur");
        filter?.setValue(inputImage, forKey: kCIInputImageKey);
        filter?.setValue("\(radius)", forKey:kCIInputRadiusKey);

        if let result = filter?.value(forKey: kCIOutputImageKey) as? CIImage{

            let rect = CGRect(origin: CGPoint(x: radius * 2,y :radius * 2), size: CGSize(width: image.size.width - radius * 4, height: image.size.height - radius * 4))

            if let cgImage = context.createCGImage(result, from: rect){
                return UIImage(cgImage: cgImage);
                }
        }
        return nil;
    }

1

2019年代码

以下是使用了惊人的@AdamBardon技术的更完整的示例。

@IBDesignable class ButtonOrSomethingWithBlur: UIButton {

    var ba: UIViewPropertyAnimator?
    private lazy var blurry: BlurryBall = { return BlurryBall() }()

    override func didMoveToSuperview() {
        super.didMoveToSuperview()
        
        // Setup the blurry ball.  BE SURE TO TEARDOWN.
        // Use superb trick to access the internal guassian level of Apple's
        // standard gpu blurrer per stackoverflow.com/a/55378168/294884
        
        superview?.insertSubview(blurry, belowSubview: self)
        ba = UIViewPropertyAnimator(duration:1, curve:.linear) {[weak self] in
            // note, those duration/curve values are simply unusued
            self?.blurry.effect = UIBlurEffect(style: .extraLight)
        }
        ba?.fractionComplete = live.largeplaybutton_blurfactor
    }
    
    override func willMove(toSuperview newSuperview: UIView?) {
        
        // Teardown for the blurry ball - critical
        
        if newSuperview == nil { print("safe teardown")
            ba?.stopAnimation(true)
            ba?.finishAnimation(at: .current)
        }
    }

    override func layoutSubviews() { super.layoutSubviews()
        blurry.frame = bounds, your drawing frame or whatever
    }

作为一名iOS工程师,didMoveToWindow可能比didMoveToSuperview更适合您。其次,您可以使用其他方法进行拆卸,但是那里显示的两行代码是拆卸的代码。BlurryBall只是一个UIVisualEffectView。请注意视觉效果视图的初始化。如果您需要圆角或其他内容,请在此类中执行。
class BlurryBall: UIVisualEffectView {
    
    override init(effect: UIVisualEffect?) { super.init(effect: effect)
        commonInit() }
    
    required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder)
        commonInit() }
    
    private func commonInit() {
        clipsToBounds = true
        backgroundColor = .clear
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        layer.cornerRadius = bounds.width / 2
    }
}

0

这是Swift 2.0代码,用于解决已在接受的答案中提供的问题:

    //only apply the blur if the user hasn't disabled transparency effects
    if !UIAccessibilityIsReduceTransparencyEnabled() {
        self.view.backgroundColor = UIColor.clearColor()

        let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.Dark)
        let blurEffectView = UIVisualEffectView(effect: blurEffect)
        //always fill the view
        blurEffectView.frame = self.view.bounds
        blurEffectView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]

        self.view.addSubview(blurEffectView) //if you have more UIViews, use an insertSubview API to place it where needed
    } else {
        self.view.backgroundColor = UIColor.blackColor()
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接