🍣

[SwiftUI]背景半透明のViewの表示

2023/10/20に公開

SwiftUIで、あるViewの上に背景半透明のViewを表示する方法を調査しました。OSバージョンやアニメーションの有無を考慮すると、幾つかの方法に分かれており、それぞれの実装方法とメリット・デメリットをまとめました。

やりたいこと

カスタムalert viewやdrawer menuを実装します。どちらも背景が半透明の黒であることが一般的なので、黒の半透明で実装します。
drawer menuについては、横からスライドするアニメーションも実装します。

  • カスタムalert viewは、alertの外側が半透明
  • drawer menuは、drawer自体が半透明

開発環境

  • iOS16.4
  • Xcode14.3

2つの考えられる実装方法

考えられる実装方法は2つです。

  1. .fullScreenCover modifierを使い、背景を半透明にして実装する方法
  2. 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を使っているところです。

表示結果は以下です。

Screenshot

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を使ってレイアウト管理をしているので、やや複雑になってしまう

実装結果はこちらになります。

Screen Record

まとめ

背景透明Viewの実装方法について2つ紹介しました。この記事で、やりたいことに対して適切な方法で実装できるようになる指針となれば幸いです。

ソースコード

https://github.com/usk-sample/TransparencyTest

参考

【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

https://zenn.dev/usk2000/scraps/99df702068c223

Discussion