Forum Archive

UI gaussian blur

Webmaster4o

One of the major UI elements since iOS 7 is the large-radius gaussian blur behind a view. It's everywhere since iOS 7 /OS X 10.10. In ui, is there a way to make a view that blurs everything behind it in this way?

Some examples from google images:

omz

Below is a little BlurView class I've written to wrap UIVisualEffectView in an easy-to-use ui.View subclass. For the style argument, you can pass 1 (extra light) 2 (light) or 3 (dark). You can also use this class in the UI editor. Simply add a custom view and set its class to BlurView. To set the style from the UI editor, you can put something like {'style': 2} in the "custom attributes" field.

import ui
from objc_util import *

class BlurView (ui.View):
    def __init__(self, style=1, *args, **kwargs):
        ui.View.__init__(self, **kwargs)
        self._style = style
        self.effect_view = None
        self.setup_effect_view()

    @on_main_thread
    def setup_effect_view(self):
        if self.effect_view is not None:
            self.effect_view.removeFromSuperview()
        UIVisualEffectView = ObjCClass('UIVisualEffectView')
        UIBlurEffect = ObjCClass('UIBlurEffect')
        frame = (self.bounds[:2], self.bounds[2:])
        self.effect_view = UIVisualEffectView.alloc().initWithFrame_(frame).autorelease()
        effect = UIBlurEffect.effectWithStyle_(self._style)
        self.effect_view.effect = effect
        self.effect_view.setAutoresizingMask_(18)
        ObjCInstance(self).addSubview_(self.effect_view)

    @property
    def style(self):
        return self._style

    @style.setter
    def style(self, value):
        if value != self._style:
            self._style = value
            self.setup_effect_view()
Webmaster4o

This is awesome! Thanks!

Phuket2

Ok, I didn't want to be the stupid one to ask this question, but I will. What's the usage of the class. Sorry, I don't get it from the text.
Do you use create a custom class inheriting from BlurView, or do you add Blur view as a subview to a class?
Sorry, its probably a silly question,

omz

@Phuket2 Basically, you just create a BlurView instance, and add it on top of the background you want to apply the blur effect on.

Phuket2

@omz , ok thanks.
Should it work if you already have a custom class that inherits from ui.View, then you change it to inherit from BlurView instead

omz

@Phuket2 Should be possible, but you must make sure to call BlurView.__init__ in your custom subclass.

blmacbeth

@omz I have been playing around with UIBlurView and UIVibrancyEffect, but have not been able to get the UIVibrancyEffect to work (text does not show up). Any suggestions on how to get it to work? I'm away from my iPad right now and can post some code later, if needed. But, my code is based on the following Objective-C code:

// Blur effect
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
[blurEffectView setFrame:self.view.bounds];
[self.view addSubview:blurEffectView];

// Vibrancy effect
UIVibrancyEffect *vibrancyEffect = [UIVibrancyEffect effectForBlurEffect:blurEffect];
UIVisualEffectView *vibrancyEffectView = [[UIVisualEffectView alloc] initWithEffect:vibrancyEffect];
[vibrancyEffectView setFrame:self.view.bounds];

// Label for vibrant text
UILabel *vibrantLabel = [[UILabel alloc] init];
[vibrantLabel setText:@"Vibrant"];
[vibrantLabel setFont:[UIFont systemFontOfSize:72.0f]];
[vibrantLabel sizeToFit];
[vibrantLabel setCenter: self.view.center];

// Add label to the vibrancy view
[[vibrancyEffectView contentView] addSubview:vibrantLabel];

// Add the vibrancy view to the blur view
[[blurEffectView contentView] addSubview:vibrancyEffectView];

Thanks,
B

omz

@blmacbeth Here's an extended version of BlurView that includes support for vibrancy effects. The main function shows a little demo.

# coding: utf-8

import ui
from objc_util import *

class BlurView (ui.View):
    def __init__(self, style=1, *args, **kwargs):
        ui.View.__init__(self, **kwargs)
        self._style = style
        self.effect_view = None
        self.setup_effect_view()

    @on_main_thread
    def setup_effect_view(self):
        if self.effect_view is not None:
            self.effect_view.removeFromSuperview()
        UIVisualEffectView = ObjCClass('UIVisualEffectView')
        UIVibrancyEffect = ObjCClass('UIVibrancyEffect')
        UIBlurEffect = ObjCClass('UIBlurEffect')
        UILabel = ObjCClass('UILabel')
        frame = (self.bounds[:2], self.bounds[2:])
        self.effect_view = UIVisualEffectView.alloc().initWithFrame_(frame).autorelease()
        effect = UIBlurEffect.effectWithStyle_(self._style)
        self.effect_view.effect = effect
        self.effect_view.setAutoresizingMask_(18)
        ObjCInstance(self).addSubview_(self.effect_view)
        vibrancy_effect = UIVibrancyEffect.effectForBlurEffect_(effect)
        self.vibrancy_view = UIVisualEffectView.alloc().initWithFrame_(frame).autorelease()
        self.vibrancy_view.effect = vibrancy_effect
        self.effect_view.contentView().addSubview_(self.vibrancy_view)

    @property
    def style(self):
        return self._style

    @style.setter
    def style(self, value):
        if value != self._style:
            self._style = value
            self.setup_effect_view()

    @on_main_thread
    def add_vibrant_label(self, label):
        self.vibrancy_view.contentView().addSubview_(ObjCInstance(label))

