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")
}
}
}
Button
にsheet
を繋げる
こうすることで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
このファンクションを使うためにはまず、Item
をIdentifiable
に準拠させる必要があります。
次ので例では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()
}
}
}
次の記事もオススメです。