web-dev-qa-db-fra.com

Comment puis-je exécuter une action lorsqu'un état change?

enum SectionType: String, CaseIterable {
    case top = "Top"
    case best = "Best"
}

struct ContentView : View {
    @State private var selection: Int = 0

    var body: some View {
        SegmentedControl(selection: $selection) {
            ForEach(SectionType.allCases.identified(by: \.self)) { type in
                Text(type.rawValue).tag(type)
            }
        }
    }
}

Comment exécuter du code (par exemple print("Selection changed to \(selection)") lorsque l'état $selection Change? J'ai parcouru les documents et je n'ai rien trouvé.

22

Vous ne pouvez pas utiliser didSet observer sur @State mais vous pouvez le faire sur une propriété ObservableObject.

import SwiftUI
import Combine

final class SelectionStore: ObservableObject {
    var selection: SectionType = .top {
        didSet {
            print("Selection changed to \(selection)")
        }
    }

    // @Published var items = ["Jane Doe", "John Doe", "Bob"]
}

Ensuite, utilisez-le comme ceci:

import SwiftUI

enum SectionType: String, CaseIterable {
    case top = "Top"
    case best = "Best"
}

struct ContentView : View {
    @ObservedObject var store = SelectionStore()

    var body: some View {
        List {
            Picker("Selection", selection: $store.selection) {
                ForEach(FeedType.allCases, id: \.self) { type in
                    Text(type.rawValue).tag(type)
                }
            }.pickerStyle(SegmentedPickerStyle())

            // ForEach(store.items) { item in
            //     Text(item)
            // }
        }
    }
}
24

Voici une autre option si vous avez un composant qui met à jour un @Binding. Plutôt que de faire cela:

Component(selectedValue: self.$item, ...)

vous pouvez le faire et avoir un peu plus de contrôle:

Component(selectedValue: Binding(
    get: { self.item },
    set: { (newValue) in
              self.item = newValue
              // now do whatever you need to do once this has changed
    }), ... )

De cette façon, vous obtenez les avantages de la liaison ainsi que la détection du moment où le Component a changé la valeur.

6
P. Ent

Ne répondant pas vraiment à votre question, mais voici la bonne façon de configurer SegmentedControl (je ne voulais pas publier ce code en tant que commentaire, car il a l'air moche). Remplacez votre version ForEach par le code suivant:

ForEach(0..<SectionType.allCases.count) { index in 
    Text(SectionType.allCases[index].rawValue).tag(index)
}

Le balisage des vues avec des cas d'énumération ou même des chaînes fait qu'il se comporte de manière inadéquate - la sélection ne fonctionne pas.

Vous pouvez également ajouter les éléments suivants après la déclaration SegmentedControl pour vous assurer que la sélection fonctionne:

Text("Value: \(SectionType.allCases[self.selection].rawValue)")

Version complète de body:

var body: some View {
    VStack {
        SegmentedControl(selection: self.selection) {
            ForEach(0..<SectionType.allCases.count) { index in
                Text(SectionType.allCases[index].rawValue).tag(index)
                }
            }

        Text("Value: \(SectionType.allCases[self.selection].rawValue)")
    }
}

Concernant votre question - j'ai essayé d'ajouter didSet observateur à selection, mais cela plante l'éditeur Xcode et génère une erreur "Segmentation fault: 11" lors de la construction.

1
Russian