3

我正在尝试在我的 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()
        }
    }

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

4

1 回答 1

1

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

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

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

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

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

于 2017-08-06T21:02:08.197 回答