[iOS 8] UIVisualEffectViewを使ってすりガラス効果を実現する

磨りガラス効果のUIView

iOS 7からUIToolBarやUINavigationBarに磨りガラス効果が導入されましたが、 APIとして磨りガラス効果を持ったUIViewが提供されることはなく、 磨りガラス効果を使うためには iOS-blurUIImage-BlurredFrameなどの OSSを用いる方法がありました。

iOS 8では磨りガラス効果を実現できるUIViewサブクラスとしてUIVisualEffectViewが提供されています。

早速その使い方を確認していきましょう。

何ができるか?

UIVisualEffectViewは背後に描画されたレイヤにエフェクトをかけ、内部のcontentViewに置かれたUIViewに対してもエフェクトを掛けることができます。

UIVisualEffectViewに適用可能なエフェクトとしてはUIBlurEffectとUIVibrancyEffectがあります。

  • UIBlurEffect: 背後のレイヤに磨りガラス効果のエフェクトをかけ、内部のcontentViewに置かれたUIViewに対しては何も作用しません。
  • UIVibrancyEffect: UIBlurEffectが適用されたViewの上に置かれる使用方法が想定されています。内部のcontentViewに置かれたUIViewに対して、UIBlurEffectがかかったViewの背後レイヤに対応したエフェクトを掛けることができます。エフェクトはcontentViewに対してのみ作用し、subViewに作用しないことに注意してください。

以下サンプルアプリのコードを中心に具体的な実装方法を解説していきます。

import UIKit

class ViewController: UIViewController {
                            
    @IBOutlet var darkImageView: UIImageView?
    @IBOutlet var lightImageView: UIImageView?
    @IBOutlet var extraLightImageView: UIImageView?

    override func viewDidLoad() {
        super.viewDidLoad()
        addEffectViews()
    }
    
    func addEffectViews() {
        
        // 磨りガラスViewを生成
        let darkBlurView =
        blurEffectView(
            fromBlurStyle: .Dark,
            frame: darkImageView!.frame)
        let lightBlurView =
        blurEffectView(
            fromBlurStyle: .Light,
            frame: lightImageView!.frame)
        let extraLightBlurView =
        blurEffectView(
            fromBlurStyle: .ExtraLight,
            frame: extraLightImageView!.frame)
        
        self.view.addSubview(darkBlurView)
        self.view.addSubview(lightBlurView)
        self.view.addSubview(extraLightBlurView)
        
        // Vibrancyエフェクト
        let darkVibrancyView =
        vibrancyEffectView(
            fromBlurEffect: darkBlurView.effect as UIBlurEffect,
            frame: darkBlurView.bounds)
        let lightVibrancyView =
        vibrancyEffectView(
            fromBlurEffect: lightBlurView.effect as UIBlurEffect,
            frame: lightBlurView.bounds)
        let extraLightVibrancyView =
        vibrancyEffectView(
            fromBlurEffect: extraLightBlurView.effect as UIBlurEffect,
            frame: extraLightBlurView.bounds)
        
        darkBlurView.contentView.addSubview(darkVibrancyView)
        lightBlurView.contentView.addSubview(lightVibrancyView)
        extraLightBlurView.contentView.addSubview(extraLightVibrancyView)
        
        // Vibrancyエフェクトのかかった画像を追加する
        addLogoImageForVibrancyView(darkVibrancyView)
        addLogoImageForVibrancyView(lightVibrancyView)
        addLogoImageForVibrancyView(extraLightVibrancyView)
        
        // Vibrancyエフェクトのかかったラベルを追加する
        addLabelWithText("Dark", forVibrancyView: darkVibrancyView)
        addLabelWithText("Light", forVibrancyView: lightVibrancyView)
        addLabelWithText("ExtraLight", forVibrancyView: extraLightVibrancyView)
        
    }
    
    // 磨りガラス効果のViewを生成
    func blurEffectView(fromBlurStyle style: UIBlurEffectStyle, frame: CGRect) -> UIVisualEffectView {
        let effect = UIBlurEffect(style: style)
        let blurView = UIVisualEffectView(effect: effect)
        blurView.frame = frame
        return blurView
    }
    
    // VibrancyエフェクトのViewを生成
    func vibrancyEffectView(fromBlurEffect effect: UIBlurEffect, frame: CGRect) -> UIVisualEffectView {
        let vibrancyEffect = UIVibrancyEffect(forBlurEffect: effect)
        let vibrancyView = UIVisualEffectView(effect: vibrancyEffect)
        vibrancyView.frame = frame
        return vibrancyView
    }
    
    // VibrancyエフェクトのViewにロゴイメージを追加
    func addLogoImageForVibrancyView(vibrancyView: UIVisualEffectView) {
        let logoImage = UIImage(named: "classmethod").imageWithRenderingMode(.AlwaysTemplate)
        let imageView = UIImageView(image: logoImage)
        imageView.frame = CGRect(origin: CGPointZero, size: logoImage.size / 2)
        imageView.contentMode = .ScaleAspectFit
        vibrancyView.contentView.addSubview(imageView)
    }
    
