konohazukux / swiftSample

0 stars 0 forks source link

遷移元に値を戻す #18

Closed konohazukux closed 2 months ago

konohazukux commented 2 months ago

ProductDetailViewからProductCategoryViewに値を戻すには、Bindingを使用して状態を共有する方法が適しています。以下に、その実装方法を示します。

まず、ProductDetailViewに戻したい値を受け取るためのBindingプロパティを追加します。

//
//  ProductDetailView.swift
//  MyListApp
//
//  Created by TAKESHI SHIMADA on 2024/07/04.
//

import SwiftUI

struct ProductDetailView: View {

  let title: String
  @Binding var value1: String // Bindingプロパティを追加

  var body: some View {
    VStack {
      Text("Hello, \(title)")
        .padding(.bottom, 20)
      Text("\(value1)")
        .padding(.bottom, 20)
      HStack {
        Button(action: {
          self.value1 = "OK"
        }, label: {
          Text("OK")
        })
        .padding(.horizontal, 20)
        Button(action: {
          self.value1 = "NG"
        }, label: {
          Text("NG")
        })
        .padding(.horizontal, 20)
      }
      Spacer()
    }
  }
}

#Preview {
  ProductDetailView(title: "タイトル", value1: .constant("--"))
}

次に、ProductCategoryViewvalue1の状態を保持するために、@Stateプロパティを追加し、それをProductDetailViewに渡します。

//
//  ProductCategoryView.swift
//  MyListApp
//
//  Created by TAKESHI SHIMADA on 2024/07/04.
//

import SwiftUI

struct ProductCategoryView: View {
  @Binding var productCategory: ProductCategory
  @State private var selectedValue: String = "--" // Stateプロパティを追加

  var body: some View {
    VStack {
      HStack {
        Text(productCategory.title)
        Spacer()
        Image(systemName: productCategory.isCompleted ? "checkmark.circle.fill" : "circle")
          .foregroundColor(productCategory.isCompleted ? .green : .gray)
          .onTapGesture { }
      }
      ScrollView(.horizontal, showsIndicators: false) {
        HStack {
          ForEach(productCategory.items) { item in
            NavigationLink(destination: ProductDetailView(title: item.title, value1: $selectedValue)) { // Bindingを渡す
              ProductItemView(item: item)
            }
          }
        }
      }
      Text("Selected Value: \(selectedValue)") // 選択された値を表示
    }
    .padding(.horizontal, 10)
  }
}

#Preview {
  ProductCategoryView(productCategory: .constant(ProductCategory(title: "Sample Category", isCompleted: false, items: [ProductItem(title: "Sample Item")])))
}

これにより、ProductDetailViewから変更されたvalue1の値がProductCategoryViewに戻り、selectedValueとして表示されます。これで、詳細ビューから元のビューに値を戻すことができます。

konohazukux commented 2 months ago

f0875010cdf52627e537ca958ece557482da6707

konohazukux commented 2 months ago

クロージャーで戻す 1d8c7ed26554769a3b6116bc7afcd5245f548d6d


SwiftUIの@Bindingや@Environment、@Stateなどのプロパティラッパーは、ビュー間での状態管理やデータ共有を簡単に行うために設計されていますが、特定の要件や状況に応じて、クロージャーやデリゲートパターンを使用することも引き続き可能です。

とのことなので、基本的には @Bindingや@Environment、@State などを使用した方が良さそう

konohazukux commented 2 months ago

SwiftUIでは、従来のUIKitで使用されていたデリゲートやクロージャーに加えて、データの受け渡しや状態管理に特化した新しい方法を提供しています。ただし、デリゲートやクロージャーを使用して値を渡すことも可能です。以下に、クロージャーを使用して遷移元に値を渡す方法を説明します。

クロージャーを使用した方法

ProductDetailViewにクロージャーを追加し、ProductCategoryViewからクロージャーを渡す方法です。

ステップ1: ProductDetailViewにクロージャーを追加

ProductDetailViewにクロージャープロパティを追加します。

import SwiftUI

struct ProductDetailView: View {
    let title: String
    @State private var value1: String = "--"
    var onValueChange: (String) -> Void

    var body: some View {
        VStack {
            Text("Hello, \(title)")
                .padding(.bottom, 20)
            Text("\(value1)")
                .padding(.bottom, 20)
            HStack {
                Button(action: {
                    self.value1 = "OK"
                    self.onValueChange(self.value1) // クロージャーを呼び出す
                }, label: {
                    Text("OK")
                })
                .padding(.horizontal, 20)
                Button(action: {
                    self.value1 = "NG"
                    self.onValueChange(self.value1) // クロージャーを呼び出す
                }, label: {
                    Text("NG")
                })
                .padding(.horizontal, 20)
            }
            Spacer()
        }
    }
}

#Preview {
    ProductDetailView(title: "タイトル", onValueChange: { _ in })
}

ステップ2: ProductCategoryViewからクロージャーを渡す

ProductCategoryViewで、ProductDetailViewにクロージャーを渡します。

import SwiftUI

struct ProductCategoryView: View {
    @Binding var productCategory: ProductCategory
    @State private var selectedValue: String = "--" // Stateプロパティを追加

    var body: some View {
        NavigationView {
            VStack {
                HStack {
                    Text(productCategory.title)
                    Spacer()
                    Image(systemName: productCategory.isCompleted ? "checkmark.circle.fill" : "circle")
                        .foregroundColor(productCategory.isCompleted ? .green : .gray)
                        .onTapGesture { }
                }
                ScrollView(.horizontal, showsIndicators: false) {
                    HStack {
                        ForEach(productCategory.items) { item in
                            NavigationLink(destination: ProductDetailView(title: item.title, onValueChange: { newValue in
                                self.selectedValue = newValue // クロージャーで値を受け取る
                            })) {
                                ProductItemView(item: item)
                            }
                        }
                    }
                }
                Text("Selected Value: \(selectedValue)") // 選択された値を表示
            }
            .padding(.horizontal, 10)
        }
    }
}

#Preview {
    ProductCategoryView(productCategory: .constant(ProductCategory(title: "Sample Category", isCompleted: false, items: [ProductItem(title: "Sample Item")])))
}

まとめ

この方法では、ProductDetailViewで値が変更されたときに、クロージャーを通じてその値がProductCategoryViewに渡され、selectedValueとして表示されます。クロージャーを使用することで、UIKitでのデリゲートパターンに似た方法でデータの受け渡しが可能になります。

SwiftUIの@Binding@Environment@Stateなどのプロパティラッパーは、ビュー間での状態管理やデータ共有を簡単に行うために設計されていますが、特定の要件や状況に応じて、クロージャーやデリゲートパターンを使用することも引き続き可能です。