👻
SwiftUIでブラー(ぼかし)を徐々に弱くしたい
何を言ってるのか分からないかもしれませんが、iOS 18の写真アプリや Globetrotter のヘッダー部分などで採用されているUIを再現したいという感じです。
SwiftUIで UIVisualEffect
のようなぼかしを利用するには Material
を活用します。
Color.clear
.background(.regularMaterial)
のようにするとぼかしを一面に敷くことができます。
ただ、これだと強さが一定なので、徐々に強さを変えていくようにしたいです。
これをどう実現するかは悩んだのですが、別の調べ物をしている時に、SwiftUIでブレンドモードを利用できることを知り、活用できると思いました。
ブレンドモード
ブレンドモードはレイヤーの重なった部分の色をそれぞれのモードで計算することで、動的に画像処理を加えることができる手法です。
Photoshopなどに昔から実装されていたので馴染みがある人も多いかもしれません。
最近はCSSでもできるみたいですね。
UI系では destinationOut
がくり抜きなどで便利なのでよく使われるのですが、ここでも例に漏れず使用します。
ZStack {
Color.clear
.background(.regularMaterial)
LinearGradient(
gradient: Gradient(colors: [.black.opacity(0), .black]),
startPoint: .top,
endPoint: .bottom
)
.blendMode(.destinationOut)
}
.compositingGroup()
.compositingGroup()
でラップすると反映されます。
Before & After
ブレンドモードを指定する前と後はこんな感じです。
上の方にブラーが強くかかり、下に行くほど無くなっていくことが分かります。
出来上がったコード
public struct BlurBackgroundView: View {
public init() {}
let maxHeight: CGFloat = 160
public var body: some View {
ZStack {
Color.clear
.background(.regularMaterial)
.frame(maxWidth: .infinity, maxHeight: maxHeight)
LinearGradient(
gradient: Gradient(colors: [.black.opacity(0), .black]),
startPoint: .top,
endPoint: .bottom
)
.blendMode(.destinationOut)
.frame(maxWidth: .infinity, maxHeight: maxHeight)
}
.compositingGroup()
.frame(maxHeight: maxHeight)
.ignoresSafeArea()
.allowsHitTesting(false)
}
}
グラデーションの方向を入れ替えればタブ側にも利用できると思います。
allowsHitTesting(false)
で触っても影響がないようにしています。
extension View {
@ViewBuilder
public func blurNavigationBar() -> some View {
ZStack(alignment: .top) {
self
BlurBackgroundView()
}
}
こんな感じのextensionを生やしておくと、使いたいところだけぼかし効果を入れられて便利です。
navigationBarの背景色はAppearanceで消しておくのをお忘れなく。
Discussion