[iOS 8] UIVisualEffectViewを使ってすりガラス効果を実現する
磨りガラス効果のUIView
iOS 7からUIToolBarやUINavigationBarに磨りガラス効果が導入されましたが、 APIとして磨りガラス効果を持ったUIViewが提供されることはなく、 磨りガラス効果を使うためには iOS-blurや UIImage-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) }
最後にこれらのサンプルコードで生成された画面を見てみましょう。