import SwiftUI /// A view that drives a recording session. /// /// The view shows a main button that starts, pauses, and resumes a recording, with a label above it displaying the elapsed recording time. /// While paused, a send button lets the user submit the recording for processing, showing a progress indicator until the processing finishes. /// /// All state and control behavior lives in the view's ``Model``; the view itself only renders it and forwards button presses and state changes. public struct RecordingView: View { // MARK: Properties /// The model that owns the recording state and drives the view's controls. @State private var model: Model // MARK: Initializers /// Creates a recording view in the not-recording state. public init() { self.model = .init() } // MARK: Body /// The content of the view: the timer label above the main and send buttons, with every change of the model's state forwarded back /// to ``Model/updatedState(shouldRestartTimer:)``. public var body: some View { VStack( spacing: Constant.Spacing.stack ) { if model.shouldShowTimer { Text(model.timerText) } HStack( spacing: Constant.Spacing.stack ) { Button { model.pressedMain() } label: { Image(model.iconMain) } .disabled(model.shouldDisableMain) if model.shouldShowSend { Button { model.pressedSend() } label: { if let icon = model.iconSend { Image(icon) } else { ProgressView() } } .disabled(model.isProcessing) } } } .onChange( of: model.state, initial: false ) { oldValue, _ in model.updatedState( shouldRestartTimer: oldValue == .notRecording ) } } } // MARK: - Constants /// The constant values used across the view. private enum Constant { /// The spacing constants. enum Spacing { /// The spacing between the elements of a stack. static let stack: CGFloat = 16 } } // MARK: - Previews #Preview( "Recording view" ) { RecordingView() }