- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 92
 
Description
Tweaking the MCamera to just take photos, not videos, I found a configuration like this to be necessary:
        .setCameraOutputType(.photo)
        .setAudioAvailability(false)
        .setCameraScreen { cameraManager, id, closeMCameraAction in
            DefaultCameraScreen(
                cameraManager: cameraManager,
                namespace: id,
                closeMCameraAction: closeMCameraAction
            ).cameraOutputSwitchAllowed(false)
        }
It works, so that's good :)
But it took me a while to find that I needed to use setCameraScreen to remove the switch button from the default screen.
I read the source and found DefaultCameraScreen.BottomBar.isOutputTypeSwitchActive and then cameraOutputSwitchAllowed and backtracked from there.
Initially, I expected .setCameraOutputType(.photo) to do all that for me. If the camera is limited to output photos, then the rest is somewhat implied. Requiring audio for a photo-only camera sounds like a misconfiguration more than anything else.
The .set* operations compose well, but the inner .cameraOutputSwitchAllowed(false) doesn't work well with that.  So I don't see a point in offering a convenience operation like .setCameraOutputType(.photo) that secretly also configures the camera screen view, potentially replacing whatever the user configured before.
Instead of doing this consistently on the user's side, what do you think about a convenience initializer or view factory on MCamera? That bundles everything in 1 call and does the work early.
// Convenience init
MCamera(mode: .photoOnly)
MCamera(mode: .videoOnly)
MCamera(mode: .photoAndVideo) // Default value, also available as .init()
// Factory
MCamera.make(mode: .photoOnly)
MCamera.make(mode: .videoOnly)
MCamera.make(mode: .photoAndVideo) // Default, like .init()While init(mode:) would read nicely, the mode needs to produce a View Builder that applies directly in MCamera.body (we can't call super.init().setCamerOutputType(.photo) etc. in an initializer of course).  But in the MCamera.body, the view is not of type MCamera, so we could't restrict a mode's view modification to MCamera.
Like this:
extension MCamera {
    enum InitialMode {
        static var `default`: Self { .photoAndVideo }
        case photoAndVideo
        case photoOnly
        case videoOnly
        @ViewBuilder
        func body(content: MCamera) -> some View {
            content.setCameraOutputType(.photo) // etc.
        }
    }
    func apply(initialMode: InitialMode) -> some View {
        initialMode.body(content: self)
    }
}That would express the idea well, but you can't call apply(initialMode:) in MCamera.body:
public struct MCamera: View {
    @ObservedObject var manager: CameraManager
    @Namespace var namespace
    var config: Config = .init()
    private let initialMode: InitialMode
    public init(initialMode: InitialMode = .photoAndVideo) {
        self.initialMode = initialMode
    }
    public var body: some View { 
        initialMode.apply(content: content) // 🛑 content is not the correct type
   }
    private var content: some View { if config.isCameraConfigured {
        ZStack(content: createContent)
            .onDisappear(perform: onDisappear)
            .onChange(of: manager.attributes.capturedMedia, perform: onCapturedMediaChange)
    }}
}
With a static factory, that would work:
extension MCamera {
    static func make(initialMode: InitialMode) -> Self {
        MCamera().apply(initialMode: initialMode)
    }
}