    // VibrancyエフェクトのViewにラベルを追加
    func addLabelWithText(text: NSString, forVibrancyView vibrancyView: UIVisualEffectView) {
        let label =
        UILabel(frame:
            CGRect(origin:
                CGPoint(
                    x: 10.0,
                    y: vibrancyView.bounds.height / 2),
                size:
                CGSize(
                    width: vibrancyView.bounds.width,
                    height: 20.0)
            )
        )
        label.text = text
        vibrancyView.contentView.addSubview(label)
    }
    
}

// CGSizeのリサイズのためのカスタム演算子
func / (lhs: CGSize, rhs: CGFloat) -> CGSize {
    return CGSize(width: lhs.width / rhs, height: lhs.height / rhs)
}

こちらのサンプルコードではStoryboardで生成されたUIImageViewがIBOOutletで参照されています。

UIBlurEffect

背後のレイヤに磨りガラス効果のエフェクトを掛けるためには次のようにUIBlurEffectを生成します。それを元に各エフェクトに応じたUIVisualEffectViewを作成します。

l66 - l72

// 磨りガラス効果のViewを生成
func blurEffectView(fromBlurStyle style: UIBlurEffectStyle, frame: CGRect) -> UIVisualEffectView {
    let effect = UIBlurEffect(style: style)
    let blurView = UIVisualEffectView(effect: effect)
    blurView.frame = frame
    return blurView
}

UIBlurEffectではUIBlurEffectStyleとして三種類のエフェクトが選べます

enum UIBlurEffectStyle : Int {
    case ExtraLight 
    case Light 
    case Dark
}

一つ一つのエフェクトを見てみましょう

元画像 Dark
Light ExtraLight

UIVibrancyEffect

追加されたUIBlurEffectの種類に応じて、内部に置かれたコンテンツに対して背後レイヤーの影響が強いVibrancyエフェクトを与えます。

まずはコンテンツを置くVibrancyEffect用のViewを生成している箇所を見てみましょう。

l74 - l80

// VibrancyエフェクトのViewを生成
func vibrancyEffectView(fromBlurEffect effect: UIBlurEffect, frame: CGRect) -> UIVisualEffectView {
    let vibrancyEffect = UIVibrancyEffect(forBlurEffect: effect)
    let vibrancyView = UIVisualEffectView(effect: vibrancyEffect)
    vibrancyView.frame = frame
    return vibrancyView
}

UIVibrancyEffectは以下のようにBlurEffectのcontentViewの上におきます。

l48-l52

// 磨りガラスエフェクトのかかったUIVisualEffectViewのcontentViewに
// VibrancyエフェクトかかったUIVisualEffectViewを置く
darkBlurView.contentView.addSubview(darkVibrancyView)
lightBlurView.contentView.addSubview(lightVibrancyView)
extraLightBlurView.contentView.addSubview(extraLightVibrancyView)

Vibrancyエフェクトを掛けるViewを置くためのUIVisualEffectViewが用意出来ました。

次に例としてUIImageViewとUILabelを内部のcontentViewに置くことでVibrancyエフェクトをかけてみましょう。

UIImageとの併用

UIImageをVibrancyエフェクトに対応させるためにはUIImageのrenderingModeプロパティをUIImageRenderingMode.AlwaysTemplateに設定する必要があります。

l82-l89

// VibrancyエフェクトのViewにロゴイメージを追加
func addLogoImageForVibrancyView(vibrancyView: UIVisualEffectView) {
    let logoImage = UIImage(named: "classmethod").imageWithRenderingMode(.AlwaysTemplate)
    let imageView = UIImageView(image: logoImage)
    imageView.frame = CGRect(origin: CGPointZero, size: logoImage.size / 2)
    imageView.contentMode = .ScaleAspectFit
    vibrancyView.contentView.addSubview(imageView)
}

レンダリングモードの調整されたUIImageは、UIButton等に設置されてもUIButtonをVibrancyエフェクトのかかったViewのcontentViewに入れればやはりエフェクトがかかります。

UILabelとの併用

UILabelについては、内部のcontentViewに置くだけで適切に調整されたエフェクトがかかります。

l91-l107

// VibrancyエフェクトのViewにラベルを追加
func addLabelWithText(text: NSString, forVibrancyView vibrancyView: UIVisualEffectView) {
    let label =
    UILabel(frame:
        CGRect(origin:
            CGPoint(
                x: 10.0,
                y: vibrancyView.bounds.height / 2),
                 size:
            CGSize(
                width: vibrancyView.bounds.width,
                height: 20.0)
        )
    )
    label.text     = text
    vibrancyView.contentView.addSubview(label)
}

最後にこれらのサンプルコードで生成された画面を見てみましょう。

参考サイト