2017-08-05 1 views
3

J'essaie de travailler avec ReSwift dans mon projet ios et j'avais une question sur la façon de gérer les changements dans ma vue. Je trouve que j'ai besoin de savoir ce qu'était l'ancien état avant de pouvoir appliquer les changements proposés par le nouvel état. Je n'ai jamais eu besoin de savoir quel était mon ancien état quand je travaillais avec un redux dans mes réactions.ReSwift - Comment gérer les changements d'état qui dépendent de l'ancien état ainsi que du nouvel état dans la vue

Mon cas d'utilisation particulier est, je suis bulding un CameraView avec un écran de superposition. De n'importe où dans l'application, disons un ViewController je peux créer un CameraView et le déclencher pour ouvrir un UIImagePickerController à partir de celui-ci en tirant une action. Voici un code:

//ViewController: 

class MainViewController: UIViewController { 
    var cameraView: CameraView? 
    @IBOutlet weak var launchCameraButton: UIButton! 

    init() { 
     cameraView = CameraView(self) 
    } 

    @IBAction func launchCameraButtonClicked(_ sender: Any) { 
     store.dispatch(OpenCameraAction()) 
    } 

} 

//CameraActions 
struct OpenCameraAction: Action {} 
struct CloseCameraAction: Action {} 

//CameraState 
struct CameraState { 
    var cameraIsVisible: Bool 
} 

func cameraReducer(state: CameraState?, action: Action) -> CameraState { 
    let initialState = state ?? CameraState() 

    switch action { 
     case _ as OpenCameraAction: 
      return CameraState(cameraIsVisible: true) 
     default: 
      return initialState 
    }  
} 

//CameraView 

class CameraView: StoreSubscriber { 
    private var imagePicker: UIImagePickerController? 
    weak private var viewController: UIViewController? 

    init(viewController: UIViewController) { 
     self.viewController = viewController 
     super.init() 
     imagePicker = UIImagePickerController() 
     imagePicker?.allowsEditing = true 
     imagePicker?.sourceType = .camera 
     imagePicker?.cameraCaptureMode = .photo 
     imagePicker?.cameraDevice = .rear 
     imagePicker?.modalPresentationStyle = .fullScreen 
     imagePicker?.delegate = self 
     imagePicker?.showsCameraControls = false 

     store.subscribe(self) { subscription in 
      subscription.select { state in 
       state.camera 
      } 
     } 
    } 

    func newState(state: CameraState?) { 
     guard let state = state else { 
      return 
     } 
     if state.cameraIsVisible { 
      self.open() 
     } else if !state.cameraIsVisible { 
      self.close() 
     } 
    } 

    func open() { 
     if let imagePicker = self.imagePicker { 
      self.viewController?.present(
       imagePicker, 
       animated: true 
      ) 
     } 
    } 

    func close(){ 
     self.imagePicker?.dismiss(animated: true) 
    } 

} 

Ce qui précède est tout le code pour ouvrir et fermer la caméra. Ma confusion commence, lorsque nous ajoutons d'autres actions, telles que désactiver ou activer le flash. J'ai besoin de virer sur les transitions d'état supplémentaires à mon avis.

Mes actions se développent maintenant:

struct OpenCameraAction: Action {} 
struct CloseCameraAction: Action {} 
struct FlashToggleAction: Action {} 

Mon état ressemble maintenant à ceci:

struct CameraState { 
    var cameraIsVisible: Bool 
    var flashOn: Bool 
} 
// not showing reducer changes as it self explanatory 
// what the state changes will be for my actions. 

Selon moi, où les complications commencent. Si l'utilisateur a activé Flash et que je réponds à un changement d'état FlashToggleAction, comment puis-je modifier le changement d'état dans ma vue?

func newState(state: CameraState?) { 
     guard let state = state else { 
      return 
     } 

     // this will get triggered regardless of whether 
     // a change in the flash happened or not. 
     self.toggleFlash(state.flashOn) 

     // now the below lines will be executed even though 
     // the only change was a flash on action. 
     //I don't want my camera to opened again or closed. 
     if state.cameraIsVisible { 
      self.open() 
     } else if !state.cameraIsVisible { 
      self.close() 
     } 
    } 

Comment puis-je répondre aux changements maintenant? La seule façon dont je peux penser à gérer cela est de stocker une référence à l'ancien état et de comparer la différence moi-même.

+1

Je n'ai pas le temps de répondre maintenant, mais d'abord en commençant par le début. à l'intérieur de la vue, le contrôleur de vue que vous passez Une voiture faible est probablement le bon candidat pour la conformité à StoreSubscriber.Pour cette action, je stocke généralement un état didAskForCamera qui déclenche l'ouverture de la caméra sur le callback newState. Vous redéfinissez ensuite cette variable sur false pour qu'elle ne soit pas appelée plusieurs fois pour chaque changement d'état. Enfin, créez un état séparé pour la vue de la caméra avec des actions comme flashon, flashoff (pas basculer). – Rog

+1

Aussi pour des actions simples comme ci-dessus, je trouve que les cas d'énumération fonctionnent très bien. ie enum CameraActions: ActionType {affaire flashOn, affaire flashOff, etc) – Rog

Répondre

2

Dans ce cas, ma première question est vraiment: avez-vous besoin de gérer cela dans le cadre de l'état de votre application? Quelqu'un a-t-il besoin d'être averti du changement d'état de la caméra? Sinon, conservez-le en tant que détail d'implémentation dans votre couche d'interface utilisateur. Laisser le contrôleur Biew ouvrir la caméra par lui-même, prendre une image résultante, puis envoyer DoStuffWithImage(imageFromCam).

Ceci est juste un conseil général: ne modélisez pas vos interactions spécifiques à UIKit dans ReSwift. Modélisez le flux de données qui est intéressant. Ensuite, faites en sorte que les composants de l'interface utilisateur atteignent cet objectif.

Votre vraie question se lit comme suit: Comment puis-je partitionner les abonnés?

Actuellement, vous approchez le problème en utilisant un seul abonné pour les choses liées à la caméra. Mais vous pouvez aussi bien écrire 1 abonné pour chaque composant modifiable indépendamment. Comme la bascule flash. Ensuite, il vous suffit de vérifier les changements d'état en ce qui concerne la bascule du flash, en ignorant les autres paramètres; vous pouvez réinitialiser l'état du flash lorsque l'appareil photo se ferme pour prendre soin de cela ", ce qui déplace efficacement la logique" flash AND camera active " Je peux trouver plus d'approches et d'idées, mais en fin de compte cela se résume à ceci: qu'est-ce qu'un composant dans votre application? Est-ce que le contrôle de l'état de la caméra est une pièce centrale de l'état de l'application, ou juste Si vous pesez cela dès le début du processus de conception, vous pourrez trouver une solution appropriée

+0

Merci ctietze. La caméra est un composant commun et n'importe quel ViewController devrait être capable d'ouvrir la caméra en déclenchant une action. J'aime la philosophie de redux qui dit que tous les changements dans la vue sont tous déclenchés via des actions. Cela nous donne la prévisibilité que nous désirons. Merci pour l'idée du partitionnement des abonnés. Je vais essayer ça maintenant. – sethu