This is a simple implementation of integrating state machine.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | struct SpeechRecordBtnView : View { @State var state : SpeechState = . notAuth @ObservedObject var manager = SpeechAuthManager . shared var backgroundColor : Color { switch state { case . notAuth : return . gray case . idle : return . accentColor case . recording : return . red case . cancel : return . init ( white : 0.1 ) } } var scale : CGFloat { switch state { case . notAuth : return 1.0 case . idle : return 1.0 case . recording : return 1.8 case . cancel : return 1.4 } } public var body : some View { ZStack { backgroundColor . animation (. easeOut ( duration : 0.2 )) . clipShape ( Circle ()) . zIndex ( 0 ) Image ( systemName : state != . cancel ? "waveform" : "xmark" ) . font (. system ( size : 30 , weight : . medium , design : . default )) . foregroundColor (. white ) . opacity ( state == . recording ? 0.8 : 1.0 ) . padding ( 20 ) . transition (. opacity ) . layoutPriority ( 2 ) . zIndex ( 1 ) } . scaleEffect ( scale ) . shadow ( color : Color (. sRGBLinear , white : 0 , opacity : 0.2 ), radius : 5 , x : 0 , y : 3 ) } } enum SpeechState { case notAuth case idle case recording case cancel } class SpeechAuthManager : ObservableObject { // singleton static let shared = SpeechAuthManager () // status @ Published var authStatus : SFSpeechRecognizerAuthorizationStatus = SFSpeechRecognizer . authorizationStatus () // request func requestSpeechRecognitionGrant () { SFSpeechRecognizer . requestAuthorization { authStatus in if self . authStatus != authStatus { DispatchQueue . main . async { self . authStatus = authStatus } } } } } |