├── MOKORecorder ├── MOKORecorder.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcuserdata │ │ └── spring.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ └── MOKORecorder.xcscheme └── MOKORecorder │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── MOKORecordButton.h │ ├── MOKORecordButton.m │ ├── MOKORecordHeaderDefine.h │ ├── MOKORecordPowerAnimationView.h │ ├── MOKORecordPowerAnimationView.m │ ├── MOKORecordShowManager.h │ ├── MOKORecordShowManager.m │ ├── MOKORecordToastContentView.h │ ├── MOKORecordToastContentView.m │ ├── MOKORecordView.h │ ├── MOKORecordView.m │ ├── MOKORecorderTool.h │ ├── MOKORecorderTool.m │ ├── ViewController.h │ ├── ViewController.m │ ├── ic_record@2x.png │ ├── ic_record@3x.png │ ├── ic_record_ripple@2x.png │ ├── ic_record_ripple@3x.png │ ├── ic_record_too_short@2x.png │ ├── ic_record_too_short@3x.png │ ├── ic_release_to_cancel@2x.png │ ├── ic_release_to_cancel@3x.png │ └── main.m └── README.md /MOKORecorder/MOKORecorder.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6FFBAA331EB1C50700988ED3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FFBAA321EB1C50700988ED3 /* main.m */; }; 11 | 6FFBAA361EB1C50700988ED3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FFBAA351EB1C50700988ED3 /* AppDelegate.m */; }; 12 | 6FFBAA391EB1C50700988ED3 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FFBAA381EB1C50700988ED3 /* ViewController.m */; }; 13 | 6FFBAA3C1EB1C50700988ED3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6FFBAA3A1EB1C50700988ED3 /* Main.storyboard */; }; 14 | 6FFBAA3E1EB1C50700988ED3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FFBAA3D1EB1C50700988ED3 /* Assets.xcassets */; }; 15 | 6FFBAA411EB1C50700988ED3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6FFBAA3F1EB1C50700988ED3 /* LaunchScreen.storyboard */; }; 16 | 6FFBAA501EB1C53000988ED3 /* ic_record_ripple@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6FFBAA481EB1C53000988ED3 /* ic_record_ripple@2x.png */; }; 17 | 6FFBAA511EB1C53000988ED3 /* ic_record_ripple@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6FFBAA491EB1C53000988ED3 /* ic_record_ripple@3x.png */; }; 18 | 6FFBAA521EB1C53000988ED3 /* ic_record_too_short@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6FFBAA4A1EB1C53000988ED3 /* ic_record_too_short@2x.png */; }; 19 | 6FFBAA531EB1C53000988ED3 /* ic_record_too_short@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6FFBAA4B1EB1C53000988ED3 /* ic_record_too_short@3x.png */; }; 20 | 6FFBAA541EB1C53000988ED3 /* ic_record@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6FFBAA4C1EB1C53000988ED3 /* ic_record@2x.png */; }; 21 | 6FFBAA551EB1C53000988ED3 /* ic_record@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6FFBAA4D1EB1C53000988ED3 /* ic_record@3x.png */; }; 22 | 6FFBAA561EB1C53000988ED3 /* ic_release_to_cancel@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6FFBAA4E1EB1C53000988ED3 /* ic_release_to_cancel@2x.png */; }; 23 | 6FFBAA571EB1C53000988ED3 /* ic_release_to_cancel@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6FFBAA4F1EB1C53000988ED3 /* ic_release_to_cancel@3x.png */; }; 24 | 6FFBAA5A1EB1C55B00988ED3 /* MOKORecordButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FFBAA591EB1C55B00988ED3 /* MOKORecordButton.m */; }; 25 | 6FFBAA5D1EB1C69A00988ED3 /* MOKORecorderTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FFBAA5C1EB1C69A00988ED3 /* MOKORecorderTool.m */; }; 26 | 6FFBAA601EB1C73E00988ED3 /* MOKORecordPowerAnimationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FFBAA5F1EB1C73E00988ED3 /* MOKORecordPowerAnimationView.m */; }; 27 | 6FFBAA631EB1C83500988ED3 /* MOKORecordToastContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FFBAA621EB1C83500988ED3 /* MOKORecordToastContentView.m */; }; 28 | 6FFBAA661EB1D2BE00988ED3 /* MOKORecordView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FFBAA651EB1D2BE00988ED3 /* MOKORecordView.m */; }; 29 | 6FFBAA6A1EB1D4D200988ED3 /* MOKORecordShowManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FFBAA691EB1D4D200988ED3 /* MOKORecordShowManager.m */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 6FFBAA2E1EB1C50700988ED3 /* MOKORecorder.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MOKORecorder.app; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 6FFBAA321EB1C50700988ED3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 35 | 6FFBAA341EB1C50700988ED3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 36 | 6FFBAA351EB1C50700988ED3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 37 | 6FFBAA371EB1C50700988ED3 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 38 | 6FFBAA381EB1C50700988ED3 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 39 | 6FFBAA3B1EB1C50700988ED3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 40 | 6FFBAA3D1EB1C50700988ED3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 41 | 6FFBAA401EB1C50700988ED3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 42 | 6FFBAA421EB1C50700988ED3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 43 | 6FFBAA481EB1C53000988ED3 /* ic_record_ripple@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ic_record_ripple@2x.png"; sourceTree = ""; }; 44 | 6FFBAA491EB1C53000988ED3 /* ic_record_ripple@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ic_record_ripple@3x.png"; sourceTree = ""; }; 45 | 6FFBAA4A1EB1C53000988ED3 /* ic_record_too_short@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ic_record_too_short@2x.png"; sourceTree = ""; }; 46 | 6FFBAA4B1EB1C53000988ED3 /* ic_record_too_short@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ic_record_too_short@3x.png"; sourceTree = ""; }; 47 | 6FFBAA4C1EB1C53000988ED3 /* ic_record@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ic_record@2x.png"; sourceTree = ""; }; 48 | 6FFBAA4D1EB1C53000988ED3 /* ic_record@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ic_record@3x.png"; sourceTree = ""; }; 49 | 6FFBAA4E1EB1C53000988ED3 /* ic_release_to_cancel@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ic_release_to_cancel@2x.png"; sourceTree = ""; }; 50 | 6FFBAA4F1EB1C53000988ED3 /* ic_release_to_cancel@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ic_release_to_cancel@3x.png"; sourceTree = ""; }; 51 | 6FFBAA581EB1C55B00988ED3 /* MOKORecordButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MOKORecordButton.h; sourceTree = ""; }; 52 | 6FFBAA591EB1C55B00988ED3 /* MOKORecordButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MOKORecordButton.m; sourceTree = ""; }; 53 | 6FFBAA5B1EB1C69A00988ED3 /* MOKORecorderTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MOKORecorderTool.h; sourceTree = ""; }; 54 | 6FFBAA5C1EB1C69A00988ED3 /* MOKORecorderTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MOKORecorderTool.m; sourceTree = ""; }; 55 | 6FFBAA5E1EB1C73E00988ED3 /* MOKORecordPowerAnimationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MOKORecordPowerAnimationView.h; sourceTree = ""; }; 56 | 6FFBAA5F1EB1C73E00988ED3 /* MOKORecordPowerAnimationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MOKORecordPowerAnimationView.m; sourceTree = ""; }; 57 | 6FFBAA611EB1C83500988ED3 /* MOKORecordToastContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MOKORecordToastContentView.h; sourceTree = ""; }; 58 | 6FFBAA621EB1C83500988ED3 /* MOKORecordToastContentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MOKORecordToastContentView.m; sourceTree = ""; }; 59 | 6FFBAA641EB1D2BE00988ED3 /* MOKORecordView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MOKORecordView.h; sourceTree = ""; }; 60 | 6FFBAA651EB1D2BE00988ED3 /* MOKORecordView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MOKORecordView.m; sourceTree = ""; }; 61 | 6FFBAA671EB1D2ED00988ED3 /* MOKORecordHeaderDefine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MOKORecordHeaderDefine.h; sourceTree = ""; }; 62 | 6FFBAA681EB1D4D200988ED3 /* MOKORecordShowManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MOKORecordShowManager.h; sourceTree = ""; }; 63 | 6FFBAA691EB1D4D200988ED3 /* MOKORecordShowManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MOKORecordShowManager.m; sourceTree = ""; }; 64 | /* End PBXFileReference section */ 65 | 66 | /* Begin PBXFrameworksBuildPhase section */ 67 | 6FFBAA2B1EB1C50700988ED3 /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | /* End PBXFrameworksBuildPhase section */ 75 | 76 | /* Begin PBXGroup section */ 77 | 6FFBAA251EB1C50700988ED3 = { 78 | isa = PBXGroup; 79 | children = ( 80 | 6FFBAA301EB1C50700988ED3 /* MOKORecorder */, 81 | 6FFBAA2F1EB1C50700988ED3 /* Products */, 82 | ); 83 | sourceTree = ""; 84 | }; 85 | 6FFBAA2F1EB1C50700988ED3 /* Products */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 6FFBAA2E1EB1C50700988ED3 /* MOKORecorder.app */, 89 | ); 90 | name = Products; 91 | sourceTree = ""; 92 | }; 93 | 6FFBAA301EB1C50700988ED3 /* MOKORecorder */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 6FFBAA481EB1C53000988ED3 /* ic_record_ripple@2x.png */, 97 | 6FFBAA491EB1C53000988ED3 /* ic_record_ripple@3x.png */, 98 | 6FFBAA4A1EB1C53000988ED3 /* ic_record_too_short@2x.png */, 99 | 6FFBAA4B1EB1C53000988ED3 /* ic_record_too_short@3x.png */, 100 | 6FFBAA4C1EB1C53000988ED3 /* ic_record@2x.png */, 101 | 6FFBAA4D1EB1C53000988ED3 /* ic_record@3x.png */, 102 | 6FFBAA4E1EB1C53000988ED3 /* ic_release_to_cancel@2x.png */, 103 | 6FFBAA4F1EB1C53000988ED3 /* ic_release_to_cancel@3x.png */, 104 | 6FFBAA341EB1C50700988ED3 /* AppDelegate.h */, 105 | 6FFBAA351EB1C50700988ED3 /* AppDelegate.m */, 106 | 6FFBAA371EB1C50700988ED3 /* ViewController.h */, 107 | 6FFBAA381EB1C50700988ED3 /* ViewController.m */, 108 | 6FFBAA5B1EB1C69A00988ED3 /* MOKORecorderTool.h */, 109 | 6FFBAA5C1EB1C69A00988ED3 /* MOKORecorderTool.m */, 110 | 6FFBAA581EB1C55B00988ED3 /* MOKORecordButton.h */, 111 | 6FFBAA591EB1C55B00988ED3 /* MOKORecordButton.m */, 112 | 6FFBAA5E1EB1C73E00988ED3 /* MOKORecordPowerAnimationView.h */, 113 | 6FFBAA5F1EB1C73E00988ED3 /* MOKORecordPowerAnimationView.m */, 114 | 6FFBAA611EB1C83500988ED3 /* MOKORecordToastContentView.h */, 115 | 6FFBAA621EB1C83500988ED3 /* MOKORecordToastContentView.m */, 116 | 6FFBAA641EB1D2BE00988ED3 /* MOKORecordView.h */, 117 | 6FFBAA651EB1D2BE00988ED3 /* MOKORecordView.m */, 118 | 6FFBAA671EB1D2ED00988ED3 /* MOKORecordHeaderDefine.h */, 119 | 6FFBAA681EB1D4D200988ED3 /* MOKORecordShowManager.h */, 120 | 6FFBAA691EB1D4D200988ED3 /* MOKORecordShowManager.m */, 121 | 6FFBAA3A1EB1C50700988ED3 /* Main.storyboard */, 122 | 6FFBAA3D1EB1C50700988ED3 /* Assets.xcassets */, 123 | 6FFBAA3F1EB1C50700988ED3 /* LaunchScreen.storyboard */, 124 | 6FFBAA421EB1C50700988ED3 /* Info.plist */, 125 | 6FFBAA311EB1C50700988ED3 /* Supporting Files */, 126 | ); 127 | path = MOKORecorder; 128 | sourceTree = ""; 129 | }; 130 | 6FFBAA311EB1C50700988ED3 /* Supporting Files */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 6FFBAA321EB1C50700988ED3 /* main.m */, 134 | ); 135 | name = "Supporting Files"; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | 6FFBAA2D1EB1C50700988ED3 /* MOKORecorder */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = 6FFBAA451EB1C50700988ED3 /* Build configuration list for PBXNativeTarget "MOKORecorder" */; 144 | buildPhases = ( 145 | 6FFBAA2A1EB1C50700988ED3 /* Sources */, 146 | 6FFBAA2B1EB1C50700988ED3 /* Frameworks */, 147 | 6FFBAA2C1EB1C50700988ED3 /* Resources */, 148 | ); 149 | buildRules = ( 150 | ); 151 | dependencies = ( 152 | ); 153 | name = MOKORecorder; 154 | productName = MOKORecorder; 155 | productReference = 6FFBAA2E1EB1C50700988ED3 /* MOKORecorder.app */; 156 | productType = "com.apple.product-type.application"; 157 | }; 158 | /* End PBXNativeTarget section */ 159 | 160 | /* Begin PBXProject section */ 161 | 6FFBAA261EB1C50700988ED3 /* Project object */ = { 162 | isa = PBXProject; 163 | attributes = { 164 | LastUpgradeCheck = 0830; 165 | ORGANIZATIONNAME = Spring; 166 | TargetAttributes = { 167 | 6FFBAA2D1EB1C50700988ED3 = { 168 | CreatedOnToolsVersion = 8.3; 169 | DevelopmentTeam = TJ528WBL3B; 170 | ProvisioningStyle = Automatic; 171 | }; 172 | }; 173 | }; 174 | buildConfigurationList = 6FFBAA291EB1C50700988ED3 /* Build configuration list for PBXProject "MOKORecorder" */; 175 | compatibilityVersion = "Xcode 3.2"; 176 | developmentRegion = English; 177 | hasScannedForEncodings = 0; 178 | knownRegions = ( 179 | en, 180 | Base, 181 | ); 182 | mainGroup = 6FFBAA251EB1C50700988ED3; 183 | productRefGroup = 6FFBAA2F1EB1C50700988ED3 /* Products */; 184 | projectDirPath = ""; 185 | projectRoot = ""; 186 | targets = ( 187 | 6FFBAA2D1EB1C50700988ED3 /* MOKORecorder */, 188 | ); 189 | }; 190 | /* End PBXProject section */ 191 | 192 | /* Begin PBXResourcesBuildPhase section */ 193 | 6FFBAA2C1EB1C50700988ED3 /* Resources */ = { 194 | isa = PBXResourcesBuildPhase; 195 | buildActionMask = 2147483647; 196 | files = ( 197 | 6FFBAA411EB1C50700988ED3 /* LaunchScreen.storyboard in Resources */, 198 | 6FFBAA521EB1C53000988ED3 /* ic_record_too_short@2x.png in Resources */, 199 | 6FFBAA551EB1C53000988ED3 /* ic_record@3x.png in Resources */, 200 | 6FFBAA501EB1C53000988ED3 /* ic_record_ripple@2x.png in Resources */, 201 | 6FFBAA511EB1C53000988ED3 /* ic_record_ripple@3x.png in Resources */, 202 | 6FFBAA571EB1C53000988ED3 /* ic_release_to_cancel@3x.png in Resources */, 203 | 6FFBAA3E1EB1C50700988ED3 /* Assets.xcassets in Resources */, 204 | 6FFBAA531EB1C53000988ED3 /* ic_record_too_short@3x.png in Resources */, 205 | 6FFBAA3C1EB1C50700988ED3 /* Main.storyboard in Resources */, 206 | 6FFBAA541EB1C53000988ED3 /* ic_record@2x.png in Resources */, 207 | 6FFBAA561EB1C53000988ED3 /* ic_release_to_cancel@2x.png in Resources */, 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | /* End PBXResourcesBuildPhase section */ 212 | 213 | /* Begin PBXSourcesBuildPhase section */ 214 | 6FFBAA2A1EB1C50700988ED3 /* Sources */ = { 215 | isa = PBXSourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | 6FFBAA6A1EB1D4D200988ED3 /* MOKORecordShowManager.m in Sources */, 219 | 6FFBAA601EB1C73E00988ED3 /* MOKORecordPowerAnimationView.m in Sources */, 220 | 6FFBAA391EB1C50700988ED3 /* ViewController.m in Sources */, 221 | 6FFBAA361EB1C50700988ED3 /* AppDelegate.m in Sources */, 222 | 6FFBAA5D1EB1C69A00988ED3 /* MOKORecorderTool.m in Sources */, 223 | 6FFBAA331EB1C50700988ED3 /* main.m in Sources */, 224 | 6FFBAA631EB1C83500988ED3 /* MOKORecordToastContentView.m in Sources */, 225 | 6FFBAA661EB1D2BE00988ED3 /* MOKORecordView.m in Sources */, 226 | 6FFBAA5A1EB1C55B00988ED3 /* MOKORecordButton.m in Sources */, 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | }; 230 | /* End PBXSourcesBuildPhase section */ 231 | 232 | /* Begin PBXVariantGroup section */ 233 | 6FFBAA3A1EB1C50700988ED3 /* Main.storyboard */ = { 234 | isa = PBXVariantGroup; 235 | children = ( 236 | 6FFBAA3B1EB1C50700988ED3 /* Base */, 237 | ); 238 | name = Main.storyboard; 239 | sourceTree = ""; 240 | }; 241 | 6FFBAA3F1EB1C50700988ED3 /* LaunchScreen.storyboard */ = { 242 | isa = PBXVariantGroup; 243 | children = ( 244 | 6FFBAA401EB1C50700988ED3 /* Base */, 245 | ); 246 | name = LaunchScreen.storyboard; 247 | sourceTree = ""; 248 | }; 249 | /* End PBXVariantGroup section */ 250 | 251 | /* Begin XCBuildConfiguration section */ 252 | 6FFBAA431EB1C50700988ED3 /* Debug */ = { 253 | isa = XCBuildConfiguration; 254 | buildSettings = { 255 | ALWAYS_SEARCH_USER_PATHS = NO; 256 | CLANG_ANALYZER_NONNULL = YES; 257 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 258 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 259 | CLANG_CXX_LIBRARY = "libc++"; 260 | CLANG_ENABLE_MODULES = YES; 261 | CLANG_ENABLE_OBJC_ARC = YES; 262 | CLANG_WARN_BOOL_CONVERSION = YES; 263 | CLANG_WARN_CONSTANT_CONVERSION = YES; 264 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 265 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 266 | CLANG_WARN_EMPTY_BODY = YES; 267 | CLANG_WARN_ENUM_CONVERSION = YES; 268 | CLANG_WARN_INFINITE_RECURSION = YES; 269 | CLANG_WARN_INT_CONVERSION = YES; 270 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 271 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 272 | CLANG_WARN_UNREACHABLE_CODE = YES; 273 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 274 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 275 | COPY_PHASE_STRIP = NO; 276 | DEBUG_INFORMATION_FORMAT = dwarf; 277 | ENABLE_STRICT_OBJC_MSGSEND = YES; 278 | ENABLE_TESTABILITY = YES; 279 | GCC_C_LANGUAGE_STANDARD = gnu99; 280 | GCC_DYNAMIC_NO_PIC = NO; 281 | GCC_NO_COMMON_BLOCKS = YES; 282 | GCC_OPTIMIZATION_LEVEL = 0; 283 | GCC_PREPROCESSOR_DEFINITIONS = ( 284 | "DEBUG=1", 285 | "$(inherited)", 286 | ); 287 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 288 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 289 | GCC_WARN_UNDECLARED_SELECTOR = YES; 290 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 291 | GCC_WARN_UNUSED_FUNCTION = YES; 292 | GCC_WARN_UNUSED_VARIABLE = YES; 293 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 294 | MTL_ENABLE_DEBUG_INFO = YES; 295 | ONLY_ACTIVE_ARCH = YES; 296 | SDKROOT = iphoneos; 297 | TARGETED_DEVICE_FAMILY = "1,2"; 298 | }; 299 | name = Debug; 300 | }; 301 | 6FFBAA441EB1C50700988ED3 /* Release */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ALWAYS_SEARCH_USER_PATHS = NO; 305 | CLANG_ANALYZER_NONNULL = YES; 306 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 308 | CLANG_CXX_LIBRARY = "libc++"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_WARN_BOOL_CONVERSION = YES; 312 | CLANG_WARN_CONSTANT_CONVERSION = YES; 313 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 314 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 315 | CLANG_WARN_EMPTY_BODY = YES; 316 | CLANG_WARN_ENUM_CONVERSION = YES; 317 | CLANG_WARN_INFINITE_RECURSION = YES; 318 | CLANG_WARN_INT_CONVERSION = YES; 319 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 320 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 321 | CLANG_WARN_UNREACHABLE_CODE = YES; 322 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 323 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 324 | COPY_PHASE_STRIP = NO; 325 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 326 | ENABLE_NS_ASSERTIONS = NO; 327 | ENABLE_STRICT_OBJC_MSGSEND = YES; 328 | GCC_C_LANGUAGE_STANDARD = gnu99; 329 | GCC_NO_COMMON_BLOCKS = YES; 330 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 331 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 332 | GCC_WARN_UNDECLARED_SELECTOR = YES; 333 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 334 | GCC_WARN_UNUSED_FUNCTION = YES; 335 | GCC_WARN_UNUSED_VARIABLE = YES; 336 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 337 | MTL_ENABLE_DEBUG_INFO = NO; 338 | SDKROOT = iphoneos; 339 | TARGETED_DEVICE_FAMILY = "1,2"; 340 | VALIDATE_PRODUCT = YES; 341 | }; 342 | name = Release; 343 | }; 344 | 6FFBAA461EB1C50700988ED3 /* Debug */ = { 345 | isa = XCBuildConfiguration; 346 | buildSettings = { 347 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 348 | DEVELOPMENT_TEAM = TJ528WBL3B; 349 | INFOPLIST_FILE = MOKORecorder/Info.plist; 350 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 351 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 352 | PRODUCT_BUNDLE_IDENTIFIER = Home.MOKORecorder; 353 | PRODUCT_NAME = "$(TARGET_NAME)"; 354 | TARGETED_DEVICE_FAMILY = 1; 355 | }; 356 | name = Debug; 357 | }; 358 | 6FFBAA471EB1C50700988ED3 /* Release */ = { 359 | isa = XCBuildConfiguration; 360 | buildSettings = { 361 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 362 | DEVELOPMENT_TEAM = TJ528WBL3B; 363 | INFOPLIST_FILE = MOKORecorder/Info.plist; 364 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 365 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 366 | PRODUCT_BUNDLE_IDENTIFIER = Home.MOKORecorder; 367 | PRODUCT_NAME = "$(TARGET_NAME)"; 368 | TARGETED_DEVICE_FAMILY = 1; 369 | }; 370 | name = Release; 371 | }; 372 | /* End XCBuildConfiguration section */ 373 | 374 | /* Begin XCConfigurationList section */ 375 | 6FFBAA291EB1C50700988ED3 /* Build configuration list for PBXProject "MOKORecorder" */ = { 376 | isa = XCConfigurationList; 377 | buildConfigurations = ( 378 | 6FFBAA431EB1C50700988ED3 /* Debug */, 379 | 6FFBAA441EB1C50700988ED3 /* Release */, 380 | ); 381 | defaultConfigurationIsVisible = 0; 382 | defaultConfigurationName = Release; 383 | }; 384 | 6FFBAA451EB1C50700988ED3 /* Build configuration list for PBXNativeTarget "MOKORecorder" */ = { 385 | isa = XCConfigurationList; 386 | buildConfigurations = ( 387 | 6FFBAA461EB1C50700988ED3 /* Debug */, 388 | 6FFBAA471EB1C50700988ED3 /* Release */, 389 | ); 390 | defaultConfigurationIsVisible = 0; 391 | defaultConfigurationName = Release; 392 | }; 393 | /* End XCConfigurationList section */ 394 | }; 395 | rootObject = 6FFBAA261EB1C50700988ED3 /* Project object */; 396 | } 397 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder.xcodeproj/xcuserdata/spring.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder.xcodeproj/xcuserdata/spring.xcuserdatad/xcschemes/MOKORecorder.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSMicrophoneUsageDescription 24 | 是否允许访问您的麦克风? 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/MOKORecordButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // MOKORecorderButton.h 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class MOKORecordButton; 12 | typedef void (^RecordTouchDown) (MOKORecordButton *recordButton); 13 | typedef void (^RecordTouchUpOutside) (MOKORecordButton *recordButton); 14 | typedef void (^RecordTouchUpInside) (MOKORecordButton *recordButton); 15 | typedef void (^RecordTouchDragEnter) (MOKORecordButton *recordButton); 16 | typedef void (^RecordTouchDragInside) (MOKORecordButton *recordButton); 17 | typedef void (^RecordTouchDragOutside) (MOKORecordButton *recordButton); 18 | typedef void (^RecordTouchDragExit) (MOKORecordButton *recordButton); 19 | 20 | @interface MOKORecordButton : UIButton 21 | @property (nonatomic, copy) RecordTouchDown recordTouchDownAction; 22 | @property (nonatomic, copy) RecordTouchUpOutside recordTouchUpOutsideAction; 23 | @property (nonatomic, copy) RecordTouchUpInside recordTouchUpInsideAction; 24 | @property (nonatomic, copy) RecordTouchDragEnter recordTouchDragEnterAction; 25 | @property (nonatomic, copy) RecordTouchDragInside recordTouchDragInsideAction; 26 | @property (nonatomic, copy) RecordTouchDragOutside recordTouchDragOutsideAction; 27 | @property (nonatomic, copy) RecordTouchDragExit recordTouchDragExitAction; 28 | 29 | - (void)setButtonStateWithRecording; 30 | - (void)setButtonStateWithNormal; 31 | @end 32 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/MOKORecordButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // MOKORecorderButton.m 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import "MOKORecordButton.h" 10 | 11 | @implementation MOKORecordButton 12 | 13 | - (instancetype)initWithFrame:(CGRect)frame 14 | { 15 | self = [super initWithFrame:frame]; 16 | if (self) { 17 | [self addTarget:self action:@selector(recordTouchDown) forControlEvents:UIControlEventTouchDown]; 18 | [self addTarget:self action:@selector(recordTouchUpOutside) forControlEvents:UIControlEventTouchUpOutside]; 19 | [self addTarget:self action:@selector(recordTouchUpInside) forControlEvents:UIControlEventTouchUpInside]; 20 | [self addTarget:self action:@selector(recordTouchDragEnter) forControlEvents:UIControlEventTouchDragEnter]; 21 | [self addTarget:self action:@selector(recordTouchDragInside) forControlEvents:UIControlEventTouchDragInside]; 22 | [self addTarget:self action:@selector(recordTouchDragOutside) forControlEvents:UIControlEventTouchDragOutside]; 23 | [self addTarget:self action:@selector(recordTouchDragExit) forControlEvents:UIControlEventTouchDragExit]; 24 | } 25 | return self; 26 | } 27 | 28 | - (void)setButtonStateWithRecording 29 | { 30 | self.backgroundColor = [UIColor lightGrayColor]; 31 | [self setTitle:@"松开 结束" forState:UIControlStateNormal]; 32 | } 33 | 34 | - (void)setButtonStateWithNormal 35 | { 36 | self.backgroundColor = [UIColor whiteColor]; 37 | [self setTitle:@"按住 说话" forState:UIControlStateNormal]; 38 | } 39 | 40 | #pragma mark -- 事件方法回调 41 | - (void)recordTouchDown 42 | { 43 | if (self.recordTouchDownAction) { 44 | self.recordTouchDownAction(self); 45 | } 46 | } 47 | 48 | - (void)recordTouchUpOutside 49 | { 50 | if (self.recordTouchUpOutsideAction) { 51 | self.recordTouchUpOutsideAction(self); 52 | } 53 | } 54 | 55 | - (void)recordTouchUpInside 56 | { 57 | if (self.recordTouchUpInsideAction) { 58 | self.recordTouchUpInsideAction(self); 59 | } 60 | } 61 | 62 | - (void)recordTouchDragEnter 63 | { 64 | if (self.recordTouchDragEnterAction) { 65 | self.recordTouchDragEnterAction(self); 66 | } 67 | } 68 | 69 | - (void)recordTouchDragInside 70 | { 71 | if (self.recordTouchDragInsideAction) { 72 | self.recordTouchDragInsideAction(self); 73 | } 74 | } 75 | 76 | - (void)recordTouchDragOutside 77 | { 78 | if (self.recordTouchDragOutsideAction) { 79 | self.recordTouchDragOutsideAction(self); 80 | } 81 | } 82 | 83 | - (void)recordTouchDragExit 84 | { 85 | if (self.recordTouchDragExitAction) { 86 | self.recordTouchDragExitAction(self); 87 | } 88 | } 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/MOKORecordHeaderDefine.h: -------------------------------------------------------------------------------- 1 | // 2 | // MOKORecordHeaderDefine.h 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #ifndef MOKORecordHeaderDefine_h 10 | #define MOKORecordHeaderDefine_h 11 | 12 | typedef NS_ENUM(NSInteger, MOKORecordState) 13 | { 14 | MOKORecordState_Normal, //初始状态 15 | MOKORecordState_Recording, //正在录音 16 | MOKORecordState_ReleaseToCancel, //上滑取消(也在录音状态,UI显示有区别) 17 | MOKORecordState_RecordCounting, //最后10s倒计时(也在录音状态,UI显示有区别) 18 | MOKORecordState_RecordTooShort, //录音时间太短(录音结束了) 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/MOKORecordPowerAnimationView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MOKORecordPowerAnimationView.h 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 声音波动视图 8 | 9 | #import 10 | 11 | @interface MOKORecordPowerAnimationView : UIView 12 | 13 | - (void)updateWithPower:(float)power; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/MOKORecordPowerAnimationView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MOKORecordPowerAnimationView.m 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import "MOKORecordPowerAnimationView.h" 10 | @interface MOKORecordPowerAnimationView() 11 | @property (nonatomic, strong) UIImageView *imgContent; 12 | @property (nonatomic, strong) CAShapeLayer *maskLayer; 13 | @end 14 | @implementation MOKORecordPowerAnimationView 15 | 16 | - (instancetype)initWithFrame:(CGRect)frame 17 | { 18 | if(self = [super initWithFrame:frame]) 19 | { 20 | self.imgContent = [[UIImageView alloc] init]; 21 | self.imgContent.image = [UIImage imageNamed:@"ic_record_ripple"]; 22 | [self addSubview:self.imgContent]; 23 | 24 | self.maskLayer = [[CAShapeLayer alloc] init]; 25 | self.maskLayer.backgroundColor = [UIColor blackColor].CGColor; 26 | } 27 | return self; 28 | } 29 | - (void)layoutSubviews 30 | { 31 | [super layoutSubviews]; 32 | self.imgContent.frame = self.bounds; 33 | } 34 | - (void)updateWithPower:(float)power 35 | { 36 | int viewCount = ceil(fabs(power) * 10); 37 | if (viewCount == 0) { 38 | viewCount++; 39 | } 40 | if (viewCount >= 9) { 41 | viewCount = 9; 42 | } 43 | 44 | CGFloat itemHeight = 5.5; 45 | 46 | CGFloat maskPadding = itemHeight * viewCount; 47 | 48 | //从下面往上画线 49 | UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, self.imgContent.frame.size.height, self.imgContent.frame.size.width, -maskPadding)]; 50 | 51 | self.maskLayer.path = path.CGPath; 52 | self.imgContent.layer.mask = self.maskLayer; 53 | } 54 | @end 55 | 56 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/MOKORecordShowManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // MOKORecordShowManager.h 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MOKORecordHeaderDefine.h" 11 | @interface MOKORecordShowManager : NSObject 12 | - (void)updateUIWithRecordState:(MOKORecordState)state; 13 | - (void)showToast:(NSString *)message; 14 | - (void)updatePower:(float)power; 15 | - (void)showRecordCounting:(float)remainTime; 16 | @end 17 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/MOKORecordShowManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // MOKORecordShowManager.m 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import "MOKORecordShowManager.h" 10 | #import "MOKORecordView.h" 11 | #import "MOKORecordToastContentView.h" 12 | 13 | @interface MOKORecordShowManager() 14 | @property (nonatomic, strong) MOKORecordView *voiceRecordView; 15 | @property (nonatomic, strong) MOKORecordTipView *tipView; 16 | @end 17 | 18 | @implementation MOKORecordShowManager 19 | 20 | - (void)updatePower:(float)power 21 | { 22 | [self.voiceRecordView updatePower:power]; 23 | } 24 | 25 | - (void)showRecordCounting:(float)remainTime 26 | { 27 | [self.voiceRecordView updateWithRemainTime:remainTime]; 28 | } 29 | 30 | - (void)showToast:(NSString *)message 31 | { 32 | if (self.tipView.superview == nil) { 33 | [[UIApplication sharedApplication].keyWindow addSubview:self.tipView]; 34 | [self.tipView showWithMessage:message]; 35 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 36 | [self.tipView removeFromSuperview]; 37 | }); 38 | } 39 | } 40 | 41 | - (void)updateUIWithRecordState:(MOKORecordState)state 42 | { 43 | if (state == MOKORecordState_Normal) { 44 | if (self.voiceRecordView.superview) { 45 | [self.voiceRecordView removeFromSuperview]; 46 | } 47 | return; 48 | } 49 | 50 | if (self.voiceRecordView.superview == nil) { 51 | [[UIApplication sharedApplication].keyWindow addSubview:self.voiceRecordView]; 52 | } 53 | 54 | [self.voiceRecordView updateUIWithRecordState:state]; 55 | } 56 | 57 | - (MOKORecordView *)voiceRecordView 58 | { 59 | if (_voiceRecordView == nil) { 60 | _voiceRecordView = [MOKORecordView new]; 61 | _voiceRecordView.frame = CGRectMake(0, 0, 150, 150); 62 | _voiceRecordView.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/2); 63 | } 64 | return _voiceRecordView; 65 | } 66 | 67 | - (MOKORecordTipView *)tipView 68 | { 69 | if (_tipView == nil) { 70 | _tipView = [MOKORecordTipView new]; 71 | _tipView.frame = CGRectMake(0, 0, 150, 150); 72 | _tipView.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/2); 73 | } 74 | return _tipView; 75 | } 76 | 77 | @end 78 | 79 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/MOKORecordToastContentView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MOKORecordToastContentView.h 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MOKORecordToastContentView : UIView 12 | 13 | @end 14 | 15 | //----------------------------------------// 16 | @interface MOKORecordingView : MOKORecordToastContentView 17 | 18 | - (void)updateWithPower:(float)power; 19 | 20 | @end 21 | 22 | //----------------------------------------// 23 | @interface MOKORecordReleaseToCancelView : MOKORecordToastContentView 24 | 25 | 26 | @end 27 | 28 | //----------------------------------------// 29 | 30 | @interface MOKORecordCountingView : MOKORecordToastContentView 31 | 32 | - (void)updateWithRemainTime:(float)remainTime; 33 | 34 | @end 35 | 36 | //----------------------------------------// 37 | @interface MOKORecordTipView : MOKORecordToastContentView 38 | 39 | - (void)showWithMessage:(NSString *)msg; 40 | 41 | @end 42 | 43 | 44 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/MOKORecordToastContentView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MOKORecordToastContentView.m 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import "MOKORecordToastContentView.h" 10 | #import "MOKORecordPowerAnimationView.h" 11 | 12 | @implementation MOKORecordToastContentView 13 | 14 | @end 15 | 16 | //----------------------------------------// 17 | @interface MOKORecordingView () 18 | @property (nonatomic, strong) UIImageView *imgRecord; 19 | @property (nonatomic, strong) UILabel *lbContent; 20 | @property (nonatomic, strong) MOKORecordPowerAnimationView *powerView; 21 | @end 22 | 23 | @implementation MOKORecordingView 24 | 25 | - (instancetype)initWithFrame:(CGRect)frame 26 | { 27 | if (self = [super initWithFrame:frame]) { 28 | [self setUp]; 29 | } 30 | return self; 31 | } 32 | 33 | - (void)setUp 34 | { 35 | self.lbContent = [[UILabel alloc] init]; 36 | self.lbContent.text = @"手指上滑 取消发送"; 37 | self.lbContent.textColor = [UIColor whiteColor]; 38 | self.lbContent.textAlignment = NSTextAlignmentCenter; 39 | self.lbContent.font = [UIFont systemFontOfSize:14]; 40 | [self addSubview:self.lbContent]; 41 | 42 | self.imgRecord = [[UIImageView alloc] init]; 43 | self.imgRecord.image = [UIImage imageNamed:@"ic_record"]; 44 | [self addSubview:self.imgRecord]; 45 | 46 | self.powerView = [[MOKORecordPowerAnimationView alloc] init]; 47 | [self addSubview:self.powerView]; 48 | 49 | //默认显示一格音量 50 | [self.powerView updateWithPower:0]; 51 | } 52 | 53 | - (void)updateWithPower:(float)power 54 | { 55 | [self.powerView updateWithPower:power]; 56 | } 57 | - (void)layoutSubviews 58 | { 59 | [super layoutSubviews]; 60 | 61 | [self.lbContent sizeToFit]; 62 | self.lbContent.frame = CGRectMake(0, self.frame.size.height - self.lbContent.frame.size.height - 12, self.frame.size.width, self.lbContent.frame.size.height); 63 | 64 | self.imgRecord.frame = CGRectMake(40, 30, self.imgRecord.image.size.width, self.imgRecord.image.size.height); 65 | 66 | self.powerView.frame = CGRectMake(self.imgRecord.frame.origin.x+self.imgRecord.frame.size.width + 6, self.imgRecord.frame.origin.y+self.imgRecord.frame.size.height-56, 29, 54); 67 | } 68 | @end 69 | 70 | //----------------------------------------// 71 | @interface MOKORecordReleaseToCancelView () 72 | @property (nonatomic, strong) UIImageView *imgRelease; 73 | @property (nonatomic, strong) UILabel *lbContent; 74 | @end 75 | 76 | @implementation MOKORecordReleaseToCancelView 77 | 78 | - (instancetype)initWithFrame:(CGRect)frame 79 | { 80 | if (self = [super initWithFrame:frame]) 81 | { 82 | [self setUp]; 83 | } 84 | return self; 85 | } 86 | 87 | - (void)setUp 88 | { 89 | self.lbContent = [[UILabel alloc] init]; 90 | self.lbContent.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.5]; 91 | self.lbContent.text = @"松开手指 取消发送"; 92 | self.lbContent.textColor = [UIColor whiteColor]; 93 | self.lbContent.textAlignment = NSTextAlignmentCenter; 94 | self.lbContent.font = [UIFont boldSystemFontOfSize:14]; 95 | [self addSubview:self.lbContent]; 96 | 97 | self.imgRelease = [[UIImageView alloc] init]; 98 | self.imgRelease.image = [UIImage imageNamed:@"ic_release_to_cancel"]; 99 | [self addSubview:self.imgRelease]; 100 | } 101 | - (void)layoutSubviews 102 | { 103 | [super layoutSubviews]; 104 | 105 | self.imgRelease.frame = CGRectMake((self.frame.size.width - self.imgRelease.image.size.width) * 0.5, 30, self.imgRelease.image.size.width, self.imgRelease.image.size.height); 106 | 107 | self.lbContent.frame = CGRectMake(6, self.frame.size.height - 25 - 7, self.frame.size.width - 12, 25); 108 | 109 | self.lbContent.layer.cornerRadius = 2; 110 | self.lbContent.clipsToBounds = YES; 111 | } 112 | @end 113 | 114 | //----------------------------------------// 115 | @interface MOKORecordCountingView () 116 | @property (nonatomic, strong) UILabel *lbContent; 117 | @property (nonatomic, strong) UILabel *lbRemainTime; 118 | @end 119 | 120 | @implementation MOKORecordCountingView 121 | 122 | - (instancetype)initWithFrame:(CGRect)frame 123 | { 124 | if (self = [super initWithFrame:frame]) { 125 | [self setUp]; 126 | } 127 | return self; 128 | } 129 | 130 | - (void)setUp 131 | { 132 | self.lbContent = [[UILabel alloc] init]; 133 | // _lbContent.backgroundColor = [UIColor colorWithHex:0xA52E2C]; 134 | self.lbContent.text = @"松开手指 取消发送"; 135 | self.lbContent.textColor = [UIColor whiteColor]; 136 | self.lbContent.textAlignment = NSTextAlignmentCenter; 137 | self.lbContent.font = [UIFont boldSystemFontOfSize:14]; 138 | [self addSubview:self.lbContent]; 139 | 140 | self.lbRemainTime = [[UILabel alloc] init]; 141 | self.lbRemainTime.font = [UIFont boldSystemFontOfSize:80]; 142 | self.lbRemainTime.textColor = [UIColor whiteColor]; 143 | [self addSubview:self.lbRemainTime]; 144 | } 145 | 146 | - (void)layoutSubviews 147 | { 148 | [super layoutSubviews]; 149 | 150 | self.lbContent.frame = CGRectMake(6, self.frame.size.height - 25 - 7, self.frame.size.width - 12, 25); 151 | self.lbContent.layer.cornerRadius = 2; 152 | self.lbContent.clipsToBounds = YES; 153 | 154 | [self.lbRemainTime sizeToFit]; 155 | self.lbRemainTime.frame = CGRectMake((self.frame.size.width - self.lbRemainTime.frame.size.width) * 0.5, 16, self.lbRemainTime.frame.size.width, 95); 156 | } 157 | - (void)updateWithRemainTime:(float)remainTime 158 | { 159 | self.lbRemainTime.text = [NSString stringWithFormat:@"%d",(int)remainTime]; 160 | [self setNeedsLayout]; 161 | } 162 | 163 | @end 164 | 165 | //----------------------------------------// 166 | @interface MOKORecordTipView () 167 | @property (nonatomic, strong) UIImageView *imgIcon; 168 | @property (nonatomic, strong) UILabel *lbContent; 169 | @end 170 | 171 | @implementation MOKORecordTipView 172 | - (instancetype)initWithFrame:(CGRect)frame 173 | { 174 | if (self = [super initWithFrame:frame]) 175 | { 176 | [self setUp]; 177 | } 178 | return self; 179 | } 180 | 181 | - (void)setUp 182 | { 183 | self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.6]; 184 | 185 | self.lbContent = [[UILabel alloc] init]; 186 | self.lbContent.textColor = [UIColor whiteColor]; 187 | self.lbContent.textAlignment = NSTextAlignmentCenter; 188 | self.lbContent.font = [UIFont systemFontOfSize:14]; 189 | self.lbContent.text = @"说话时间太短"; 190 | [self addSubview:self.lbContent]; 191 | 192 | self.imgIcon = [[UIImageView alloc] init]; 193 | self.imgIcon.image = [UIImage imageNamed:@"ic_record_too_short"]; 194 | [self addSubview:self.imgIcon]; 195 | } 196 | - (void)layoutSubviews 197 | { 198 | [super layoutSubviews]; 199 | 200 | self.layer.cornerRadius = 6; 201 | self.clipsToBounds = YES; 202 | 203 | self.imgIcon.frame = CGRectMake((self.frame.size.width - self.imgIcon.image.size.width) * 0.5, 32, self.imgIcon.image.size.width, self.imgIcon.image.size.height); 204 | 205 | self.lbContent.frame = CGRectMake(0, self.frame.size.height - 25 - 7, self.frame.size.width, 25); 206 | } 207 | - (void)showWithMessage:(NSString *)msg 208 | { 209 | self.lbContent.text = msg; 210 | [self setNeedsLayout]; 211 | } 212 | @end 213 | 214 | 215 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/MOKORecordView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MOKOVoiceRecordView.h 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MOKORecordHeaderDefine.h" 11 | @interface MOKORecordView : UIView 12 | 13 | - (void)updateUIWithRecordState:(MOKORecordState)state; 14 | - (void)updatePower:(float)power; 15 | - (void)updateWithRemainTime:(float)remainTime; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/MOKORecordView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MOKOVoiceRecordView.m 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import "MOKORecordView.h" 10 | #import "MOKORecordToastContentView.h" 11 | @interface MOKORecordView () 12 | @property (nonatomic, strong) MOKORecordingView *recodingView; 13 | @property (nonatomic, strong) MOKORecordReleaseToCancelView *releaseToCancelView; 14 | @property (nonatomic, strong) MOKORecordCountingView *countingView; 15 | @property (nonatomic, assign) MOKORecordState currentState; 16 | @end 17 | 18 | @implementation MOKORecordView 19 | - (instancetype)initWithFrame:(CGRect)frame 20 | { 21 | if (self = [super initWithFrame:frame]) 22 | { 23 | [self setUp]; 24 | } 25 | return self; 26 | } 27 | 28 | - (void)setUp 29 | { 30 | self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.6]; 31 | 32 | self.recodingView = [[MOKORecordingView alloc] init]; 33 | [self addSubview:self.recodingView]; 34 | self.recodingView.hidden = YES; 35 | 36 | self.releaseToCancelView = [[MOKORecordReleaseToCancelView alloc] init]; 37 | [self addSubview:self.releaseToCancelView]; 38 | self.releaseToCancelView.hidden = YES; 39 | 40 | self.countingView = [[MOKORecordCountingView alloc] init]; 41 | [self addSubview:self.countingView]; 42 | self.countingView.hidden = YES; 43 | } 44 | 45 | - (void)layoutSubviews 46 | { 47 | [super layoutSubviews]; 48 | self.layer.cornerRadius = 6; 49 | self.clipsToBounds = YES; 50 | self.recodingView.frame = self.bounds; 51 | self.releaseToCancelView.frame = self.bounds; 52 | self.countingView.frame = self.bounds; 53 | } 54 | 55 | - (void)updatePower:(float)power 56 | { 57 | if (self.currentState != MOKORecordState_Recording) { 58 | return; 59 | } 60 | [self.recodingView updateWithPower:power]; 61 | } 62 | 63 | - (void)updateWithRemainTime:(float)remainTime 64 | { 65 | if (self.currentState != MOKORecordState_RecordCounting || self.releaseToCancelView.hidden == false) { 66 | return; 67 | } 68 | [self.countingView updateWithRemainTime:remainTime]; 69 | } 70 | 71 | - (void)updateUIWithRecordState:(MOKORecordState)state 72 | { 73 | self.currentState = state; 74 | if (state == MOKORecordState_Normal) { 75 | self.recodingView.hidden = YES; 76 | self.releaseToCancelView.hidden = YES; 77 | self.countingView.hidden = YES; 78 | } 79 | else if (state == MOKORecordState_Recording) 80 | { 81 | self.recodingView.hidden = NO; 82 | self.releaseToCancelView.hidden = YES; 83 | self.countingView.hidden = YES; 84 | } 85 | else if (state == MOKORecordState_ReleaseToCancel) 86 | { 87 | self.recodingView.hidden = YES; 88 | self.releaseToCancelView.hidden = NO; 89 | self.countingView.hidden = YES; 90 | } 91 | else if (state == MOKORecordState_RecordCounting) 92 | { 93 | self.recodingView.hidden = YES; 94 | self.releaseToCancelView.hidden = YES; 95 | self.countingView.hidden = NO; 96 | } 97 | else if (state == MOKORecordState_RecordTooShort) 98 | { 99 | self.recodingView.hidden = YES; 100 | self.releaseToCancelView.hidden = YES; 101 | self.countingView.hidden = YES; 102 | } 103 | } 104 | @end 105 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/MOKORecorderTool.h: -------------------------------------------------------------------------------- 1 | // 2 | // MOKOSecretTrainRecorder.h 3 | // MOKORecord 4 | // 5 | // Created by Spring on 2017/4/26. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 新建密训录音播放工具 8 | 9 | #import 10 | #import 11 | 12 | @class MOKORecorderTool; 13 | @protocol MOKOSecretTrainRecorderDelegate 14 | 15 | @optional 16 | - (void)recorder:(MOKORecorderTool *)recorder didstartRecoring:(int)no; 17 | - (void)recordToolDidFinishPlay:(MOKORecorderTool *)recorder; 18 | @end 19 | @interface MOKORecorderTool : NSObject 20 | 21 | //录音工具的单例 22 | + (instancetype)sharedRecorder; 23 | 24 | //开始录音 25 | - (void)startRecording; 26 | 27 | //停止录音 28 | - (void)stopRecording; 29 | 30 | //播放录音文件 31 | - (void)playRecordingFile; 32 | 33 | //停止播放录音文件 34 | - (void)stopPlaying; 35 | 36 | //销毁录音文件 37 | - (void)destructionRecordingFile; 38 | 39 | //录音对象 40 | @property (nonatomic, strong) AVAudioRecorder *recorder; 41 | 42 | //播放器对象 43 | @property (nonatomic, strong) AVAudioPlayer *player; 44 | 45 | //更新图片的代理 46 | @property (nonatomic, assign) id delegate; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/MOKORecorderTool.m: -------------------------------------------------------------------------------- 1 | // 2 | // MOKOSecretTrainRecorder.m 3 | // MOKORecord 4 | // 5 | // Created by Spring on 2017/4/26. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import "MOKORecorderTool.h" 10 | 11 | #define MOKOSecretTrainRecordFielName @"lvRecord.m4a" 12 | 13 | @interface MOKORecorderTool() 14 | //录音文件地址 15 | @property (nonatomic, strong) NSURL *recordFileUrl; 16 | 17 | @property (nonatomic, strong) AVAudioSession *session; 18 | 19 | @end 20 | 21 | @implementation MOKORecorderTool 22 | 23 | static MOKORecorderTool *instance = nil; 24 | #pragma mark - 单例 25 | + (instancetype)sharedRecorder 26 | { 27 | static dispatch_once_t onceToken; 28 | dispatch_once(&onceToken, ^{ 29 | if (instance == nil) { 30 | instance = [[self alloc] init]; 31 | } 32 | }); 33 | return instance; 34 | } 35 | 36 | - (void)startRecording 37 | { 38 | // 录音时停止播放 删除曾经生成的文件 39 | [self stopPlaying]; 40 | [self destructionRecordingFile]; 41 | // 真机环境下需要的代码 42 | self.session = [AVAudioSession sharedInstance]; 43 | [self.session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; 44 | [self.recorder record]; 45 | } 46 | 47 | - (void)updateImage 48 | { 49 | [self.recorder updateMeters]; 50 | 51 | double lowPassResults = pow(10, (0.05 * [self.recorder peakPowerForChannel:0])); 52 | float result = 10 * (float)lowPassResults; 53 | //NSLog(@"%f", result); 54 | int no = 0; 55 | if (result > 0 && result <= 1.3) { 56 | no = 1; 57 | } else if (result > 1.3 && result <= 2) { 58 | no = 2; 59 | } else if (result > 2 && result <= 3.0) { 60 | no = 3; 61 | } else if (result > 3.0 && result <= 3.0) { 62 | no = 4; 63 | } else if (result > 5.0 && result <= 10) { 64 | no = 5; 65 | } else if (result > 10 && result <= 40) { 66 | no = 6; 67 | } else if (result > 40) { 68 | no = 7; 69 | } 70 | if ([self.delegate respondsToSelector:@selector(recorder:didstartRecoring:)]) 71 | { 72 | [self.delegate recorder:self didstartRecoring: no]; 73 | } 74 | else 75 | { 76 | 77 | } 78 | } 79 | - (void)stopRecording 80 | { 81 | if ([self.recorder isRecording]) 82 | { 83 | [self.recorder stop]; 84 | } 85 | } 86 | - (void)playRecordingFile 87 | { 88 | [self.recorder stop];// 播放时停止录音 89 | // 正在播放就返回 90 | if ([self.player isPlaying]) 91 | { 92 | return; 93 | } 94 | NSError * error; 95 | self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:self.recordFileUrl error:&error]; 96 | self.player.delegate = self; 97 | [self.session setCategory:AVAudioSessionCategoryPlayback error:nil]; 98 | [self.player play]; 99 | } 100 | 101 | - (void)stopPlaying 102 | { 103 | [self.player stop]; 104 | } 105 | 106 | #pragma mark - 懒加载 107 | - (AVAudioRecorder *)recorder { 108 | if (!_recorder) { 109 | // 1.获取沙盒地址 110 | NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; 111 | NSString *filePath = [path stringByAppendingPathComponent:MOKOSecretTrainRecordFielName]; 112 | self.recordFileUrl = [NSURL fileURLWithPath:filePath]; 113 | NSLog(@"%@", filePath); 114 | 115 | // 3.设置录音的一些参数 116 | NSMutableDictionary *setting = [NSMutableDictionary dictionary]; 117 | // 音频格式 118 | setting[AVFormatIDKey] = @(kAudioFormatMPEG4AAC); 119 | // 录音采样率(Hz) 如:AVSampleRateKey==8000/44100/96000(影响音频的质量) 120 | setting[AVSampleRateKey] = @(16000.0); 121 | // 音频通道数 1 或 2 122 | setting[AVNumberOfChannelsKey] = @(1); 123 | // 线性音频的位深度 8、16、24、32 124 | setting[AVLinearPCMBitDepthKey] = @(16); 125 | //录音的质量 126 | setting[AVEncoderAudioQualityKey] = [NSNumber numberWithInt:AVAudioQualityHigh]; 127 | 128 | _recorder = [[AVAudioRecorder alloc] initWithURL:self.recordFileUrl settings:setting error:NULL]; 129 | _recorder.delegate = self; 130 | _recorder.meteringEnabled = YES; 131 | 132 | [_recorder prepareToRecord]; 133 | } 134 | return _recorder; 135 | } 136 | 137 | - (void)destructionRecordingFile 138 | { 139 | NSFileManager *fileManager = [NSFileManager defaultManager]; 140 | if (self.recordFileUrl) 141 | { 142 | [fileManager removeItemAtURL:self.recordFileUrl error:NULL]; 143 | } 144 | } 145 | 146 | #pragma mark - AVAudioRecorderDelegate 147 | - (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag 148 | { 149 | //录音结束 150 | } 151 | #pragma mark - AVAudioPlayerDelegate 152 | - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag 153 | { 154 | //录音播放结束 155 | if ([self.delegate respondsToSelector:@selector(recordToolDidFinishPlay:)]) 156 | { 157 | [self.delegate recordToolDidFinishPlay:self]; 158 | } 159 | } 160 | 161 | @end 162 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "MOKORecorderTool.h" 11 | #import "MOKORecordShowManager.h" 12 | #import "MOKORecordButton.h" 13 | 14 | #define kFakeTimerDuration 1 15 | #define kMaxRecordDuration 60 //最长录音时长 16 | #define kRemainCountingDuration 10 //剩余多少秒开始倒计时 17 | 18 | @interface ViewController () 19 | @property (nonatomic, strong) MOKORecordShowManager *voiceRecordCtrl; 20 | @property (nonatomic, assign) MOKORecordState currentRecordState; 21 | @property (nonatomic, strong) NSTimer *fakeTimer; 22 | @property (nonatomic, assign) float duration; 23 | @property (nonatomic, assign) BOOL canceled; 24 | 25 | @property (nonatomic, strong) MOKORecordButton *recordButton; 26 | @property (nonatomic, strong) MOKORecorderTool *recorder; 27 | 28 | @property (nonatomic, strong) UIButton *playBtn; 29 | 30 | @end 31 | 32 | @implementation ViewController 33 | 34 | - (void)playVoice 35 | { 36 | NSLog(@"播放录音"); 37 | [self.recorder playRecordingFile]; 38 | } 39 | - (void)viewDidLoad { 40 | [super viewDidLoad]; 41 | self.view.backgroundColor = [UIColor yellowColor]; 42 | 43 | self.playBtn = [[UIButton alloc] initWithFrame:CGRectMake((self.view.frame.size.width - 150) * 0.5, 20, 150, 80)]; 44 | [self.playBtn addTarget:self action:@selector(playVoice) forControlEvents:UIControlEventTouchUpInside]; 45 | [self.playBtn setTitle:@"点击我 播放语音" forState:UIControlStateNormal]; 46 | self.playBtn.backgroundColor = [UIColor blueColor]; 47 | [self.view addSubview:self.playBtn]; 48 | 49 | self.recordButton = [MOKORecordButton buttonWithType:UIButtonTypeCustom]; 50 | self.recorder = [MOKORecorderTool sharedRecorder]; 51 | self.recorder.delegate = self; 52 | self.recordButton.frame = CGRectMake(20, self.view.frame.size.height - 50, self.view.frame.size.width - 40, 40); 53 | self.recordButton.backgroundColor = [UIColor whiteColor]; 54 | self.recordButton.titleLabel.font = [UIFont systemFontOfSize:14]; 55 | self.recordButton.layer.cornerRadius = 4; 56 | self.recordButton.clipsToBounds = YES; 57 | [self.recordButton setTitle:@"按住 说话" forState:UIControlStateNormal]; 58 | [self.recordButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 59 | [self.view addSubview:self.recordButton]; 60 | 61 | //录音相关 62 | [self toDoRecord]; 63 | } 64 | 65 | #pragma mark ---- 录音全部状态的监听 以及视图的构建 切换 66 | -(void)toDoRecord 67 | { 68 | __weak typeof(self) weak_self = self; 69 | //手指按下 70 | self.recordButton.recordTouchDownAction = ^(MOKORecordButton *sender){ 71 | //如果用户没有开启麦克风权限,不能让其录音 72 | if (![weak_self canRecord]) return; 73 | 74 | NSLog(@"开始录音"); 75 | if (sender.highlighted) { 76 | sender.highlighted = YES; 77 | [sender setButtonStateWithRecording]; 78 | } 79 | [weak_self.recorder startRecording]; 80 | weak_self.currentRecordState = MOKORecordState_Recording; 81 | [weak_self dispatchVoiceState]; 82 | }; 83 | 84 | //手指抬起 85 | self.recordButton.recordTouchUpInsideAction = ^(MOKORecordButton *sender){ 86 | NSLog(@"完成录音"); 87 | [sender setButtonStateWithNormal]; 88 | [weak_self.recorder stopRecording]; 89 | weak_self.currentRecordState = MOKORecordState_Normal; 90 | [weak_self dispatchVoiceState]; 91 | }; 92 | 93 | //手指滑出按钮 94 | self.recordButton.recordTouchUpOutsideAction = ^(MOKORecordButton *sender){ 95 | NSLog(@"取消录音"); 96 | [sender setButtonStateWithNormal]; 97 | weak_self.currentRecordState = MOKORecordState_Normal; 98 | [weak_self dispatchVoiceState]; 99 | }; 100 | 101 | //中间状态 从 TouchDragInside ---> TouchDragOutside 102 | self.recordButton.recordTouchDragExitAction = ^(MOKORecordButton *sender){ 103 | weak_self.currentRecordState = MOKORecordState_ReleaseToCancel; 104 | [weak_self dispatchVoiceState]; 105 | }; 106 | 107 | //中间状态 从 TouchDragOutside ---> TouchDragInside 108 | self.recordButton.recordTouchDragEnterAction = ^(MOKORecordButton *sender){ 109 | NSLog(@"继续录音"); 110 | weak_self.currentRecordState = MOKORecordState_Recording; 111 | [weak_self dispatchVoiceState]; 112 | }; 113 | } 114 | 115 | - (void)startFakeTimer 116 | { 117 | if (_fakeTimer) { 118 | [_fakeTimer invalidate]; 119 | _fakeTimer = nil; 120 | } 121 | self.fakeTimer = [NSTimer scheduledTimerWithTimeInterval:kFakeTimerDuration target:self selector:@selector(onFakeTimerTimeOut) userInfo:nil repeats:YES]; 122 | [_fakeTimer fire]; 123 | } 124 | 125 | - (void)stopFakeTimer 126 | { 127 | if (_fakeTimer) { 128 | [_fakeTimer invalidate]; 129 | _fakeTimer = nil; 130 | } 131 | } 132 | 133 | - (void)onFakeTimerTimeOut 134 | { 135 | self.duration += kFakeTimerDuration; 136 | NSLog(@"+++duration+++ %f",self.duration); 137 | float remainTime = kMaxRecordDuration - self.duration; 138 | if ((int)remainTime == 0) { 139 | self.currentRecordState = MOKORecordState_Normal; 140 | [self dispatchVoiceState]; 141 | } 142 | else if ([self shouldShowCounting]) { 143 | self.currentRecordState = MOKORecordState_RecordCounting; 144 | [self dispatchVoiceState]; 145 | [self.voiceRecordCtrl showRecordCounting:remainTime]; 146 | } 147 | else 148 | { 149 | [self.recorder.recorder updateMeters]; 150 | float level = 0.0f; // The linear 0.0 .. 1.0 value we need. 151 | 152 | float minDecibels = -80.0f; // Or use -60dB, which I measured in a silent room. 153 | float decibels = [self.recorder.recorder peakPowerForChannel:0]; 154 | if (decibels < minDecibels) 155 | { 156 | level = 0.0f; 157 | } 158 | else if (decibels >= 0.0f) 159 | { 160 | level = 1.0f; 161 | } 162 | else 163 | { 164 | float root = 2.0f; 165 | float minAmp = powf(10.0f, 0.05f * minDecibels); 166 | float inverseAmpRange = 1.0f / (1.0f - minAmp); 167 | float amp = powf(10.0f, 0.05f * decibels); 168 | float adjAmp = (amp - minAmp) * inverseAmpRange; 169 | level = powf(adjAmp, 1.0f / root); 170 | } 171 | 172 | [self.voiceRecordCtrl updatePower:level]; 173 | } 174 | } 175 | - (BOOL)shouldShowCounting 176 | { 177 | if (self.duration >= (kMaxRecordDuration - kRemainCountingDuration) && self.duration < kMaxRecordDuration && self.currentRecordState != MOKORecordState_ReleaseToCancel) { 178 | return YES; 179 | } 180 | return NO; 181 | } 182 | 183 | - (void)resetState 184 | { 185 | [self stopFakeTimer]; 186 | self.duration = 0; 187 | self.canceled = YES; 188 | } 189 | 190 | - (void)dispatchVoiceState 191 | { 192 | if (_currentRecordState == MOKORecordState_Recording) { 193 | self.canceled = NO; 194 | [self startFakeTimer]; 195 | } 196 | else if (_currentRecordState == MOKORecordState_Normal) 197 | { 198 | [self resetState]; 199 | } 200 | [self.voiceRecordCtrl updateUIWithRecordState:_currentRecordState]; 201 | } 202 | 203 | - (MOKORecordShowManager *)voiceRecordCtrl 204 | { 205 | if (_voiceRecordCtrl == nil) { 206 | _voiceRecordCtrl = [MOKORecordShowManager new]; 207 | } 208 | return _voiceRecordCtrl; 209 | } 210 | 211 | //判断是否允许使用麦克风7.0新增的方法requestRecordPermission 212 | -(BOOL)canRecord 213 | { 214 | __block BOOL bCanRecord = YES; 215 | AVAudioSession *audioSession = [AVAudioSession sharedInstance]; 216 | if ([audioSession respondsToSelector:@selector(requestRecordPermission:)]) { 217 | [audioSession performSelector:@selector(requestRecordPermission:) withObject:^(BOOL granted) { 218 | if (granted) { 219 | bCanRecord = YES; 220 | } 221 | else { 222 | bCanRecord = NO; 223 | dispatch_async(dispatch_get_main_queue(), ^{ 224 | [[[UIAlertView alloc] initWithTitle:nil 225 | message:@"app需要访问您的麦克风。\n请启用麦克风-设置/隐私/麦克风" 226 | delegate:nil 227 | cancelButtonTitle:@"关闭" 228 | otherButtonTitles:nil] show]; 229 | }); 230 | } 231 | }]; 232 | } 233 | return bCanRecord; 234 | } 235 | 236 | @end 237 | -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/ic_record@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringAndSummer/MOKORecorder/b43f63ba0b19161ecb2fb221b1495b88921f1b93/MOKORecorder/MOKORecorder/ic_record@2x.png -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/ic_record@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringAndSummer/MOKORecorder/b43f63ba0b19161ecb2fb221b1495b88921f1b93/MOKORecorder/MOKORecorder/ic_record@3x.png -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/ic_record_ripple@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringAndSummer/MOKORecorder/b43f63ba0b19161ecb2fb221b1495b88921f1b93/MOKORecorder/MOKORecorder/ic_record_ripple@2x.png -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/ic_record_ripple@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringAndSummer/MOKORecorder/b43f63ba0b19161ecb2fb221b1495b88921f1b93/MOKORecorder/MOKORecorder/ic_record_ripple@3x.png -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/ic_record_too_short@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringAndSummer/MOKORecorder/b43f63ba0b19161ecb2fb221b1495b88921f1b93/MOKORecorder/MOKORecorder/ic_record_too_short@2x.png -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/ic_record_too_short@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringAndSummer/MOKORecorder/b43f63ba0b19161ecb2fb221b1495b88921f1b93/MOKORecorder/MOKORecorder/ic_record_too_short@3x.png -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/ic_release_to_cancel@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringAndSummer/MOKORecorder/b43f63ba0b19161ecb2fb221b1495b88921f1b93/MOKORecorder/MOKORecorder/ic_release_to_cancel@2x.png -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/ic_release_to_cancel@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpringAndSummer/MOKORecorder/b43f63ba0b19161ecb2fb221b1495b88921f1b93/MOKORecorder/MOKORecorder/ic_release_to_cancel@3x.png -------------------------------------------------------------------------------- /MOKORecorder/MOKORecorder/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MOKORecorder 4 | // 5 | // Created by Spring on 2017/4/27. 6 | // Copyright © 2017年 Spring. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MOKORecorder 2 | 高仿微信录音动画和录音功能 3 | 基本实现了微信的录音的所有的动画功能和录音功能 4 | --------------------------------------------------------------------------------