[SwiftUI]背景半透明のViewの表示
SwiftUIで、あるViewの上に背景半透明のViewを表示する方法を調査しました。OSバージョンやアニメーションの有無を考慮すると、幾つかの方法に分かれており、それぞれの実装方法とメリット・デメリットをまとめました。
やりたいこと
カスタムalert viewやdrawer menuを実装します。どちらも背景が半透明の黒であることが一般的なので、黒の半透明で実装します。
drawer menuについては、横からスライドするアニメーションも実装します。
- カスタムalert viewは、alertの外側が半透明
- drawer menuは、drawer自体が半透明
開発環境
- iOS16.4
- Xcode14.3
2つの考えられる実装方法
考えられる実装方法は2つです。
-
.fullScreenCover
modifierを使い、背景を半透明にして実装する方法 -
ZStack
を使う方法
fullScreenCover(isPresented:onDismiss:content:) | Apple Developer Documentation
ZStack | Apple Developer Documentation
1. fullScreenCoverを使う方法
.fullScreenCover
を使う方法も、iOSのバージョンによって2通りあります。
- iOS16.4未満:
UIViewRepresentatibe
を使う方法 - iOS16.4以降:
.presentationBackground
を使う方法
iOS16.4未満の実装方法
最初にUIViewRepresentatibe
のサブクラスを作成します。
struct BackgroundCleanerView: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
let view = UIView()
DispatchQueue.main.async {
view.superview?.superview?.backgroundColor = .clear
}
return view
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
view.superview?.superview?.backgroundColor = .clear
の部分で、.fullScreenCover
で表示するViewの背景色を指定しています。
次に、元となるViewをカスタムAlert Viewを作成し、.fullScreenCover
で表示します。
struct FullScreenCoverView: View {
@State var isPresented: Bool = false
var body: some View {
VStack {
Text("...")
Button("show alert") {
var transaction = Transaction()
transaction.disablesAnimations = true
withTransaction(transaction) {
self.isPresented = true
}
}
}
.fullScreenCover(isPresented: $isPresented) {
CustomAlertView()
.background(BackgroundCleanerView())
}
}
}
ポイントは2点あります。
.fullScreenCover
で表示するCustomAlertView
(ソースコードを参照)に対し、.background
modifierでBackgroundCleanerView
を設定していることと、アニメーションをさせないためにwithTransaction
を使っているところです。
表示結果は以下です。
iOS16.4以上の実装方法
iOS16.4からは、新たなView Modifierである.presentationBackground
が使えます。
presentationBackground(_:) | Apple Developer Documentation
これはiOS16.4
から使える機能になります。
先ほどのBackgroundCleanerView
を新たに定義しなくても、背景透過を実現できます。
struct NewFullScreenCoverView: View {
@State var isPresented: Bool = false
var body: some View {
VStack {
Text("using .fullScreenCover")
Text("with .presentationBackground")
Button.init("show alert") {
var transaction = Transaction()
transaction.disablesAnimations = true
withTransaction(transaction) {
self.isPresented = true
}
}
}
.fullScreenCover(isPresented: $isPresented) {
CustomAlertView()
.presentationBackground(Color.clear)
}
}
}
Color.clear
を指定することで、背景透過を実現しています。
ちなみに、背景色の半透明はCustomAlertView
側で設定しています。
fullScreenCoverを使うメリットとデメリット
メリット
- iOS16.4以前でも使えるが、iOS16.4以降の方が使いやすい
- アニメーションを使わない画面遷移に適している
デメリット
- drawer menuのようなスライドして表示する場合には適していない
2. ZStackを使う方法
次はZStack
を使う方法です。実装方法はやや複雑ですが、OSバージョンとアニメーションの実装について気にしなくて良いメリットがあります
実装方法
struct DrawerView: View {
@State var showMenu: Bool = false
var body: some View {
ZStack {
VStack {
Text("using ZStack")
Button.init("show menu") {
withAnimation {
showMenu = true
}
}
}
if showMenu {
DrawerMenu(showMenu: $showMenu)
.transition(.move(edge: .leading))
}
}
}
}
.transition(.move(edge: .leading))
を使うことにより、スライドアニメーションを実現しています。withAnimation
を使うことで、アニメーション操作が可能になります。
ZStackを使うメリットとデメリット
メリット
- OSバージョンに関係なく使用できる
- 複雑なアニメーションにも適している
デメリット
- ZStackを使ってレイアウト管理をしているので、やや複雑になってしまう
実装結果はこちらになります。
まとめ
背景透明Viewの実装方法について2つ紹介しました。この記事で、やりたいことに対して適切な方法で実装できるようになる指針となれば幸いです。
ソースコード
参考
【SwiftUI】トランジション(transition)の使い方 | カピ通信
ios - How to change transition in fullScreenCover in SwiftUI? - Stack Overflow
【SwiftUI】フルスクリーンでモーダル表示する【fullScreenCover】 – .NET ゆる〜りワーク
[Resolve]-Is there a way to set a fullScreenCover background opacity?
SwiftUI: fullScreenCover with no animation? - Stack Overflow
Discussion