web-dev-qa-db-fra.com

Moins de flou avec `Visualisation par effet visuel avec flou`?

Le titre demande à peu près tout ...

Je joue avec iOS8 Visual Effect View with Blur. C'est sur une UIImageView qui montre une photo d'arrière-plan que l'utilisateur peut choisir. La vue placée dans le contentView lui-même est ma propre vue personnalisée, montre une sorte de graphique/calendrier. La plupart des graphiques de ce calendrier sont transparents. Le flou qu'il applique à la photo derrière elle est vraiment lourd. Je voudrais laisser plus de détails à travers. Mais Apple ne semble donner que trois valeurs prédéfinies:

typedef enum {
    UIBlurEffectStyleExtraLight,
    UIBlurEffectStyleLight,
    UIBlurEffectStyleDark 
} UIBlurEffectStyle;

J'ai fouillé avec différents alphas et couleurs d'arrière-plan de la UIVisualEffectView (même si la documentation le met en garde), mais cela ne fait rien mais aggrave la situation.

21
Travis Griggs

La raison pour laquelle vous obtenez un flou important est que le style de l'effet de flou affecte le niveau luminosité de l'image, et non la quantité de flou appliquée. 

enter image description here

Malheureusement, bien qu'Apple ait clairement la capacité de contrôler la quantité de flou appliquée par programme (essayez de glisser lentement sur le tableau de bord pour observer la transition de flou de Spotlight). Je ne vois aucune API publique permettant de passer le flou à UIBlurEffect.

Cet article affirme que l'ajustement de la couleur d'arrière-plan alpha entraînera le flou. Cela vaut la peine d'essayer, mais je ne vois pas où cela est documenté: Comment faire apparaître en fondu un UIVisualEffectView et/ou UIBlurEffect?

28
StilesCrisis

Dommage qu'Apple n'ait fourni aucune option pour l'effet de flou. Mais cette solution de contournement a fonctionné pour moi: animer l’effet de flou et le suspendre avant l’achèvement.

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

et les extensions UIView pour la pause (avec un délai) et la reprise de l'animation de la vue

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

Cela fonctionne pour moi.

Je mets UIVisualEffectView dans un UIView avant d'ajouter à mon avis.

Je facilite l'utilisation de cette fonction. Vous pouvez utiliser cette fonction pour rendre flou toute zone de votre vue.

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

Par exemple, vous pouvez rendre toute la vue floue en appelant:

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

Vous pouvez remplacer Dark par le style de flou souhaité et 0.8 par la valeur alpha souhaitée. 

8
Demilitarized Zone

Oui, vous pouvez.

Voici un exemple de UIImageView avec un effet de flou. N'oubliez pas d'ajouter une image à UIImageView.

Ajustez la quantité de flou avec blurEffectView.alpha = 0,8 (de 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
  }

}
5
alegelos

Ajouter un BlurEffectView à une vue avec un alpha de la vue <1

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)
    }
}
1
Tà Truhoada

J'utilise UIVisualEffectView comme celui-ci pour obtenir des cercles de flou ajustables. Le niveau de flou est contrôlé par un curseur qui contrôle l'alpha. Je vais également inclure le gestionnaire de curseur ci-dessous. La taille du cercle flou est réglable avec une action de pincement. Je vais inclure cela aussi. Et vous pouvez faire glisser les cercles flous. Je vais laisser cela comme un exercice pour le lecteur. Si vous voulez un rectangle flou, évitez les angles. Pour voir ce motif de cercle flou en action, chargez l'application MemeSoEasy (gratuite), ajoutez une photo (sur laquelle vous pouvez placer un cercle flou), puis ajoutez un cercle flou.

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];

Gestionnaire de curseur: 

Notez que je stocke mes objets de flou dans un tableau afin de permettre aux utilisateurs d'en créer autant que nécessaire. Le gestionnaire de curseur fonctionne sur le dernier objet du tableau. Les valeurs min et max du curseur sont 0.0 et 1.0

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

Gestionnaire de changement de taille pour la propagation de pincement

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];
1
user3408691

Une solution similaire à certaines solutions ici, mais plus simple, consiste à utiliser un UIViewPropertyAnimator (iOS 10+) et à définir sa propriété fractionComplete sur une valeur comprise entre 0 et 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

Notez que si vous prévoyez de modifier fractionComplete en fonction d'un geste panoramique, d'une vue de défilement, d'un curseur, etc., vous devrez définir pausesOnCompletion = true (iOS 11+).

0
pinch

Cette réponse est basée sur l'excellente réponse précédente de Mitja Semolic . Je l'ai converti en Swift 3, ajouté une explication à ce qui se passe dans les commentaires, en fait une extension d'un contrôleur UIViewController afin que tout VC puisse l'appeler à volonté, j'ai ajouté une vue non floue pour afficher bloc d'achèvement afin que le contrôleur de vue appelant puisse faire ce qu'il veut à la fin du flou.

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

J'ai confirmé que cela fonctionne avec iOS 10.3.1 et iOS 11

0