def main():
    image_view = ui.ImageView(frame=(0, 0, 320, 320))
    image_view.image = ui.Image.named('test:Mandrill')
    blur_view = BlurView(style=2, frame=image_view.bounds.inset(40, 40))
    image_view.add_subview(blur_view)
    label = ui.Label(frame=blur_view.bounds)
    label.text = 'Hello World'
    label.font = ('HelveticaNeue', 40)
    label.alignment = ui.ALIGN_CENTER
    blur_view.add_vibrant_label(label)
    image_view.present('sheet')

main()
blmacbeth

@omz You're the man! Thanks for that.

blmacbeth

Noticed some bugs in the above code, so here are my fixes to make it work (in Pythonista3, at least).

# coding: utf-8

import ui
from objc_util import *

class BlurView (ui.View):
    def __init__(self, style=1, *args, **kwargs):
        ui.View.__init__(self, **kwargs)
        self._style = style
        self.effect_view = None
        self.setup_effect_view()

    @on_main_thread
    def setup_effect_view(self):
        if self.effect_view is not None:
            self.effect_view.removeFromSuperview()
        UIVisualEffectView = ObjCClass('UIVisualEffectView')
        UIVibrancyEffect = ObjCClass('UIVibrancyEffect')
        UIBlurEffect = ObjCClass('UIBlurEffect')
        UILabel = ObjCClass('UILabel')
        # Brooks patch for Rect not having a 'slice' 
        bounds = self.bounds.as_tuple()
        frame = (bounds[:2], bounds[2:])
        self.effect_view = UIVisualEffectView.alloc().initWithFrame_(frame).autorelease()
        effect = UIBlurEffect.effectWithStyle_(self._style)
        self.effect_view.effect = effect
        self.effect_view.setAutoresizingMask_(18)
        ObjCInstance(self).addSubview_(self.effect_view)
        vibrancy_effect = UIVibrancyEffect.effectForBlurEffect_(effect)
        self.vibrancy_view = UIVisualEffectView.alloc().initWithFrame_(frame).autorelease()
        self.vibrancy_view.effect = vibrancy_effect
        self.effect_view.contentView().addSubview_(self.vibrancy_view)

    @property
    def style(self):
        return self._style

    @style.setter
    def style(self, value):
        if value != self._style:
            self._style = value
            self.setup_effect_view()

    # Brooks fix @on_mains_thread to @on_main_thread
    @on_main_thread
    def add_vibrant_label(self, label):
        self.vibrancy_view.contentView().addSubview_(ObjCInstance(label))

def main():
    image_view = ui.ImageView(frame=(0, 0, 320, 320))
    image_view.image = ui.Image.named('test:Mandrill')
    blur_view = BlurView(style=2, frame=image_view.bounds.inset(40, 40))
    image_view.add_subview(blur_view)
    label = ui.Label(frame=blur_view.bounds)
    label.text = 'Hello World'
    label.font = ('HelveticaNeue', 40)
    label.alignment = ui.ALIGN_CENTER
    blur_view.add_vibrant_label(label)
    image_view.present('sheet')

main()

The biggest problem was that the ui.Rect() object does not have the ability to use slices. This was fixed with the ui.Rect().as_tuple() function. The second problem was just a typo.

B

rb

How could you apply the vibrancy effect to work with a rectangular region rather than a label? I read there is a fill method and a separator method but not sure how you would implement.

cvp

@rb What do you call a rectangular region? A label could be seen as a rectangular region if you don't set its text.

rb

Ah ok - so what is the difference then between fill and label?I just thought label would be isolated to text - but it I’ll try that!

cvp

@rb the blur_view it-self is a rectangular region, thus no need of label at all

def main():
    image_view = ui.ImageView(frame=(0, 0, 320, 320))
    image_view.image = ui.Image.named('test:Mandrill')
    blur_view = BlurView(style=16, frame=image_view.bounds.inset(40, 40))
    image_view.add_subview(blur_view)
    #label = ui.Label(frame=blur_view.bounds)
    #label.text = 'Hello World'
    #label.font = ('HelveticaNeue', 40)
    #label.alignment = ui.ALIGN_CENTER
    #blur_view.add_vibrant_label(label)
    image_view.present('sheet')
rb

No I mentioned vibrancy- ie I want bg to be blurred but a central rectangle to be not blurred and more vibrant.

cvp

@rb ok, sorry, misunderstood