【问题标题】:ReSwift - How to deal with state changes that depend on old state as well as new state in the ViewReSwift - 如何处理依赖于旧状态和视图中新状态的状态变化
【发布时间】:2017-08-05 17:01:38
【问题描述】:

我正在尝试在我的 ios 项目中使用 ReSwift,并且有一个关于如何处理我的视图变化的问题。我发现我需要知道旧状态是什么,然后才能应用新状态提出的更改。在我的反应项目中使用 redux 时,我从来不需要知道我的旧状态是什么。

我的特殊用例是,我正在构建一个带有 Overlay 屏幕的 CameraView。从应用程序中的任何位置说 ViewController 我可以创建一个 CameraView 并触发它通过触发一个动作从其中打开一个 UIImagePickerController。这是一些代码:

//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)
    }

}

以上就是打开和关闭相机的所有代码。当我们添加更多操作(例如禁用或启用闪光灯)时,我的困惑开始了。在我看来,我需要处理额外的状态转换。

我的行为现在发展到:

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

我现在的状态是这样的:

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.

我的观点是复杂的开始。如果用户启用了 Flash 并且我正在响应 FlashToggleAction 状态更改,我如何在我的视图中处理状态更改?

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()
        }
    }

我现在如何应对变化?我能想到的唯一方法是存储对旧状态的引用并自己比较差异。

【问题讨论】:

  • 我现在没有时间给出完整的答案,但首先要做的是。打开/关闭图像选择器的逻辑不应该存在于视图中。您作为弱车传递的视图控制器很可能是符合 StoreSubscriber 的正确候选者。对于此操作,我通常存储一个 didAskForCamera 状态,该状态会在 newState 回调上触发相机的打开。然后,您再次将该变量重置为 false,这样它就不会在每次状态更改时被多次调用。最后,使用 flashon、flashoff(不是切换)等动作为相机视图创建一个单独的状态。
  • 对于像上面这样的简单操作,我发现枚举案例工作得很好。即枚举CameraActions: ActionType { case flashOn, case flashOff, etc)

标签: ios swift redux reswift


【解决方案1】:

在这种情况下,我的第一个问题确实是:您是否需要将其作为应用状态的一部分进行处理?是否有人需要收到有关相机状态更改的通知?如果没有,请将其作为实现细节保留在 UI 层中。让 biew 控制器自己打开摄像头,拍摄结果图像,然后发送 DoStuffWithImage(imageFromCam)

这只是一个一般性建议:不要在 ReSwift 中为您的 UIKit 特定交互建模。对有趣的数据流进行建模。然后让 UI 组件朝着这个目标努力。

您真正的问题暗示:如何对存储订阅者进行分区?

目前,您使用单个订阅者来处理与相机相关的内容的问题。但是您也可以为每个可独立修改的组件编写 1 个订阅者。就像闪光灯切换一样。然后你只需要检查那里的闪光灯切换的状态变化,忽略其他设置;虽然在这种情况下,闪光灯切换可以利用知道他的相机是否真的打开 - 你在相机关闭时重置闪光灯状态以解决这个问题”,有效地移动“闪光灯AND camera active" 逻辑到减速器。

我可以提出更多的方法和想法,但最终归结为:您的应用程序中的组件是什么?相机状态控制是应用程序状态的核心部分,还是只是一个微小的细节?在设计过程的早期权衡这一点有助于找到合适的解决方案。

【讨论】:

  • 谢谢 ctietze。 Camera 是一个通用组件,任何 ViewController 都应该能够通过触发 Action 来打开相机。我喜欢 redux 的哲学,它说 View 中的任何更改都是通过 Actions 触发的。这给了我们想要的可预测性。感谢关于订阅者分区的想法。现在就试试。
猜你喜欢
  • 2021-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-21
  • 2016-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多