├── GIF ├── 1.gif └── 2.gif ├── LICENSE ├── README.md └── XLPlayButtonExample ├── XLPlayButtonExample.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ ├── MengXianLiang.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── mxl.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── MengXianLiang.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── XLPlayButtonExample.xcscheme │ │ └── xcschememanagement.plist │ └── mxl.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── XLPlayButtonExample ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── ViewController.h ├── ViewController.m └── main.m ├── YouKuPlayButton ├── YouKuPlayButton.h └── YouKuPlayButton.m └── iQiYiPlayButton ├── iQiYiPlayButton.h └── iQiYiPlayButton.m /GIF/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mengxianliang/XLPlayButton/fbf1def26348a3029002eb45ae6eebb759af15f7/GIF/1.gif -------------------------------------------------------------------------------- /GIF/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mengxianliang/XLPlayButton/fbf1def26348a3029002eb45ae6eebb759af15f7/GIF/2.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 XianLiang Meng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XLPlayButton 2 | 3 | 4 | ### 爱奇艺播放、暂停按钮动画效果 5 | 6 | ![image](https://github.com/mengxianliang/XLPlayButton/blob/master/GIF/1.gif) 7 | 8 | ### 优酷播放、暂停按钮动画效果 9 | 10 | ![image](https://github.com/mengxianliang/XLPlayButton/blob/master/GIF/2.gif) 11 | 12 | ### 实现原理 13 | 14 | 实现原理是利用贝塞尔曲线和CAShapeLayer绘制出三角、圆弧、直线,然后通过核心动画实现的动态效果。 15 | 16 | ### 使用方法 17 | 18 | * XLPlayButton 是继承UIButton的,只是创建方式和UIButton不同,其他的使用方法均一致。 19 | * 创建方法 20 | ```objc 21 | _iQiYiPlayButton = [[iQiYiPlayButton alloc] initWithFrame:CGRectMake(0, 0, 60, 60) state:iQiYiPlayButtonStatePlay]; 22 | ``` 23 | * 唯一属性 24 | ```objc 25 | /** 26 | 通过setter方式控制按钮动画 27 | 设置XLPlayButtonStatePlay显示播放按钮 28 | 设置XLPlayButtonStatePause显示暂停按钮 29 | */ 30 | @property (nonatomic, assign) XLPlayButtonState buttonState; 31 | ``` 32 | * 切换状态方法 33 | ```objc 34 | - (void)iQiYiPlayMethod { 35 | //通过判断当前状态 切换显示状态 36 | if (_iQiYiPlayButton.buttonState == iQiYiPlayButtonStatePause) { 37 | _iQiYiPlayButton.buttonState = iQiYiPlayButtonStatePlay; 38 | }else { 39 | _iQiYiPlayButton.buttonState = iQiYiPlayButtonStatePause; 40 | } 41 | } 42 | ``` 43 | 44 | ### 优酷播放按钮动画实现原理 [CSDN](http://blog.csdn.net/u013282507/article/details/77247437) / [简书](http://www.jianshu.com/p/32e7becf1a92) 45 | 46 | ### 爱奇艺播放按钮动画实现原理 [CSDN](http://blog.csdn.net/u013282507/article/details/77676294) / [简书](http://www.jianshu.com/p/3546964996ff) 47 | 48 | ### 个人开发过的UI工具集合 [XLUIKit](https://github.com/mengxianliang/XLUIKit) 49 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5E650B771F3C7137007126FF /* YouKuPlayButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E650B761F3C7137007126FF /* YouKuPlayButton.m */; }; 11 | 5E650B7B1F3C73B3007126FF /* iQiYiPlayButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E650B7A1F3C73B3007126FF /* iQiYiPlayButton.m */; }; 12 | 5E6F7C891F3ADF04003AF9FB /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E6F7C881F3ADF04003AF9FB /* main.m */; }; 13 | 5E6F7C8C1F3ADF04003AF9FB /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E6F7C8B1F3ADF04003AF9FB /* AppDelegate.m */; }; 14 | 5E6F7C8F1F3ADF04003AF9FB /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E6F7C8E1F3ADF04003AF9FB /* ViewController.m */; }; 15 | 5E6F7C921F3ADF04003AF9FB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E6F7C901F3ADF04003AF9FB /* Main.storyboard */; }; 16 | 5E6F7C941F3ADF04003AF9FB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E6F7C931F3ADF04003AF9FB /* Assets.xcassets */; }; 17 | 5E6F7C971F3ADF04003AF9FB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E6F7C951F3ADF04003AF9FB /* LaunchScreen.storyboard */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 5E650B751F3C7137007126FF /* YouKuPlayButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YouKuPlayButton.h; sourceTree = ""; }; 22 | 5E650B761F3C7137007126FF /* YouKuPlayButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YouKuPlayButton.m; sourceTree = ""; }; 23 | 5E650B791F3C73B3007126FF /* iQiYiPlayButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iQiYiPlayButton.h; sourceTree = ""; }; 24 | 5E650B7A1F3C73B3007126FF /* iQiYiPlayButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iQiYiPlayButton.m; sourceTree = ""; }; 25 | 5E6F7C841F3ADF04003AF9FB /* XLPlayButtonExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XLPlayButtonExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 26 | 5E6F7C881F3ADF04003AF9FB /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 27 | 5E6F7C8A1F3ADF04003AF9FB /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 28 | 5E6F7C8B1F3ADF04003AF9FB /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 29 | 5E6F7C8D1F3ADF04003AF9FB /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 30 | 5E6F7C8E1F3ADF04003AF9FB /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 31 | 5E6F7C911F3ADF04003AF9FB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 32 | 5E6F7C931F3ADF04003AF9FB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 33 | 5E6F7C961F3ADF04003AF9FB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 34 | 5E6F7C981F3ADF04003AF9FB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | 5E6F7C811F3ADF04003AF9FB /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | 5E650B741F3C7137007126FF /* YouKuPlayButton */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | 5E650B751F3C7137007126FF /* YouKuPlayButton.h */, 52 | 5E650B761F3C7137007126FF /* YouKuPlayButton.m */, 53 | ); 54 | path = YouKuPlayButton; 55 | sourceTree = ""; 56 | }; 57 | 5E650B781F3C73B3007126FF /* iQiYiPlayButton */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | 5E650B791F3C73B3007126FF /* iQiYiPlayButton.h */, 61 | 5E650B7A1F3C73B3007126FF /* iQiYiPlayButton.m */, 62 | ); 63 | path = iQiYiPlayButton; 64 | sourceTree = ""; 65 | }; 66 | 5E6F7C7B1F3ADF04003AF9FB = { 67 | isa = PBXGroup; 68 | children = ( 69 | 5E650B781F3C73B3007126FF /* iQiYiPlayButton */, 70 | 5E650B741F3C7137007126FF /* YouKuPlayButton */, 71 | 5E6F7C861F3ADF04003AF9FB /* XLPlayButtonExample */, 72 | 5E6F7C851F3ADF04003AF9FB /* Products */, 73 | ); 74 | sourceTree = ""; 75 | }; 76 | 5E6F7C851F3ADF04003AF9FB /* Products */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 5E6F7C841F3ADF04003AF9FB /* XLPlayButtonExample.app */, 80 | ); 81 | name = Products; 82 | sourceTree = ""; 83 | }; 84 | 5E6F7C861F3ADF04003AF9FB /* XLPlayButtonExample */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 5E6F7C8A1F3ADF04003AF9FB /* AppDelegate.h */, 88 | 5E6F7C8B1F3ADF04003AF9FB /* AppDelegate.m */, 89 | 5E6F7C8D1F3ADF04003AF9FB /* ViewController.h */, 90 | 5E6F7C8E1F3ADF04003AF9FB /* ViewController.m */, 91 | 5E6F7C901F3ADF04003AF9FB /* Main.storyboard */, 92 | 5E6F7C931F3ADF04003AF9FB /* Assets.xcassets */, 93 | 5E6F7C951F3ADF04003AF9FB /* LaunchScreen.storyboard */, 94 | 5E6F7C981F3ADF04003AF9FB /* Info.plist */, 95 | 5E6F7C871F3ADF04003AF9FB /* Supporting Files */, 96 | ); 97 | path = XLPlayButtonExample; 98 | sourceTree = ""; 99 | }; 100 | 5E6F7C871F3ADF04003AF9FB /* Supporting Files */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 5E6F7C881F3ADF04003AF9FB /* main.m */, 104 | ); 105 | name = "Supporting Files"; 106 | sourceTree = ""; 107 | }; 108 | /* End PBXGroup section */ 109 | 110 | /* Begin PBXNativeTarget section */ 111 | 5E6F7C831F3ADF04003AF9FB /* XLPlayButtonExample */ = { 112 | isa = PBXNativeTarget; 113 | buildConfigurationList = 5E6F7C9B1F3ADF04003AF9FB /* Build configuration list for PBXNativeTarget "XLPlayButtonExample" */; 114 | buildPhases = ( 115 | 5E6F7C801F3ADF04003AF9FB /* Sources */, 116 | 5E6F7C811F3ADF04003AF9FB /* Frameworks */, 117 | 5E6F7C821F3ADF04003AF9FB /* Resources */, 118 | ); 119 | buildRules = ( 120 | ); 121 | dependencies = ( 122 | ); 123 | name = XLPlayButtonExample; 124 | productName = XLPlayButtonExample; 125 | productReference = 5E6F7C841F3ADF04003AF9FB /* XLPlayButtonExample.app */; 126 | productType = "com.apple.product-type.application"; 127 | }; 128 | /* End PBXNativeTarget section */ 129 | 130 | /* Begin PBXProject section */ 131 | 5E6F7C7C1F3ADF04003AF9FB /* Project object */ = { 132 | isa = PBXProject; 133 | attributes = { 134 | LastUpgradeCheck = 1020; 135 | ORGANIZATIONNAME = mxl; 136 | TargetAttributes = { 137 | 5E6F7C831F3ADF04003AF9FB = { 138 | CreatedOnToolsVersion = 8.3.3; 139 | DevelopmentTeam = X496RVQDTB; 140 | ProvisioningStyle = Manual; 141 | }; 142 | }; 143 | }; 144 | buildConfigurationList = 5E6F7C7F1F3ADF04003AF9FB /* Build configuration list for PBXProject "XLPlayButtonExample" */; 145 | compatibilityVersion = "Xcode 3.2"; 146 | developmentRegion = en; 147 | hasScannedForEncodings = 0; 148 | knownRegions = ( 149 | en, 150 | Base, 151 | ); 152 | mainGroup = 5E6F7C7B1F3ADF04003AF9FB; 153 | productRefGroup = 5E6F7C851F3ADF04003AF9FB /* Products */; 154 | projectDirPath = ""; 155 | projectRoot = ""; 156 | targets = ( 157 | 5E6F7C831F3ADF04003AF9FB /* XLPlayButtonExample */, 158 | ); 159 | }; 160 | /* End PBXProject section */ 161 | 162 | /* Begin PBXResourcesBuildPhase section */ 163 | 5E6F7C821F3ADF04003AF9FB /* Resources */ = { 164 | isa = PBXResourcesBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | 5E6F7C971F3ADF04003AF9FB /* LaunchScreen.storyboard in Resources */, 168 | 5E6F7C941F3ADF04003AF9FB /* Assets.xcassets in Resources */, 169 | 5E6F7C921F3ADF04003AF9FB /* Main.storyboard in Resources */, 170 | ); 171 | runOnlyForDeploymentPostprocessing = 0; 172 | }; 173 | /* End PBXResourcesBuildPhase section */ 174 | 175 | /* Begin PBXSourcesBuildPhase section */ 176 | 5E6F7C801F3ADF04003AF9FB /* Sources */ = { 177 | isa = PBXSourcesBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | 5E6F7C8F1F3ADF04003AF9FB /* ViewController.m in Sources */, 181 | 5E650B771F3C7137007126FF /* YouKuPlayButton.m in Sources */, 182 | 5E650B7B1F3C73B3007126FF /* iQiYiPlayButton.m in Sources */, 183 | 5E6F7C8C1F3ADF04003AF9FB /* AppDelegate.m in Sources */, 184 | 5E6F7C891F3ADF04003AF9FB /* main.m in Sources */, 185 | ); 186 | runOnlyForDeploymentPostprocessing = 0; 187 | }; 188 | /* End PBXSourcesBuildPhase section */ 189 | 190 | /* Begin PBXVariantGroup section */ 191 | 5E6F7C901F3ADF04003AF9FB /* Main.storyboard */ = { 192 | isa = PBXVariantGroup; 193 | children = ( 194 | 5E6F7C911F3ADF04003AF9FB /* Base */, 195 | ); 196 | name = Main.storyboard; 197 | sourceTree = ""; 198 | }; 199 | 5E6F7C951F3ADF04003AF9FB /* LaunchScreen.storyboard */ = { 200 | isa = PBXVariantGroup; 201 | children = ( 202 | 5E6F7C961F3ADF04003AF9FB /* Base */, 203 | ); 204 | name = LaunchScreen.storyboard; 205 | sourceTree = ""; 206 | }; 207 | /* End PBXVariantGroup section */ 208 | 209 | /* Begin XCBuildConfiguration section */ 210 | 5E6F7C991F3ADF04003AF9FB /* Debug */ = { 211 | isa = XCBuildConfiguration; 212 | buildSettings = { 213 | ALWAYS_SEARCH_USER_PATHS = NO; 214 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 215 | CLANG_ANALYZER_NONNULL = YES; 216 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 217 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 218 | CLANG_CXX_LIBRARY = "libc++"; 219 | CLANG_ENABLE_MODULES = YES; 220 | CLANG_ENABLE_OBJC_ARC = YES; 221 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 222 | CLANG_WARN_BOOL_CONVERSION = YES; 223 | CLANG_WARN_COMMA = YES; 224 | CLANG_WARN_CONSTANT_CONVERSION = YES; 225 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 226 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 227 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 228 | CLANG_WARN_EMPTY_BODY = YES; 229 | CLANG_WARN_ENUM_CONVERSION = YES; 230 | CLANG_WARN_INFINITE_RECURSION = YES; 231 | CLANG_WARN_INT_CONVERSION = YES; 232 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 233 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 234 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 235 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 236 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 237 | CLANG_WARN_STRICT_PROTOTYPES = YES; 238 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 239 | CLANG_WARN_UNREACHABLE_CODE = YES; 240 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 241 | CODE_SIGN_IDENTITY = "iPhone Developer: xianliang meng (79SX5888C3)"; 242 | COPY_PHASE_STRIP = NO; 243 | DEBUG_INFORMATION_FORMAT = dwarf; 244 | DEVELOPMENT_TEAM = X496RVQDTB; 245 | ENABLE_STRICT_OBJC_MSGSEND = YES; 246 | ENABLE_TESTABILITY = YES; 247 | GCC_C_LANGUAGE_STANDARD = gnu99; 248 | GCC_DYNAMIC_NO_PIC = NO; 249 | GCC_NO_COMMON_BLOCKS = YES; 250 | GCC_OPTIMIZATION_LEVEL = 0; 251 | GCC_PREPROCESSOR_DEFINITIONS = ( 252 | "DEBUG=1", 253 | "$(inherited)", 254 | ); 255 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 256 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 257 | GCC_WARN_UNDECLARED_SELECTOR = YES; 258 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 259 | GCC_WARN_UNUSED_FUNCTION = YES; 260 | GCC_WARN_UNUSED_VARIABLE = YES; 261 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 262 | MTL_ENABLE_DEBUG_INFO = YES; 263 | ONLY_ACTIVE_ARCH = YES; 264 | PROVISIONING_PROFILE = "63d0bee2-4f74-4b71-a719-620d70b5a076"; 265 | SDKROOT = iphoneos; 266 | }; 267 | name = Debug; 268 | }; 269 | 5E6F7C9A1F3ADF04003AF9FB /* Release */ = { 270 | isa = XCBuildConfiguration; 271 | buildSettings = { 272 | ALWAYS_SEARCH_USER_PATHS = NO; 273 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 274 | CLANG_ANALYZER_NONNULL = YES; 275 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 276 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 277 | CLANG_CXX_LIBRARY = "libc++"; 278 | CLANG_ENABLE_MODULES = YES; 279 | CLANG_ENABLE_OBJC_ARC = YES; 280 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 281 | CLANG_WARN_BOOL_CONVERSION = YES; 282 | CLANG_WARN_COMMA = YES; 283 | CLANG_WARN_CONSTANT_CONVERSION = YES; 284 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 285 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 286 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 287 | CLANG_WARN_EMPTY_BODY = YES; 288 | CLANG_WARN_ENUM_CONVERSION = YES; 289 | CLANG_WARN_INFINITE_RECURSION = YES; 290 | CLANG_WARN_INT_CONVERSION = YES; 291 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 292 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 293 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 294 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 295 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 296 | CLANG_WARN_STRICT_PROTOTYPES = YES; 297 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 298 | CLANG_WARN_UNREACHABLE_CODE = YES; 299 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 300 | CODE_SIGN_IDENTITY = "iPhone Developer: xianliang meng (79SX5888C3)"; 301 | COPY_PHASE_STRIP = NO; 302 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 303 | DEVELOPMENT_TEAM = X496RVQDTB; 304 | ENABLE_NS_ASSERTIONS = NO; 305 | ENABLE_STRICT_OBJC_MSGSEND = YES; 306 | GCC_C_LANGUAGE_STANDARD = gnu99; 307 | GCC_NO_COMMON_BLOCKS = YES; 308 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 309 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 310 | GCC_WARN_UNDECLARED_SELECTOR = YES; 311 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 312 | GCC_WARN_UNUSED_FUNCTION = YES; 313 | GCC_WARN_UNUSED_VARIABLE = YES; 314 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 315 | MTL_ENABLE_DEBUG_INFO = NO; 316 | PROVISIONING_PROFILE = "63d0bee2-4f74-4b71-a719-620d70b5a076"; 317 | SDKROOT = iphoneos; 318 | VALIDATE_PRODUCT = YES; 319 | }; 320 | name = Release; 321 | }; 322 | 5E6F7C9C1F3ADF04003AF9FB /* Debug */ = { 323 | isa = XCBuildConfiguration; 324 | buildSettings = { 325 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 326 | CODE_SIGN_IDENTITY = "iPhone Developer: xianliang meng (79SX5888C3)"; 327 | DEVELOPMENT_TEAM = X496RVQDTB; 328 | INFOPLIST_FILE = XLPlayButtonExample/Info.plist; 329 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 330 | PRODUCT_BUNDLE_IDENTIFIER = mxl.XLPlayButtonExample; 331 | PRODUCT_NAME = "$(TARGET_NAME)"; 332 | PROVISIONING_PROFILE = "63d0bee2-4f74-4b71-a719-620d70b5a076"; 333 | PROVISIONING_PROFILE_SPECIFIER = ""; 334 | }; 335 | name = Debug; 336 | }; 337 | 5E6F7C9D1F3ADF04003AF9FB /* Release */ = { 338 | isa = XCBuildConfiguration; 339 | buildSettings = { 340 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 341 | CODE_SIGN_IDENTITY = "iPhone Developer: xianliang meng (79SX5888C3)"; 342 | DEVELOPMENT_TEAM = X496RVQDTB; 343 | INFOPLIST_FILE = XLPlayButtonExample/Info.plist; 344 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 345 | PRODUCT_BUNDLE_IDENTIFIER = mxl.XLPlayButtonExample; 346 | PRODUCT_NAME = "$(TARGET_NAME)"; 347 | PROVISIONING_PROFILE = "63d0bee2-4f74-4b71-a719-620d70b5a076"; 348 | PROVISIONING_PROFILE_SPECIFIER = ""; 349 | }; 350 | name = Release; 351 | }; 352 | /* End XCBuildConfiguration section */ 353 | 354 | /* Begin XCConfigurationList section */ 355 | 5E6F7C7F1F3ADF04003AF9FB /* Build configuration list for PBXProject "XLPlayButtonExample" */ = { 356 | isa = XCConfigurationList; 357 | buildConfigurations = ( 358 | 5E6F7C991F3ADF04003AF9FB /* Debug */, 359 | 5E6F7C9A1F3ADF04003AF9FB /* Release */, 360 | ); 361 | defaultConfigurationIsVisible = 0; 362 | defaultConfigurationName = Release; 363 | }; 364 | 5E6F7C9B1F3ADF04003AF9FB /* Build configuration list for PBXNativeTarget "XLPlayButtonExample" */ = { 365 | isa = XCConfigurationList; 366 | buildConfigurations = ( 367 | 5E6F7C9C1F3ADF04003AF9FB /* Debug */, 368 | 5E6F7C9D1F3ADF04003AF9FB /* Release */, 369 | ); 370 | defaultConfigurationIsVisible = 0; 371 | defaultConfigurationName = Release; 372 | }; 373 | /* End XCConfigurationList section */ 374 | }; 375 | rootObject = 5E6F7C7C1F3ADF04003AF9FB /* Project object */; 376 | } 377 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample.xcodeproj/project.xcworkspace/xcuserdata/MengXianLiang.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mengxianliang/XLPlayButton/fbf1def26348a3029002eb45ae6eebb759af15f7/XLPlayButtonExample/XLPlayButtonExample.xcodeproj/project.xcworkspace/xcuserdata/MengXianLiang.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample.xcodeproj/project.xcworkspace/xcuserdata/mxl.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mengxianliang/XLPlayButton/fbf1def26348a3029002eb45ae6eebb759af15f7/XLPlayButtonExample/XLPlayButtonExample.xcodeproj/project.xcworkspace/xcuserdata/mxl.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample.xcodeproj/xcuserdata/MengXianLiang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample.xcodeproj/xcuserdata/MengXianLiang.xcuserdatad/xcschemes/XLPlayButtonExample.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 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample.xcodeproj/xcuserdata/MengXianLiang.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | XLPlayButtonExample.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 5E6F7C831F3ADF04003AF9FB 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample.xcodeproj/xcuserdata/mxl.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | XLPlayButtonExample.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // XLPlayButtonExample 4 | // 5 | // Created by MengXianLiang on 2017/8/9. 6 | // Copyright © 2017年 mxl. 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 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // XLPlayButtonExample 4 | // 5 | // Created by MengXianLiang on 2017/8/9. 6 | // Copyright © 2017年 mxl. 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 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample/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 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample/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 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample/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 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample/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 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // XLPlayButtonExample 4 | // 5 | // Created by MengXianLiang on 2017/8/9. 6 | // Copyright © 2017年 mxl. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // XLPlayButtonExample 4 | // 5 | // Created by MengXianLiang on 2017/8/9. 6 | // Copyright © 2017年 mxl. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "iQiYiPlayButton.h" 11 | #import "YouKuPlayButton.h" 12 | @interface ViewController () 13 | 14 | @property (nonatomic, strong) iQiYiPlayButton *iQiYiPlayButton; 15 | 16 | @property (nonatomic, strong) YouKuPlayButton *youKuPlayButton; 17 | 18 | @end 19 | 20 | @implementation ViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | 25 | //创建播放按钮,需要初始化一个状态,即显示暂停还是播放状态 26 | _iQiYiPlayButton = [[iQiYiPlayButton alloc] initWithFrame:CGRectMake(0, 0, 60, 60) state:iQiYiPlayButtonStatePlay]; 27 | _iQiYiPlayButton.center = CGPointMake(self.view.center.x, self.view.bounds.size.height/3); 28 | [_iQiYiPlayButton addTarget:self action:@selector(iQiYiPlayMethod) forControlEvents:UIControlEventTouchUpInside]; 29 | [self.view addSubview:_iQiYiPlayButton]; 30 | 31 | //创建播放按钮,需要初始化一个状态,即显示暂停还是播放状态 32 | _youKuPlayButton = [[YouKuPlayButton alloc] initWithFrame:CGRectMake(0, 0, 60, 60) state:YouKuPlayButtonStatePlay]; 33 | _youKuPlayButton.center = CGPointMake(self.view.center.x, self.view.bounds.size.height*2/3); 34 | [_youKuPlayButton addTarget:self action:@selector(youKuPlayMethod) forControlEvents:UIControlEventTouchUpInside]; 35 | [self.view addSubview:_youKuPlayButton]; 36 | } 37 | 38 | - (void)iQiYiPlayMethod { 39 | //通过判断当前状态 切换显示状态 40 | if (_iQiYiPlayButton.buttonState == iQiYiPlayButtonStatePause) { 41 | _iQiYiPlayButton.buttonState = iQiYiPlayButtonStatePlay; 42 | }else { 43 | _iQiYiPlayButton.buttonState = iQiYiPlayButtonStatePause; 44 | } 45 | } 46 | 47 | - (void)youKuPlayMethod { 48 | //通过判断当前状态 切换显示状态 49 | if (_youKuPlayButton.buttonState == YouKuPlayButtonStatePause) { 50 | _youKuPlayButton.buttonState = YouKuPlayButtonStatePlay; 51 | }else { 52 | _youKuPlayButton.buttonState = YouKuPlayButtonStatePause; 53 | } 54 | } 55 | 56 | - (void)didReceiveMemoryWarning { 57 | [super didReceiveMemoryWarning]; 58 | } 59 | 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /XLPlayButtonExample/XLPlayButtonExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // XLPlayButtonExample 4 | // 5 | // Created by MengXianLiang on 2017/8/9. 6 | // Copyright © 2017年 mxl. 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 | -------------------------------------------------------------------------------- /XLPlayButtonExample/YouKuPlayButton/YouKuPlayButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // YouKuPlayButton.h 3 | // YouKuPlayButtonExample 4 | // 5 | // Created by MengXianLiang on 2017/8/10. 6 | // Copyright © 2017年 mxl. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger,YouKuPlayButtonState) { 12 | YouKuPlayButtonStatePause = 0, 13 | YouKuPlayButtonStatePlay 14 | }; 15 | 16 | @interface YouKuPlayButton : UIButton 17 | 18 | /** 19 | 通过setter方式控制按钮动画 20 | 设置XLPlayButtonStatePlay显示播放按钮 21 | 设置XLPlayButtonStatePause显示暂停按钮 22 | */ 23 | @property (nonatomic, assign) YouKuPlayButtonState buttonState; 24 | 25 | /** 26 | 创建方法 27 | */ 28 | - (instancetype)initWithFrame:(CGRect)frame state:(YouKuPlayButtonState)state; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /XLPlayButtonExample/YouKuPlayButton/YouKuPlayButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // YouKuPlayButton.m 3 | // YouKuPlayButtonExample 4 | // 5 | // Created by MengXianLiang on 2017/8/10. 6 | // Copyright © 2017年 mxl. All rights reserved. 7 | // 8 | 9 | #import "YouKuPlayButton.h" 10 | 11 | //动画时长 12 | static CGFloat animationDuration = 0.35f; 13 | //线条颜色 14 | #define BLueColor [UIColor colorWithRed:62/255.0 green:157/255.0 blue:254/255.0 alpha:1] 15 | #define LightBLueColor [UIColor colorWithRed:87/255.0 green:188/255.0 blue:253/255.0 alpha:1] 16 | #define RedColor [UIColor colorWithRed:228/255.0 green:35/255.0 blue:6/255.0 alpha:0.8] 17 | 18 | @interface YouKuPlayButton () 19 | 20 | @property (nonatomic, strong) CAShapeLayer *leftLineLayer; 21 | 22 | @property (nonatomic, strong) CAShapeLayer *leftCircle; 23 | 24 | @property (nonatomic, strong) CAShapeLayer *rightLineLayer; 25 | 26 | @property (nonatomic, strong) CAShapeLayer *rightCircle; 27 | 28 | //三角播放按钮容器 29 | @property (nonatomic, strong) CALayer *triangleCotainer; 30 | //是否正在执行动画 31 | @property (nonatomic, assign) BOOL isAnimating; 32 | 33 | @end 34 | 35 | @implementation YouKuPlayButton 36 | 37 | - (instancetype)initWithFrame:(CGRect)frame state:(YouKuPlayButtonState)state{ 38 | if (self = [super initWithFrame:frame]) { 39 | [self buildUI]; 40 | if (state == YouKuPlayButtonStatePlay) { 41 | self.buttonState = state; 42 | } 43 | } 44 | return self; 45 | } 46 | 47 | - (void)buildUI { 48 | [self addLeftCircle]; 49 | [self addRightCircle]; 50 | [self addLeftLineLayer]; 51 | [self addRightLineLayer]; 52 | [self addCenterTriangleLayer]; 53 | } 54 | 55 | /** 56 | 添加左侧竖线层 57 | */ 58 | - (void)addLeftLineLayer { 59 | CGFloat a = self.layer.bounds.size.width; 60 | 61 | UIBezierPath *path = [UIBezierPath bezierPath]; 62 | [path moveToPoint:CGPointMake(a*0.2,a*0.9)]; 63 | [path addLineToPoint:CGPointMake(a*0.2,a*0.1)]; 64 | 65 | _leftLineLayer = [CAShapeLayer layer]; 66 | _leftLineLayer.path = path.CGPath; 67 | _leftLineLayer.fillColor = [UIColor clearColor].CGColor; 68 | _leftLineLayer.strokeColor = BLueColor.CGColor; 69 | _leftLineLayer.lineWidth = [self lineWidth]; 70 | _leftLineLayer.lineCap = kCALineCapRound; 71 | _leftLineLayer.lineJoin = kCALineJoinRound; 72 | [self.layer addSublayer:_leftLineLayer]; 73 | } 74 | 75 | /** 76 | 添加左侧圆弧 77 | */ 78 | 79 | - (void)addLeftCircle { 80 | CGFloat a = self.layer.bounds.size.width; 81 | UIBezierPath *path = [UIBezierPath bezierPath]; 82 | [path moveToPoint:CGPointMake(a*0.2,a*0.9)]; 83 | CGFloat startAngle = acos(4.0/5.0) + M_PI_2; 84 | CGFloat endAngle = startAngle - M_PI; 85 | [path addArcWithCenter:CGPointMake(a*0.5, a*0.5) radius:0.5*a startAngle:startAngle endAngle:endAngle clockwise:false]; 86 | 87 | 88 | _leftCircle = [CAShapeLayer layer]; 89 | _leftCircle.path = path.CGPath; 90 | _leftCircle.fillColor = [UIColor clearColor].CGColor; 91 | _leftCircle.strokeColor = LightBLueColor.CGColor; 92 | _leftCircle.lineWidth = [self lineWidth]; 93 | _leftCircle.lineCap = kCALineCapRound; 94 | _leftCircle.lineJoin = kCALineJoinRound; 95 | _leftCircle.strokeEnd = 0; 96 | [self.layer addSublayer:_leftCircle]; 97 | } 98 | 99 | /** 100 | 添加右侧竖线层 101 | */ 102 | - (void)addRightLineLayer { 103 | 104 | CGFloat a = self.layer.bounds.size.width; 105 | 106 | UIBezierPath *path = [UIBezierPath bezierPath]; 107 | [path moveToPoint:CGPointMake(a*0.8,a*0.1)]; 108 | [path addLineToPoint:CGPointMake(a*0.8,a*0.9)]; 109 | 110 | _rightLineLayer = [CAShapeLayer layer]; 111 | _rightLineLayer.path = path.CGPath; 112 | _rightLineLayer.fillColor = [UIColor clearColor].CGColor; 113 | _rightLineLayer.strokeColor = BLueColor.CGColor; 114 | _rightLineLayer.lineWidth = [self lineWidth]; 115 | _rightLineLayer.lineCap = kCALineCapRound; 116 | _rightLineLayer.lineJoin = kCALineJoinRound; 117 | [self.layer addSublayer:_rightLineLayer]; 118 | } 119 | 120 | 121 | /** 122 | 添加左侧圆弧 123 | */ 124 | 125 | - (void)addRightCircle { 126 | 127 | CGFloat a = self.layer.bounds.size.width; 128 | UIBezierPath *path = [UIBezierPath bezierPath]; 129 | [path moveToPoint:CGPointMake(a*0.8,a*0.1)]; 130 | CGFloat startAngle = -asin(4.0/5.0); 131 | CGFloat endAngle = startAngle - M_PI; 132 | [path addArcWithCenter:CGPointMake(a*0.5, a*0.5) radius:0.5*a startAngle:startAngle endAngle:endAngle clockwise:false]; 133 | 134 | 135 | _rightCircle = [CAShapeLayer layer]; 136 | _rightCircle.path = path.CGPath; 137 | _rightCircle.fillColor = [UIColor clearColor].CGColor; 138 | _rightCircle.strokeColor = LightBLueColor.CGColor; 139 | _rightCircle.lineWidth = [self lineWidth]; 140 | _rightCircle.lineCap = kCALineCapRound; 141 | _rightCircle.lineJoin = kCALineJoinRound; 142 | _rightCircle.strokeEnd = 0; 143 | [self.layer addSublayer:_rightCircle]; 144 | } 145 | 146 | /** 147 | 添加左侧三角 由两个直线组成 148 | */ 149 | - (void)addCenterTriangleLayer { 150 | CGFloat a = self.layer.bounds.size.width; 151 | 152 | _triangleCotainer = [CALayer layer]; 153 | _triangleCotainer.bounds = CGRectMake(0, 0, 0.4*a, 0.35*a); 154 | _triangleCotainer.position = CGPointMake(a*0.5, a*0.55); 155 | _triangleCotainer.opacity = 0; 156 | [self.layer addSublayer:_triangleCotainer]; 157 | 158 | CGFloat b = _triangleCotainer.bounds.size.width; 159 | CGFloat c = _triangleCotainer.bounds.size.height; 160 | 161 | UIBezierPath *path1 = [UIBezierPath bezierPath]; 162 | [path1 moveToPoint:CGPointMake(0,0)]; 163 | [path1 addLineToPoint:CGPointMake(b/2,c)]; 164 | 165 | UIBezierPath *path2 = [UIBezierPath bezierPath]; 166 | [path2 moveToPoint:CGPointMake(b,0)]; 167 | [path2 addLineToPoint:CGPointMake(b/2,c)]; 168 | 169 | CAShapeLayer *layer1 = [CAShapeLayer layer]; 170 | layer1.path = path1.CGPath; 171 | layer1.fillColor = [UIColor clearColor].CGColor; 172 | layer1.strokeColor = RedColor.CGColor; 173 | layer1.lineWidth = [self lineWidth]; 174 | layer1.lineCap = kCALineCapRound; 175 | layer1.lineJoin = kCALineJoinRound; 176 | layer1.strokeEnd = 1; 177 | [_triangleCotainer addSublayer:layer1]; 178 | 179 | CAShapeLayer *layer2 = [CAShapeLayer layer]; 180 | layer2.path = path2.CGPath; 181 | layer2.fillColor = [UIColor clearColor].CGColor; 182 | layer2.strokeColor = RedColor.CGColor; 183 | layer2.lineWidth = [self lineWidth]; 184 | layer2.lineCap = kCALineCapRound; 185 | layer2.lineJoin = kCALineJoinRound; 186 | layer2.strokeEnd = 1; 187 | [_triangleCotainer addSublayer:layer2]; 188 | } 189 | 190 | #pragma mark - 191 | #pragma mark 动画方法 192 | /** 193 | 通用执行strokeEnd动画 194 | */ 195 | - (CABasicAnimation *)strokeEndAnimationFrom:(CGFloat)fromValue to:(CGFloat)toValue onLayer:(CALayer *)layer name:(NSString*)animationName duration:(CGFloat)duration delegate:(id)delegate { 196 | CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 197 | strokeEndAnimation.duration = duration; 198 | strokeEndAnimation.fromValue = @(fromValue); 199 | strokeEndAnimation.toValue = @(toValue); 200 | strokeEndAnimation.fillMode = kCAFillModeForwards; 201 | strokeEndAnimation.removedOnCompletion = NO; 202 | [strokeEndAnimation setValue:animationName forKey:@"animationName"]; 203 | strokeEndAnimation.delegate = delegate; 204 | [layer addAnimation:strokeEndAnimation forKey:nil]; 205 | return strokeEndAnimation; 206 | } 207 | 208 | /** 209 | 执行旋转动画 210 | */ 211 | - (void)actionRotateAnimationClockwise:(BOOL)clockwise { 212 | //逆时针旋转 213 | CGFloat startAngle = 0.0; 214 | CGFloat endAngle = -M_PI_2; 215 | CGFloat duration = 0.75 * animationDuration; 216 | //顺时针旋转 217 | if (clockwise) { 218 | startAngle = -M_PI_2; 219 | endAngle = 0.0; 220 | duration = animationDuration; 221 | } 222 | CABasicAnimation *roateAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; 223 | roateAnimation.duration = duration; // 持续时间 224 | roateAnimation.fromValue = [NSNumber numberWithFloat:startAngle]; 225 | roateAnimation.toValue = [NSNumber numberWithFloat:endAngle]; 226 | roateAnimation.fillMode = kCAFillModeForwards; 227 | roateAnimation.removedOnCompletion = NO; 228 | [roateAnimation setValue:@"roateAnimation" forKey:@"animationName"]; 229 | [self.layer addAnimation:roateAnimation forKey:nil]; 230 | } 231 | 232 | /** 233 | 三角旋转动画 234 | */ 235 | - (void)actionTriangleAlphaAnimationFrom:(CGFloat)from to:(CGFloat)to duration:(CGFloat)duration{ 236 | 237 | CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 238 | alphaAnimation.duration = duration; // 持续时间 239 | alphaAnimation.fromValue = @(from); 240 | alphaAnimation.toValue = @(to); 241 | alphaAnimation.fillMode = kCAFillModeForwards; 242 | alphaAnimation.removedOnCompletion = NO; 243 | [alphaAnimation setValue:@"alphaAnimation" forKey:@"animationName"]; 244 | [_triangleCotainer addAnimation:alphaAnimation forKey:nil]; 245 | } 246 | 247 | //线条宽度,根据按钮的宽度按比例设置 248 | - (CGFloat)lineWidth { 249 | return self.layer.bounds.size.width * 0.18; 250 | } 251 | 252 | #pragma mark - 253 | #pragma mark Setter 254 | - (void)setButtonState:(YouKuPlayButtonState)buttonState { 255 | //如果正在执行动画则不再执行下面操作 256 | if (_isAnimating == true) {return;} 257 | _buttonState = buttonState; 258 | _isAnimating = true; 259 | if (buttonState == YouKuPlayButtonStatePlay) { 260 | [self showPlayAnimation]; 261 | } else if (buttonState == YouKuPlayButtonStatePause) { 262 | [self showPauseAnimation]; 263 | } 264 | //更新动画执行状态 265 | __weak typeof(self)weakSelf = self; 266 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (animationDuration) * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ 267 | weakSelf.isAnimating = false; 268 | }); 269 | } 270 | /** 271 | 显示暂停-播放动画 272 | */ 273 | - (void)showPlayAnimation { 274 | //收直线、放圆圈;直线的速度是圆圈的2倍 275 | [self strokeEndAnimationFrom:1 to:0 onLayer:_leftLineLayer name:nil duration:animationDuration/2 delegate:nil]; 276 | [self strokeEndAnimationFrom:1 to:0 onLayer:_rightLineLayer name:nil duration:animationDuration/2 delegate:nil]; 277 | [self strokeEndAnimationFrom:0 to:1 onLayer:_leftCircle name:nil duration:animationDuration delegate:nil]; 278 | [self strokeEndAnimationFrom:0 to:1 onLayer:_rightCircle name:nil duration:animationDuration delegate:nil]; 279 | //开始旋转动画 280 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, animationDuration/4 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ 281 | [self actionRotateAnimationClockwise:false]; 282 | }); 283 | //显示播放三角动画 284 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, animationDuration/2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ 285 | [self actionTriangleAlphaAnimationFrom:0 to:1 duration:animationDuration/2]; 286 | }); 287 | } 288 | 289 | /** 290 | 显示播放-暂停动画 291 | */ 292 | - (void)showPauseAnimation { 293 | //先收圆圈, 294 | [self strokeEndAnimationFrom:1 to:0 onLayer:_leftCircle name:nil duration:animationDuration delegate:nil]; 295 | [self strokeEndAnimationFrom:1 to:0 onLayer:_rightCircle name:nil duration:animationDuration delegate:nil]; 296 | //隐藏播放三角动画 297 | [self actionTriangleAlphaAnimationFrom:1 to:0 duration:animationDuration/2]; 298 | //旋转动画 299 | [self actionRotateAnimationClockwise:true]; 300 | //收到一半再放直线 301 | __weak typeof(self)weakSelf = self; 302 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, animationDuration/2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ 303 | [self strokeEndAnimationFrom:0 to:1 onLayer:weakSelf.leftLineLayer name:nil duration:animationDuration/2 delegate:nil]; 304 | [self strokeEndAnimationFrom:0 to:1 onLayer:weakSelf.rightLineLayer name:nil duration:animationDuration/2 delegate:nil]; 305 | }); 306 | } 307 | 308 | 309 | 310 | @end 311 | -------------------------------------------------------------------------------- /XLPlayButtonExample/iQiYiPlayButton/iQiYiPlayButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // iQiYiPlayButton.h 3 | // XLPlayButtonExample 4 | // 5 | // Created by MengXianLiang on 2017/8/9. 6 | // Copyright © 2017年 mxl. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger,iQiYiPlayButtonState) { 12 | iQiYiPlayButtonStatePause = 0, 13 | iQiYiPlayButtonStatePlay 14 | }; 15 | 16 | @interface iQiYiPlayButton : UIButton 17 | 18 | /** 19 | 通过setter方式控制按钮动画 20 | 设置XLPlayButtonStatePlay显示播放按钮 21 | 设置XLPlayButtonStatePause显示暂停按钮 22 | */ 23 | @property (nonatomic, assign) iQiYiPlayButtonState buttonState; 24 | 25 | /** 26 | 创建方法 27 | */ 28 | - (instancetype)initWithFrame:(CGRect)frame state:(iQiYiPlayButtonState)state; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /XLPlayButtonExample/iQiYiPlayButton/iQiYiPlayButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // iQiYiPlayButton.m 3 | // XLPlayButtonExample 4 | // 5 | // Created by MengXianLiang on 2017/8/9. 6 | // Copyright © 2017年 mxl. All rights reserved. 7 | // 8 | 9 | #import "iQiYiPlayButton.h" 10 | 11 | //其它动画时长 12 | static CGFloat animationDuration = 0.5f; 13 | //位移动画时长 14 | static CGFloat positionDuration = 0.3f; 15 | //线条颜色 16 | #define LineColor [UIColor colorWithRed:12/255.0 green:190/255.0 blue:6/255.0 alpha:1] 17 | //三角动画名称 18 | #define TriangleAnimation @"TriangleAnimation" 19 | //右侧直线动画名称 20 | #define RightLineAnimation @"RightLineAnimation" 21 | 22 | @interface iQiYiPlayButton () 23 | //左侧竖条 24 | @property (nonatomic, strong) CAShapeLayer *leftLineLayer; 25 | //三角 26 | @property (nonatomic, strong) CAShapeLayer *triangleLayer; 27 | //右侧竖条 28 | @property (nonatomic, strong) CAShapeLayer *rightLineLayer; 29 | //画弧layer 30 | @property (nonatomic, strong) CAShapeLayer *circleLayer; 31 | 32 | //是否正在执行动画 33 | @property (nonatomic, assign) BOOL isAnimating; 34 | 35 | @end 36 | 37 | @implementation iQiYiPlayButton 38 | 39 | - (instancetype)initWithFrame:(CGRect)frame state:(iQiYiPlayButtonState)state{ 40 | if (self = [super initWithFrame:frame]) { 41 | [self buildUI]; 42 | if (state == iQiYiPlayButtonStatePlay) { 43 | self.buttonState = state; 44 | } 45 | } 46 | return self; 47 | } 48 | 49 | /** 50 | 创建UI 51 | */ 52 | - (void)buildUI { 53 | _buttonState = iQiYiPlayButtonStatePause; 54 | [self addTriangleLayer]; 55 | [self addLeftLineLayer]; 56 | [self addRightLineLayer]; 57 | [self addCircleLayer]; 58 | } 59 | 60 | #pragma mark - 61 | #pragma mark 添加动画层 62 | /** 63 | 添加三角层 64 | */ 65 | - (void)addTriangleLayer { 66 | CGFloat a = self.bounds.size.width; 67 | 68 | UIBezierPath *path = [UIBezierPath bezierPath]; 69 | [path moveToPoint:CGPointMake(a*0.2,a*0.2)]; 70 | [path addLineToPoint:CGPointMake(a*0.2,0)]; 71 | [path addLineToPoint:CGPointMake(a,a*0.5)]; 72 | [path addLineToPoint:CGPointMake(a*0.2,a)]; 73 | [path addLineToPoint:CGPointMake(a*0.2,a*0.2)]; 74 | 75 | _triangleLayer = [CAShapeLayer layer]; 76 | _triangleLayer.path = path.CGPath; 77 | _triangleLayer.fillColor = [UIColor clearColor].CGColor; 78 | _triangleLayer.strokeColor = LineColor.CGColor; 79 | _triangleLayer.lineWidth = [self lineWidth]; 80 | _triangleLayer.lineCap = kCALineCapButt; 81 | _triangleLayer.lineJoin = kCALineJoinRound; 82 | _triangleLayer.strokeEnd = 0; 83 | [self.layer addSublayer:_triangleLayer]; 84 | } 85 | 86 | /** 87 | 添加左侧竖线层 88 | */ 89 | - (void)addLeftLineLayer { 90 | CGFloat a = self.bounds.size.width; 91 | 92 | UIBezierPath *path = [UIBezierPath bezierPath]; 93 | [path moveToPoint:CGPointMake(a*0.2,0)]; 94 | [path addLineToPoint:CGPointMake(a*0.2,a)]; 95 | 96 | _leftLineLayer = [CAShapeLayer layer]; 97 | _leftLineLayer.path = path.CGPath; 98 | _leftLineLayer.fillColor = [UIColor clearColor].CGColor; 99 | _leftLineLayer.strokeColor = LineColor.CGColor; 100 | _leftLineLayer.lineWidth = [self lineWidth]; 101 | _leftLineLayer.lineCap = kCALineCapRound; 102 | _leftLineLayer.lineJoin = kCALineJoinRound; 103 | 104 | [self.layer addSublayer:_leftLineLayer]; 105 | } 106 | 107 | /** 108 | 添加右侧竖线层 109 | */ 110 | - (void)addRightLineLayer { 111 | 112 | CGFloat a = self.bounds.size.width; 113 | 114 | UIBezierPath *path = [UIBezierPath bezierPath]; 115 | [path moveToPoint:CGPointMake(a*0.8,a)]; 116 | [path addLineToPoint:CGPointMake(a*0.8,0)]; 117 | 118 | _rightLineLayer = [CAShapeLayer layer]; 119 | _rightLineLayer.path = path.CGPath; 120 | _rightLineLayer.fillColor = [UIColor clearColor].CGColor; 121 | _rightLineLayer.strokeColor = LineColor.CGColor; 122 | _rightLineLayer.lineWidth = [self lineWidth]; 123 | _rightLineLayer.lineCap = kCALineCapRound; 124 | _rightLineLayer.lineJoin = kCALineJoinRound; 125 | [self.layer addSublayer:_rightLineLayer]; 126 | } 127 | /** 128 | 添加弧线过渡弧线层 129 | */ 130 | - (void)addCircleLayer { 131 | 132 | CGFloat a = self.bounds.size.width; 133 | UIBezierPath *path = [UIBezierPath bezierPath]; 134 | [path moveToPoint:CGPointMake(a*0.8,a*0.8)]; 135 | [path addArcWithCenter:CGPointMake(a*0.5, a*0.8) radius:0.3*a startAngle:0 endAngle:M_PI clockwise:true]; 136 | 137 | _circleLayer = [CAShapeLayer layer]; 138 | _circleLayer.path = path.CGPath; 139 | _circleLayer.fillColor = [UIColor clearColor].CGColor; 140 | _circleLayer.strokeColor = LineColor.CGColor; 141 | _circleLayer.lineWidth = [self lineWidth]; 142 | _circleLayer.lineCap = kCALineCapRound; 143 | _circleLayer.lineJoin = kCALineJoinRound; 144 | _circleLayer.strokeEnd = 0; 145 | [self.layer addSublayer:_circleLayer]; 146 | } 147 | 148 | #pragma mark - 149 | #pragma mark 动画执行方法 150 | /** 151 | 执行正向动画,即暂停-》播放 152 | */ 153 | - (void)actionPositiveAnimation { 154 | //开始三角动画 155 | [self strokeEndAnimationFrom:0 to:1 onLayer:_triangleLayer name:TriangleAnimation duration:animationDuration delegate:self]; 156 | //开始右侧线条动画 157 | [self strokeEndAnimationFrom:1 to:0 onLayer:_rightLineLayer name:RightLineAnimation duration:animationDuration/4 delegate:self]; 158 | //开始画弧动画 159 | [self strokeEndAnimationFrom:0 to:1 onLayer:_circleLayer name:nil duration:animationDuration/4 delegate:nil]; 160 | //开始逆向画弧动画 161 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, animationDuration*0.25 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ 162 | [self circleStartAnimationFrom:0 to:1]; 163 | }); 164 | //开始左侧线条缩短动画 165 | __weak typeof(self)weakSelf = self; 166 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, animationDuration*0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ 167 | //左侧竖线动画 168 | [self strokeEndAnimationFrom:1 to:0 onLayer:weakSelf.leftLineLayer name:nil duration:animationDuration/2 delegate:nil]; 169 | }); 170 | } 171 | 172 | /** 173 | 执行逆向动画,即播放-》暂停 174 | */ 175 | - (void)actionInverseAnimation { 176 | //开始三角动画 177 | [self strokeEndAnimationFrom:1 to:0 onLayer:_triangleLayer name:TriangleAnimation duration:animationDuration delegate:self]; 178 | //开始左侧线条动画 179 | [self strokeEndAnimationFrom:0 to:1 onLayer:_leftLineLayer name:nil duration:animationDuration/2 delegate:nil]; 180 | //执行画弧动画 181 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, animationDuration*0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ 182 | [self circleStartAnimationFrom:1 to:0]; 183 | }); 184 | //执行反向画弧和右侧放大动画 185 | __weak typeof(self)weakSelf = self; 186 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, animationDuration*0.75 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ 187 | //右侧竖线动画 188 | [self strokeEndAnimationFrom:0 to:1 onLayer:weakSelf.rightLineLayer name:RightLineAnimation duration:animationDuration/4 delegate:self]; 189 | //圆弧动画 190 | [self strokeEndAnimationFrom:1 to:0 onLayer:weakSelf.circleLayer name:nil duration:animationDuration/4 delegate:nil]; 191 | }); 192 | } 193 | 194 | /** 195 | 通用执行strokeEnd动画 196 | */ 197 | - (CABasicAnimation *)strokeEndAnimationFrom:(CGFloat)fromValue to:(CGFloat)toValue onLayer:(CALayer *)layer name:(NSString*)animationName duration:(CGFloat)duration delegate:(id)delegate { 198 | CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 199 | strokeEndAnimation.duration = duration; 200 | strokeEndAnimation.fromValue = @(fromValue); 201 | strokeEndAnimation.toValue = @(toValue); 202 | strokeEndAnimation.fillMode = kCAFillModeForwards; 203 | strokeEndAnimation.removedOnCompletion = NO; 204 | [strokeEndAnimation setValue:animationName forKey:@"animationName"]; 205 | strokeEndAnimation.delegate = delegate; 206 | [layer addAnimation:strokeEndAnimation forKey:nil]; 207 | return strokeEndAnimation; 208 | } 209 | 210 | /** 211 | 画弧改变起始位置动画 212 | */ 213 | - (void)circleStartAnimationFrom:(CGFloat)fromValue to:(CGFloat)toValue { 214 | CABasicAnimation *circleAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"]; 215 | circleAnimation.duration = animationDuration/4; 216 | circleAnimation.fromValue = @(fromValue); 217 | circleAnimation.toValue = @(toValue); 218 | circleAnimation.fillMode = kCAFillModeForwards; 219 | circleAnimation.removedOnCompletion = NO; 220 | [_circleLayer addAnimation:circleAnimation forKey:nil]; 221 | } 222 | 223 | 224 | #pragma mark - 225 | #pragma mark 动画开始、结束代理方法 226 | 227 | //为了避免动画结束回到原点后会有一个原点显示在屏幕上需要做一些处理,就是改变layer的lineCap属性 228 | -(void)animationDidStart:(CAAnimation *)anim { 229 | NSString *name = [anim valueForKey:@"animationName"]; 230 | bool isTriangle = [name isEqualToString:TriangleAnimation]; 231 | bool isRightLine = [name isEqualToString:RightLineAnimation]; 232 | if (isTriangle) { 233 | _triangleLayer.lineCap = kCALineCapRound; 234 | }else if (isRightLine){ 235 | _rightLineLayer.lineCap = kCALineCapRound; 236 | } 237 | } 238 | 239 | -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 240 | NSString *name = [anim valueForKey:@"animationName"]; 241 | bool isTriangle = [name isEqualToString:TriangleAnimation]; 242 | bool isRightLine = [name isEqualToString:RightLineAnimation]; 243 | if (_buttonState == iQiYiPlayButtonStatePlay && isRightLine) { 244 | _rightLineLayer.lineCap = kCALineCapButt; 245 | } else if (isTriangle) { 246 | _triangleLayer.lineCap = kCALineCapButt; 247 | } 248 | } 249 | 250 | #pragma mark - 251 | #pragma mark 其他方法 252 | //线条宽度,根据按钮的宽度按比例设置 253 | - (CGFloat)lineWidth { 254 | return self.bounds.size.width * 0.2; 255 | } 256 | 257 | 258 | #pragma mark - 259 | #pragma mark 竖线动画 260 | //暂停-》播放竖线动画 261 | - (void)linePositiveAnimation { 262 | CGFloat a = self.bounds.size.width; 263 | 264 | //左侧缩放动画 265 | UIBezierPath *leftPath1 = [UIBezierPath bezierPath]; 266 | [leftPath1 moveToPoint:CGPointMake(0.2*a,0.4*a)]; 267 | [leftPath1 addLineToPoint:CGPointMake(0.2*a,a)]; 268 | _leftLineLayer.path = leftPath1.CGPath; 269 | [_leftLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil]; 270 | 271 | //右侧竖线位移动画 272 | UIBezierPath *rightPath1 = [UIBezierPath bezierPath]; 273 | [rightPath1 moveToPoint:CGPointMake(0.8*a, 0.8*a)]; 274 | [rightPath1 addLineToPoint:CGPointMake(0.8*a,-0.2*a)]; 275 | _rightLineLayer.path = rightPath1.CGPath; 276 | [_rightLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil]; 277 | __weak typeof(self)weakSelf = self; 278 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, positionDuration/2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ 279 | //左侧位移动画 280 | UIBezierPath *leftPath2 = [UIBezierPath bezierPath]; 281 | [leftPath2 moveToPoint:CGPointMake(a*0.2,a*0.2)]; 282 | [leftPath2 addLineToPoint:CGPointMake(a*0.2,a*0.8)]; 283 | weakSelf.leftLineLayer.path = leftPath2.CGPath; 284 | [weakSelf.leftLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil]; 285 | 286 | //右侧竖线缩放动画 287 | UIBezierPath *rightPath2 = [UIBezierPath bezierPath]; 288 | [rightPath2 moveToPoint:CGPointMake(a*0.8,a*0.8)]; 289 | [rightPath2 addLineToPoint:CGPointMake(a*0.8,a*0.2)]; 290 | weakSelf.rightLineLayer.path = rightPath2.CGPath; 291 | [weakSelf.rightLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil]; 292 | }); 293 | } 294 | 295 | //播放-》暂停竖线动画 296 | - (void)lineInverseAnimation { 297 | 298 | CGFloat a = self.bounds.size.width; 299 | //左侧位移动画 300 | UIBezierPath *leftPath1 = [UIBezierPath bezierPath]; 301 | [leftPath1 moveToPoint:CGPointMake(0.2*a,0.4*a)]; 302 | [leftPath1 addLineToPoint:CGPointMake(0.2*a,a)]; 303 | _leftLineLayer.path = leftPath1.CGPath; 304 | [_leftLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil]; 305 | 306 | //右侧竖线位移动画 307 | UIBezierPath *rightPath1 = [UIBezierPath bezierPath]; 308 | [rightPath1 moveToPoint:CGPointMake(0.8*a, 0.8*a)]; 309 | [rightPath1 addLineToPoint:CGPointMake(0.8*a,-0.2*a)]; 310 | _rightLineLayer.path = rightPath1.CGPath; 311 | [_rightLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil]; 312 | 313 | __weak typeof(self)weakSelf = self; 314 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, positionDuration/2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ 315 | //左侧竖线缩放动画 316 | UIBezierPath *leftPath2 = [UIBezierPath bezierPath]; 317 | [leftPath2 moveToPoint:CGPointMake(a*0.2,0)]; 318 | [leftPath2 addLineToPoint:CGPointMake(a*0.2,a)]; 319 | weakSelf.leftLineLayer.path = leftPath2.CGPath; 320 | [weakSelf.leftLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil]; 321 | 322 | //右侧竖线缩放动画 323 | UIBezierPath *rightPath2 = [UIBezierPath bezierPath]; 324 | [rightPath2 moveToPoint:CGPointMake(a*0.8,a)]; 325 | [rightPath2 addLineToPoint:CGPointMake(a*0.8,0)]; 326 | weakSelf.rightLineLayer.path = rightPath2.CGPath; 327 | [weakSelf.rightLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil]; 328 | }); 329 | } 330 | 331 | /** 332 | 通用path动画方法 333 | */ 334 | - (CABasicAnimation *)pathAnimationWithDuration:(CGFloat)duration { 335 | CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; 336 | pathAnimation.duration = duration; 337 | pathAnimation.fillMode = kCAFillModeForwards; 338 | pathAnimation.removedOnCompletion = NO; 339 | return pathAnimation; 340 | } 341 | 342 | 343 | 344 | #pragma mark - 345 | #pragma mark Setter 346 | - (void)setButtonState:(iQiYiPlayButtonState)buttonState { 347 | //如果正在执行动画则不再执行下面操作 348 | if (_isAnimating == true) {return;} 349 | _buttonState = buttonState; 350 | 351 | if (buttonState == iQiYiPlayButtonStatePlay) {//暂停-》播放 352 | _isAnimating = true; 353 | //竖线正向动画 354 | [self linePositiveAnimation]; 355 | //再执行画弧、画三角动画 356 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, positionDuration * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ 357 | [self actionPositiveAnimation]; 358 | }); 359 | } else if (buttonState == iQiYiPlayButtonStatePause) {//播放-》暂停 360 | _isAnimating = true; 361 | //先执行画弧、画三角动画 362 | [self actionInverseAnimation]; 363 | //在执行竖线位移动画,结束动动画要比开始动画块 364 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, animationDuration * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ 365 | //竖线逆向动画 366 | [self lineInverseAnimation]; 367 | }); 368 | } 369 | //更新动画执行状态 370 | __weak typeof(self)weakSelf = self; 371 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (positionDuration + animationDuration) * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){ 372 | weakSelf.isAnimating = false; 373 | }); 374 | } 375 | 376 | 377 | @end 378 | --------------------------------------------------------------------------------