68
37

More than 3 years have passed since last update.

SwiftUIのsheet(ModalView)を出し分ける

Last updated at Posted at 2019-09-19

SwiftUIでの画面遷移

SwiftUIはViewにsheetファンクションが準備されており、そこから画面遷移を行います。

public func sheet<Content>(isPresented: Binding<Bool>, onDismiss: (() -> Void)? = nil, @ViewBuilder content: @escaping () -> Content) -> some View where Content : View

サンプルでは次のようにBinding<Bool>を使った例が紹介されています。

struct ContentView: View {

    @State var isPresented: Bool = false

    var body: some View {
        VStack {
            Button("present") {
                self.isPresented.toggle()
            }
        }
        .sheet(isPresented: $isPresented) {
            Text("Presenting")
        }
    }
}

ただしこの方法には少し問題があり、複数画面に遷移したい場合に動作しなくなることです。

SwiftUIのsheetの使い方

sheetを繋げるとダメ

まず次の例では、present 0ボタンを押しても動作しなくなります。
VStackにはsheetが2つ繋げられていますが$isPresented0は機能しなくなります。
$isPresented1の方に上書きされているためです。

struct ContentView: View {

    @State var isPresented0: Bool = false

    @State var isPresented1: Bool = false

    var body: some View {
        VStack {
            Button("present 0") {
                self.isPresented0.toggle()
            }
            Button("present 1") {
                self.isPresented1.toggle()
            }
        }
        .sheet(isPresented: $isPresented0) {
            Text("Presenting 0")
        }
        .sheet(isPresented: $isPresented1) {
            Text("Presenting 1")
        }
    }
}

Buttonsheetを繋げる

こうすることでisPresented0 isPresented1はどちらも機能するようになります。

struct ContentView: View {

    @State var isPresented0: Bool = false

    @State var isPresented1: Bool = false

    var body: some View {
        VStack {
            Button("present 0") {
                self.isPresented0.toggle()
            }
            .sheet(isPresented: $isPresented0) {
                Text("Presenting 0")
            }

            Button("present 1") {
                self.isPresented1.toggle()
            }
            .sheet(isPresented: $isPresented1) {
                Text("Presenting 1")
            }
        }
    }
}

ではもっと、遷移先が多くなった場合はどうすればいいのか。

@State var isPresented1: Boolに頼るのは大変ですね。

sheet(item: Binding<Item?>)を使う

Viewにはもう一つsheetファンクションが準備されています。

public func sheet<Item, Content>(item: Binding<Item?>, onDismiss: (() -> Void)? = nil, @ViewBuilder content: @escaping (Item) -> Content) -> some View where Item : Identifiable, Content : View

このファンクションを使うためにはまず、ItemIdentifiableに準拠させる必要があります。
次ので例ではenumを利用してViewの出しわけを行っています。

extension Identifiable where Self: Hashable {
    typealias ID = Self
    var id: Self { self }
}

struct ContentView: View {

    enum Presentation: View, Hashable, Identifiable {
        case second
        case third
        var body: some View {
            switch self {
            case .second: return AnyView(SecondView())
            case .third: return AnyView(ThirdView())
            }
        }
    }

    @State var presentation: Presentation?

    var body: some View {
        VStack {
            Button("second") {
                self.presentation = .second
            }
            Button("third") {
                self.presentation = .third
            }
        }
        .sheet(item: self.$presentation) { $0 }
    }
}

引数を持って遷移したい時

extension Identifiable where Self: Hashable {
    typealias ID = Self
    var id: Self { self }
}

struct ContentView: View {

    enum Presentation: View, Hashable, Identifiable {
        case second(text: String)
        case third
        var body: some View {

            switch self {
            case .second(let text): return AnyView(SecondView(text: text))
            case .third: return AnyView(ThirdView())
            }
        }
    }

    @State var presentation: Presentation?

    var body: some View {
        VStack {
            Button("second") {
                self.presentation = .second(text: "SwiftUI")
            }
            Button("third") {
                self.presentation = .third
            }
        }
        .sheet(item: self.$presentation) { $0 }
    }
}

struct SecondView: View {

    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

    var body: some View {
        Button("戻る") {
            self.presentationMode.wrappedValue.dismiss()
        }
    }
}

次の記事もオススメです。

68
37
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
68
37