├── .gitignore ├── FSCustomButtonDemo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── FSCustomButtonDemo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── FSCustomBUtton │ ├── CALayer+FSUI.h │ ├── CALayer+FSUI.m │ ├── FSCommenDefine.h │ ├── FSCustomButton.h │ └── FSCustomButton.m ├── Info.plist ├── ViewController.h ├── ViewController.m ├── btn_voice_play@2x.png ├── graphicreview_group_star_red@2x.png ├── home_ic_pick@2x.png ├── jkj_sort_ic_baby_s.png ├── main.m ├── my_ic_coupon@2x.png └── my_ic_coupon@3x.png ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | -------------------------------------------------------------------------------- /FSCustomButtonDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A3424BD61F009FFC005CD7E2 /* my_ic_coupon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = A3424BD51F009FFC005CD7E2 /* my_ic_coupon@3x.png */; }; 11 | A3424BD81F00A003005CD7E2 /* my_ic_coupon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A3424BD71F00A003005CD7E2 /* my_ic_coupon@2x.png */; }; 12 | A35134001EEF7925000313CB /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A35133FF1EEF7925000313CB /* main.m */; }; 13 | A35134031EEF7925000313CB /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A35134021EEF7925000313CB /* AppDelegate.m */; }; 14 | A35134061EEF7925000313CB /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A35134051EEF7925000313CB /* ViewController.m */; }; 15 | A35134091EEF7925000313CB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A35134071EEF7925000313CB /* Main.storyboard */; }; 16 | A351340B1EEF7925000313CB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A351340A1EEF7925000313CB /* Assets.xcassets */; }; 17 | A351340E1EEF7925000313CB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A351340C1EEF7925000313CB /* LaunchScreen.storyboard */; }; 18 | A35134181EEF7987000313CB /* FSCustomButton.m in Sources */ = {isa = PBXBuildFile; fileRef = A35134171EEF7987000313CB /* FSCustomButton.m */; }; 19 | A38C0DB91EF6602B00822CAA /* btn_voice_play@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A38C0DB81EF6602B00822CAA /* btn_voice_play@2x.png */; }; 20 | A38C0DBB1EF6679700822CAA /* graphicreview_group_star_red@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A38C0DBA1EF6679700822CAA /* graphicreview_group_star_red@2x.png */; }; 21 | A3CF645E1EF0DB86009BE03C /* jkj_sort_ic_baby_s.png in Resources */ = {isa = PBXBuildFile; fileRef = A3CF645D1EF0DB86009BE03C /* jkj_sort_ic_baby_s.png */; }; 22 | A3CF64601EF0E44B009BE03C /* home_ic_pick@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A3CF645F1EF0E44B009BE03C /* home_ic_pick@2x.png */; }; 23 | A3CF64A51EF12F46009BE03C /* CALayer+FSUI.m in Sources */ = {isa = PBXBuildFile; fileRef = A3CF64A41EF12F46009BE03C /* CALayer+FSUI.m */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | A3424BD51F009FFC005CD7E2 /* my_ic_coupon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "my_ic_coupon@3x.png"; sourceTree = ""; }; 28 | A3424BD71F00A003005CD7E2 /* my_ic_coupon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "my_ic_coupon@2x.png"; sourceTree = ""; }; 29 | A35133FB1EEF7925000313CB /* FSCustomButtonDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FSCustomButtonDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | A35133FF1EEF7925000313CB /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 31 | A35134011EEF7925000313CB /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 32 | A35134021EEF7925000313CB /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 33 | A35134041EEF7925000313CB /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 34 | A35134051EEF7925000313CB /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 35 | A35134081EEF7925000313CB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 36 | A351340A1EEF7925000313CB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 37 | A351340D1EEF7925000313CB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 38 | A351340F1EEF7925000313CB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39 | A35134161EEF7987000313CB /* FSCustomButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSCustomButton.h; sourceTree = ""; }; 40 | A35134171EEF7987000313CB /* FSCustomButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSCustomButton.m; sourceTree = ""; }; 41 | A351341A1EEFC724000313CB /* FSCommenDefine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSCommenDefine.h; sourceTree = ""; }; 42 | A38C0DB81EF6602B00822CAA /* btn_voice_play@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "btn_voice_play@2x.png"; sourceTree = ""; }; 43 | A38C0DBA1EF6679700822CAA /* graphicreview_group_star_red@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "graphicreview_group_star_red@2x.png"; sourceTree = ""; }; 44 | A3CF645D1EF0DB86009BE03C /* jkj_sort_ic_baby_s.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = jkj_sort_ic_baby_s.png; sourceTree = ""; }; 45 | A3CF645F1EF0E44B009BE03C /* home_ic_pick@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "home_ic_pick@2x.png"; sourceTree = ""; }; 46 | A3CF64A31EF12F46009BE03C /* CALayer+FSUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CALayer+FSUI.h"; sourceTree = ""; }; 47 | A3CF64A41EF12F46009BE03C /* CALayer+FSUI.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CALayer+FSUI.m"; sourceTree = ""; }; 48 | /* End PBXFileReference section */ 49 | 50 | /* Begin PBXFrameworksBuildPhase section */ 51 | A35133F81EEF7925000313CB /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXFrameworksBuildPhase section */ 59 | 60 | /* Begin PBXGroup section */ 61 | A35133F21EEF7924000313CB = { 62 | isa = PBXGroup; 63 | children = ( 64 | A35133FD1EEF7925000313CB /* FSCustomButtonDemo */, 65 | A35133FC1EEF7925000313CB /* Products */, 66 | ); 67 | sourceTree = ""; 68 | }; 69 | A35133FC1EEF7925000313CB /* Products */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | A35133FB1EEF7925000313CB /* FSCustomButtonDemo.app */, 73 | ); 74 | name = Products; 75 | sourceTree = ""; 76 | }; 77 | A35133FD1EEF7925000313CB /* FSCustomButtonDemo */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | A3424BD71F00A003005CD7E2 /* my_ic_coupon@2x.png */, 81 | A3424BD51F009FFC005CD7E2 /* my_ic_coupon@3x.png */, 82 | A38C0DBA1EF6679700822CAA /* graphicreview_group_star_red@2x.png */, 83 | A38C0DB81EF6602B00822CAA /* btn_voice_play@2x.png */, 84 | A3CF645F1EF0E44B009BE03C /* home_ic_pick@2x.png */, 85 | A3CF645D1EF0DB86009BE03C /* jkj_sort_ic_baby_s.png */, 86 | A35134151EEF794D000313CB /* FSCustomBUtton */, 87 | A35134011EEF7925000313CB /* AppDelegate.h */, 88 | A35134021EEF7925000313CB /* AppDelegate.m */, 89 | A35134041EEF7925000313CB /* ViewController.h */, 90 | A35134051EEF7925000313CB /* ViewController.m */, 91 | A35134071EEF7925000313CB /* Main.storyboard */, 92 | A351340A1EEF7925000313CB /* Assets.xcassets */, 93 | A351340C1EEF7925000313CB /* LaunchScreen.storyboard */, 94 | A351340F1EEF7925000313CB /* Info.plist */, 95 | A35133FE1EEF7925000313CB /* Supporting Files */, 96 | ); 97 | path = FSCustomButtonDemo; 98 | sourceTree = ""; 99 | }; 100 | A35133FE1EEF7925000313CB /* Supporting Files */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | A35133FF1EEF7925000313CB /* main.m */, 104 | ); 105 | name = "Supporting Files"; 106 | sourceTree = ""; 107 | }; 108 | A35134151EEF794D000313CB /* FSCustomBUtton */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | A351341A1EEFC724000313CB /* FSCommenDefine.h */, 112 | A35134161EEF7987000313CB /* FSCustomButton.h */, 113 | A35134171EEF7987000313CB /* FSCustomButton.m */, 114 | A3CF64A31EF12F46009BE03C /* CALayer+FSUI.h */, 115 | A3CF64A41EF12F46009BE03C /* CALayer+FSUI.m */, 116 | ); 117 | path = FSCustomBUtton; 118 | sourceTree = ""; 119 | }; 120 | /* End PBXGroup section */ 121 | 122 | /* Begin PBXNativeTarget section */ 123 | A35133FA1EEF7925000313CB /* FSCustomButtonDemo */ = { 124 | isa = PBXNativeTarget; 125 | buildConfigurationList = A35134121EEF7925000313CB /* Build configuration list for PBXNativeTarget "FSCustomButtonDemo" */; 126 | buildPhases = ( 127 | A35133F71EEF7925000313CB /* Sources */, 128 | A35133F81EEF7925000313CB /* Frameworks */, 129 | A35133F91EEF7925000313CB /* Resources */, 130 | ); 131 | buildRules = ( 132 | ); 133 | dependencies = ( 134 | ); 135 | name = FSCustomButtonDemo; 136 | productName = FSCustomButtonDemo; 137 | productReference = A35133FB1EEF7925000313CB /* FSCustomButtonDemo.app */; 138 | productType = "com.apple.product-type.application"; 139 | }; 140 | /* End PBXNativeTarget section */ 141 | 142 | /* Begin PBXProject section */ 143 | A35133F31EEF7924000313CB /* Project object */ = { 144 | isa = PBXProject; 145 | attributes = { 146 | CLASSPREFIX = FS; 147 | LastUpgradeCheck = 0830; 148 | ORGANIZATIONNAME = fengshun; 149 | TargetAttributes = { 150 | A35133FA1EEF7925000313CB = { 151 | CreatedOnToolsVersion = 8.3.1; 152 | DevelopmentTeam = P5NAKU59Y6; 153 | ProvisioningStyle = Automatic; 154 | }; 155 | }; 156 | }; 157 | buildConfigurationList = A35133F61EEF7924000313CB /* Build configuration list for PBXProject "FSCustomButtonDemo" */; 158 | compatibilityVersion = "Xcode 3.2"; 159 | developmentRegion = English; 160 | hasScannedForEncodings = 0; 161 | knownRegions = ( 162 | en, 163 | Base, 164 | ); 165 | mainGroup = A35133F21EEF7924000313CB; 166 | productRefGroup = A35133FC1EEF7925000313CB /* Products */; 167 | projectDirPath = ""; 168 | projectRoot = ""; 169 | targets = ( 170 | A35133FA1EEF7925000313CB /* FSCustomButtonDemo */, 171 | ); 172 | }; 173 | /* End PBXProject section */ 174 | 175 | /* Begin PBXResourcesBuildPhase section */ 176 | A35133F91EEF7925000313CB /* Resources */ = { 177 | isa = PBXResourcesBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | A351340E1EEF7925000313CB /* LaunchScreen.storyboard in Resources */, 181 | A351340B1EEF7925000313CB /* Assets.xcassets in Resources */, 182 | A3424BD81F00A003005CD7E2 /* my_ic_coupon@2x.png in Resources */, 183 | A38C0DBB1EF6679700822CAA /* graphicreview_group_star_red@2x.png in Resources */, 184 | A3CF64601EF0E44B009BE03C /* home_ic_pick@2x.png in Resources */, 185 | A3CF645E1EF0DB86009BE03C /* jkj_sort_ic_baby_s.png in Resources */, 186 | A3424BD61F009FFC005CD7E2 /* my_ic_coupon@3x.png in Resources */, 187 | A38C0DB91EF6602B00822CAA /* btn_voice_play@2x.png in Resources */, 188 | A35134091EEF7925000313CB /* Main.storyboard in Resources */, 189 | ); 190 | runOnlyForDeploymentPostprocessing = 0; 191 | }; 192 | /* End PBXResourcesBuildPhase section */ 193 | 194 | /* Begin PBXSourcesBuildPhase section */ 195 | A35133F71EEF7925000313CB /* Sources */ = { 196 | isa = PBXSourcesBuildPhase; 197 | buildActionMask = 2147483647; 198 | files = ( 199 | A35134061EEF7925000313CB /* ViewController.m in Sources */, 200 | A35134031EEF7925000313CB /* AppDelegate.m in Sources */, 201 | A35134001EEF7925000313CB /* main.m in Sources */, 202 | A3CF64A51EF12F46009BE03C /* CALayer+FSUI.m in Sources */, 203 | A35134181EEF7987000313CB /* FSCustomButton.m in Sources */, 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | /* End PBXSourcesBuildPhase section */ 208 | 209 | /* Begin PBXVariantGroup section */ 210 | A35134071EEF7925000313CB /* Main.storyboard */ = { 211 | isa = PBXVariantGroup; 212 | children = ( 213 | A35134081EEF7925000313CB /* Base */, 214 | ); 215 | name = Main.storyboard; 216 | sourceTree = ""; 217 | }; 218 | A351340C1EEF7925000313CB /* LaunchScreen.storyboard */ = { 219 | isa = PBXVariantGroup; 220 | children = ( 221 | A351340D1EEF7925000313CB /* Base */, 222 | ); 223 | name = LaunchScreen.storyboard; 224 | sourceTree = ""; 225 | }; 226 | /* End PBXVariantGroup section */ 227 | 228 | /* Begin XCBuildConfiguration section */ 229 | A35134101EEF7925000313CB /* Debug */ = { 230 | isa = XCBuildConfiguration; 231 | buildSettings = { 232 | ALWAYS_SEARCH_USER_PATHS = NO; 233 | CLANG_ANALYZER_NONNULL = YES; 234 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 235 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 236 | CLANG_CXX_LIBRARY = "libc++"; 237 | CLANG_ENABLE_MODULES = YES; 238 | CLANG_ENABLE_OBJC_ARC = YES; 239 | CLANG_WARN_BOOL_CONVERSION = YES; 240 | CLANG_WARN_CONSTANT_CONVERSION = YES; 241 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 242 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 243 | CLANG_WARN_EMPTY_BODY = YES; 244 | CLANG_WARN_ENUM_CONVERSION = YES; 245 | CLANG_WARN_INFINITE_RECURSION = YES; 246 | CLANG_WARN_INT_CONVERSION = YES; 247 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 248 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 249 | CLANG_WARN_UNREACHABLE_CODE = YES; 250 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 251 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 252 | COPY_PHASE_STRIP = NO; 253 | DEBUG_INFORMATION_FORMAT = dwarf; 254 | ENABLE_STRICT_OBJC_MSGSEND = YES; 255 | ENABLE_TESTABILITY = YES; 256 | GCC_C_LANGUAGE_STANDARD = gnu99; 257 | GCC_DYNAMIC_NO_PIC = NO; 258 | GCC_NO_COMMON_BLOCKS = YES; 259 | GCC_OPTIMIZATION_LEVEL = 0; 260 | GCC_PREPROCESSOR_DEFINITIONS = ( 261 | "DEBUG=1", 262 | "$(inherited)", 263 | ); 264 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 265 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 266 | GCC_WARN_UNDECLARED_SELECTOR = YES; 267 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 268 | GCC_WARN_UNUSED_FUNCTION = YES; 269 | GCC_WARN_UNUSED_VARIABLE = YES; 270 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 271 | MTL_ENABLE_DEBUG_INFO = YES; 272 | ONLY_ACTIVE_ARCH = YES; 273 | SDKROOT = iphoneos; 274 | }; 275 | name = Debug; 276 | }; 277 | A35134111EEF7925000313CB /* Release */ = { 278 | isa = XCBuildConfiguration; 279 | buildSettings = { 280 | ALWAYS_SEARCH_USER_PATHS = NO; 281 | CLANG_ANALYZER_NONNULL = YES; 282 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 283 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 284 | CLANG_CXX_LIBRARY = "libc++"; 285 | CLANG_ENABLE_MODULES = YES; 286 | CLANG_ENABLE_OBJC_ARC = YES; 287 | CLANG_WARN_BOOL_CONVERSION = YES; 288 | CLANG_WARN_CONSTANT_CONVERSION = YES; 289 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 290 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 291 | CLANG_WARN_EMPTY_BODY = YES; 292 | CLANG_WARN_ENUM_CONVERSION = YES; 293 | CLANG_WARN_INFINITE_RECURSION = YES; 294 | CLANG_WARN_INT_CONVERSION = YES; 295 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 296 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 297 | CLANG_WARN_UNREACHABLE_CODE = YES; 298 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 299 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 300 | COPY_PHASE_STRIP = NO; 301 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 302 | ENABLE_NS_ASSERTIONS = NO; 303 | ENABLE_STRICT_OBJC_MSGSEND = YES; 304 | GCC_C_LANGUAGE_STANDARD = gnu99; 305 | GCC_NO_COMMON_BLOCKS = YES; 306 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 307 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 308 | GCC_WARN_UNDECLARED_SELECTOR = YES; 309 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 310 | GCC_WARN_UNUSED_FUNCTION = YES; 311 | GCC_WARN_UNUSED_VARIABLE = YES; 312 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 313 | MTL_ENABLE_DEBUG_INFO = NO; 314 | SDKROOT = iphoneos; 315 | VALIDATE_PRODUCT = YES; 316 | }; 317 | name = Release; 318 | }; 319 | A35134131EEF7925000313CB /* Debug */ = { 320 | isa = XCBuildConfiguration; 321 | buildSettings = { 322 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 323 | DEVELOPMENT_TEAM = P5NAKU59Y6; 324 | INFOPLIST_FILE = FSCustomButtonDemo/Info.plist; 325 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 326 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 327 | PRODUCT_BUNDLE_IDENTIFIER = com.huim.FSCustomButtonDemo; 328 | PRODUCT_NAME = "$(TARGET_NAME)"; 329 | }; 330 | name = Debug; 331 | }; 332 | A35134141EEF7925000313CB /* Release */ = { 333 | isa = XCBuildConfiguration; 334 | buildSettings = { 335 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 336 | DEVELOPMENT_TEAM = P5NAKU59Y6; 337 | INFOPLIST_FILE = FSCustomButtonDemo/Info.plist; 338 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 339 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 340 | PRODUCT_BUNDLE_IDENTIFIER = com.huim.FSCustomButtonDemo; 341 | PRODUCT_NAME = "$(TARGET_NAME)"; 342 | }; 343 | name = Release; 344 | }; 345 | /* End XCBuildConfiguration section */ 346 | 347 | /* Begin XCConfigurationList section */ 348 | A35133F61EEF7924000313CB /* Build configuration list for PBXProject "FSCustomButtonDemo" */ = { 349 | isa = XCConfigurationList; 350 | buildConfigurations = ( 351 | A35134101EEF7925000313CB /* Debug */, 352 | A35134111EEF7925000313CB /* Release */, 353 | ); 354 | defaultConfigurationIsVisible = 0; 355 | defaultConfigurationName = Release; 356 | }; 357 | A35134121EEF7925000313CB /* Build configuration list for PBXNativeTarget "FSCustomButtonDemo" */ = { 358 | isa = XCConfigurationList; 359 | buildConfigurations = ( 360 | A35134131EEF7925000313CB /* Debug */, 361 | A35134141EEF7925000313CB /* Release */, 362 | ); 363 | defaultConfigurationIsVisible = 0; 364 | defaultConfigurationName = Release; 365 | }; 366 | /* End XCConfigurationList section */ 367 | }; 368 | rootObject = A35133F31EEF7924000313CB /* Project object */; 369 | } 370 | -------------------------------------------------------------------------------- /FSCustomButtonDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FSCustomButtonDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /FSCustomButtonDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // FSCustomButtonDemo 4 | // 5 | // Created by huim on 2017/6/13. 6 | // Copyright © 2017年 fengshun. 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 | -------------------------------------------------------------------------------- /FSCustomButtonDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // FSCustomButtonDemo 4 | // 5 | // Created by huim on 2017/6/13. 6 | // Copyright © 2017年 fengshun. 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 | -------------------------------------------------------------------------------- /FSCustomButtonDemo/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 | } -------------------------------------------------------------------------------- /FSCustomButtonDemo/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 | -------------------------------------------------------------------------------- /FSCustomButtonDemo/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 | -------------------------------------------------------------------------------- /FSCustomButtonDemo/FSCustomBUtton/CALayer+FSUI.h: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+FSUI.h 3 | // FSCustomButtonDemo 4 | // 5 | // Created by huim on 2017/6/14. 6 | // Copyright © 2017年 fengshun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CALayer (FSUI) 12 | /** 13 | * 把某个sublayer移动到当前所有sublayers的最后面 14 | * @param sublayer 要被移动的layer 15 | * @warning 要被移动的sublayer必须已经添加到当前layer上 16 | */ 17 | - (void)FS_sendSublayerToBack:(CALayer *)sublayer; 18 | 19 | /** 20 | * 把某个sublayer移动到当前所有sublayers的最前面 21 | * @param sublayer 要被移动的layer 22 | * @warning 要被移动的sublayer必须已经添加到当前layer上 23 | */ 24 | - (void)FS_bringSublayerToFront:(CALayer *)sublayer; 25 | 26 | /** 27 | * 移除 CALayer(包括 CAShapeLayer 和 CAGradientLayer)所有支持动画的属性的默认动画,方便需要一个不带动画的 layer 时使用。 28 | */ 29 | - (void)FS_removeDefaultAnimations; 30 | @end 31 | -------------------------------------------------------------------------------- /FSCustomButtonDemo/FSCustomBUtton/CALayer+FSUI.m: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+FSUI.m 3 | // FSCustomButtonDemo 4 | // 5 | // Created by huim on 2017/6/14. 6 | // Copyright © 2017年 fengshun. All rights reserved. 7 | // 8 | 9 | #import "CALayer+FSUI.h" 10 | #import "FSCommenDefine.h" 11 | 12 | @implementation CALayer (FSUI) 13 | 14 | - (void)FS_sendSublayerToBack:(CALayer *)sublayer { 15 | if (sublayer.superlayer == self) { 16 | [sublayer removeFromSuperlayer]; 17 | [self insertSublayer:sublayer atIndex:0]; 18 | } 19 | } 20 | 21 | - (void)FS_bringSublayerToFront:(CALayer *)sublayer { 22 | if (sublayer.superlayer == self) { 23 | [sublayer removeFromSuperlayer]; 24 | [self insertSublayer:sublayer atIndex:(unsigned)self.sublayers.count]; 25 | } 26 | } 27 | 28 | - (void)FS_removeDefaultAnimations { 29 | NSMutableDictionary> *actions = @{NSStringFromSelector(@selector(bounds)): [NSNull null], 30 | NSStringFromSelector(@selector(position)): [NSNull null], 31 | NSStringFromSelector(@selector(zPosition)): [NSNull null], 32 | NSStringFromSelector(@selector(anchorPoint)): [NSNull null], 33 | NSStringFromSelector(@selector(anchorPointZ)): [NSNull null], 34 | NSStringFromSelector(@selector(transform)): [NSNull null], 35 | BeginIgnoreClangWarning(-Wundeclared-selector) 36 | NSStringFromSelector(@selector(hidden)): [NSNull null], 37 | NSStringFromSelector(@selector(doubleSided)): [NSNull null], 38 | EndIgnoreClangWarning 39 | NSStringFromSelector(@selector(sublayerTransform)): [NSNull null], 40 | NSStringFromSelector(@selector(masksToBounds)): [NSNull null], 41 | NSStringFromSelector(@selector(contents)): [NSNull null], 42 | NSStringFromSelector(@selector(contentsRect)): [NSNull null], 43 | NSStringFromSelector(@selector(contentsScale)): [NSNull null], 44 | NSStringFromSelector(@selector(contentsCenter)): [NSNull null], 45 | NSStringFromSelector(@selector(minificationFilterBias)): [NSNull null], 46 | NSStringFromSelector(@selector(backgroundColor)): [NSNull null], 47 | NSStringFromSelector(@selector(cornerRadius)): [NSNull null], 48 | NSStringFromSelector(@selector(borderWidth)): [NSNull null], 49 | NSStringFromSelector(@selector(borderColor)): [NSNull null], 50 | NSStringFromSelector(@selector(opacity)): [NSNull null], 51 | NSStringFromSelector(@selector(compositingFilter)): [NSNull null], 52 | NSStringFromSelector(@selector(filters)): [NSNull null], 53 | NSStringFromSelector(@selector(backgroundFilters)): [NSNull null], 54 | NSStringFromSelector(@selector(shouldRasterize)): [NSNull null], 55 | NSStringFromSelector(@selector(rasterizationScale)): [NSNull null], 56 | NSStringFromSelector(@selector(shadowColor)): [NSNull null], 57 | NSStringFromSelector(@selector(shadowOpacity)): [NSNull null], 58 | NSStringFromSelector(@selector(shadowOffset)): [NSNull null], 59 | NSStringFromSelector(@selector(shadowRadius)): [NSNull null], 60 | NSStringFromSelector(@selector(shadowPath)): [NSNull null]}.mutableCopy; 61 | 62 | if ([self isKindOfClass:[CAShapeLayer class]]) { 63 | [actions addEntriesFromDictionary:@{NSStringFromSelector(@selector(path)): [NSNull null], 64 | NSStringFromSelector(@selector(fillColor)): [NSNull null], 65 | NSStringFromSelector(@selector(strokeColor)): [NSNull null], 66 | NSStringFromSelector(@selector(strokeStart)): [NSNull null], 67 | NSStringFromSelector(@selector(strokeEnd)): [NSNull null], 68 | NSStringFromSelector(@selector(lineWidth)): [NSNull null], 69 | NSStringFromSelector(@selector(miterLimit)): [NSNull null], 70 | NSStringFromSelector(@selector(lineDashPhase)): [NSNull null]}]; 71 | } 72 | 73 | if ([self isKindOfClass:[CAGradientLayer class]]) { 74 | [actions addEntriesFromDictionary:@{NSStringFromSelector(@selector(colors)): [NSNull null], 75 | NSStringFromSelector(@selector(locations)): [NSNull null], 76 | NSStringFromSelector(@selector(startPoint)): [NSNull null], 77 | NSStringFromSelector(@selector(endPoint)): [NSNull null]}]; 78 | } 79 | 80 | self.actions = actions; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /FSCustomButtonDemo/FSCustomBUtton/FSCommenDefine.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | #import 4 | 5 | #define ScreenScale ([[UIScreen mainScreen] scale]) 6 | 7 | #pragma mark - Clang 8 | 9 | #define ArgumentToString(macro) #macro 10 | #define ClangWarningConcat(warning_name) ArgumentToString(clang diagnostic ignored warning_name) 11 | 12 | #define BeginIgnoreClangWarning(warningName) _Pragma("clang diagnostic push") _Pragma(ClangWarningConcat(#warningName)) 13 | #define EndIgnoreClangWarning _Pragma("clang diagnostic pop") 14 | 15 | #define BeginIgnorePerformSelectorLeaksWarning BeginIgnoreClangWarning(-Warc-performSelector-leaks) 16 | #define EndIgnorePerformSelectorLeaksWarning EndIgnoreClangWarning 17 | 18 | #define BeginIgnoreAvailabilityWarning BeginIgnoreClangWarning(-Wpartial-availability) 19 | #define EndIgnoreAvailabilityWarning EndIgnoreClangWarning 20 | 21 | #define BeginIgnoreDeprecatedWarning BeginIgnoreClangWarning(-Wdeprecated-declarations) 22 | #define EndIgnoreDeprecatedWarning EndIgnoreClangWarning 23 | #pragma mark - 方法-C对象、结构操作 24 | 25 | /** 26 | * 基于指定的倍数,对传进来的 floatValue 进行像素取整。若指定倍数为0,则表示以当前设备的屏幕倍数为准。 27 | * 28 | * 例如传进来 “2.1”,在 2x 倍数下会返回 2.5(0.5pt 对应 1px),在 3x 倍数下会返回 2.333(0.333pt 对应 1px)。 29 | */ 30 | CG_INLINE CGFloat 31 | flatSpecificScale(CGFloat floatValue, CGFloat scale) { 32 | scale = scale == 0 ? ScreenScale : scale; 33 | CGFloat flattedValue = ceil(floatValue * scale) / scale; 34 | return flattedValue; 35 | } 36 | 37 | /** 38 | * 基于当前设备的屏幕倍数,对传进来的 floatValue 进行像素取整。 39 | * 40 | * 注意如果在 Core Graphic 绘图里使用时,要注意当前画布的倍数是否和设备屏幕倍数一致,若不一致,不可使用 flat() 函数,而应该用 flatSpecificScale 41 | */ 42 | CG_INLINE CGFloat 43 | flat(CGFloat floatValue) { 44 | return flatSpecificScale(floatValue, 0); 45 | } 46 | 47 | /** 48 | * 类似flat(),只不过 flat 是向上取整,而 floorInPixel 是向下取整 49 | */ 50 | CG_INLINE CGFloat 51 | floorInPixel(CGFloat floatValue) { 52 | CGFloat resultValue = floor(floatValue * ScreenScale) / ScreenScale; 53 | return resultValue; 54 | } 55 | 56 | CG_INLINE BOOL 57 | between(CGFloat minimumValue, CGFloat value, CGFloat maximumValue) { 58 | return minimumValue < value && value < maximumValue; 59 | } 60 | 61 | CG_INLINE BOOL 62 | betweenOrEqual(CGFloat minimumValue, CGFloat value, CGFloat maximumValue) { 63 | return minimumValue <= value && value <= maximumValue; 64 | } 65 | 66 | CG_INLINE void 67 | ReplaceMethod(Class _class, SEL _originSelector, SEL _newSelector) { 68 | Method oriMethod = class_getInstanceMethod(_class, _originSelector); 69 | Method newMethod = class_getInstanceMethod(_class, _newSelector); 70 | BOOL isAddedMethod = class_addMethod(_class, _originSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)); 71 | if (isAddedMethod) { 72 | class_replaceMethod(_class, _newSelector, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod)); 73 | } else { 74 | method_exchangeImplementations(oriMethod, newMethod); 75 | } 76 | } 77 | 78 | #pragma mark - CGFloat 79 | 80 | /// 用于居中运算 81 | CG_INLINE CGFloat 82 | CGFloatGetCenter(CGFloat parent, CGFloat child) { 83 | return flat((parent - child) / 2.0); 84 | } 85 | 86 | #pragma mark - CGPoint 87 | 88 | /// 两个point相加 89 | CG_INLINE CGPoint 90 | CGPointUnion(CGPoint point1, CGPoint point2) { 91 | return CGPointMake(flat(point1.x + point2.x), flat(point1.y + point2.y)); 92 | } 93 | 94 | /// 获取rect的center,包括rect本身的x/y偏移 95 | CG_INLINE CGPoint 96 | CGPointGetCenterWithRect(CGRect rect) { 97 | return CGPointMake(flat(CGRectGetMidX(rect)), flat(CGRectGetMidY(rect))); 98 | } 99 | 100 | CG_INLINE CGPoint 101 | CGPointGetCenterWithSize(CGSize size) { 102 | return CGPointMake(flat(size.width / 2.0), flat(size.height / 2.0)); 103 | } 104 | 105 | #pragma mark - UIEdgeInsets 106 | 107 | // 获取UIEdgeInsets在水平方向上的值 108 | CG_INLINE CGFloat 109 | UIEdgeInsetsGetHorizontalValue(UIEdgeInsets insets) { 110 | return insets.left + insets.right; 111 | } 112 | 113 | // 获取UIEdgeInsets在垂直方向上的值 114 | CG_INLINE CGFloat 115 | UIEdgeInsetsGetVerticalValue(UIEdgeInsets insets) { 116 | return insets.top + insets.bottom; 117 | } 118 | 119 | // 将两个UIEdgeInsets合并为一个 120 | CG_INLINE UIEdgeInsets 121 | UIEdgeInsetsConcat(UIEdgeInsets insets1, UIEdgeInsets insets2) { 122 | insets1.top += insets2.top; 123 | insets1.left += insets2.left; 124 | insets1.bottom += insets2.bottom; 125 | insets1.right += insets2.right; 126 | return insets1; 127 | } 128 | 129 | CG_INLINE UIEdgeInsets 130 | UIEdgeInsetsSetTop(UIEdgeInsets insets, CGFloat top) { 131 | insets.top = flat(top); 132 | return insets; 133 | } 134 | 135 | CG_INLINE UIEdgeInsets 136 | UIEdgeInsetsSetLeft(UIEdgeInsets insets, CGFloat left) { 137 | insets.left = flat(left); 138 | return insets; 139 | } 140 | CG_INLINE UIEdgeInsets 141 | UIEdgeInsetsSetBottom(UIEdgeInsets insets, CGFloat bottom) { 142 | insets.bottom = flat(bottom); 143 | return insets; 144 | } 145 | 146 | CG_INLINE UIEdgeInsets 147 | UIEdgeInsetsSetRight(UIEdgeInsets insets, CGFloat right) { 148 | insets.right = flat(right); 149 | return insets; 150 | } 151 | 152 | #pragma mark - CGSize 153 | 154 | /// 判断一个size是否为空(宽或高为0) 155 | CG_INLINE BOOL 156 | CGSizeIsEmpty(CGSize size) { 157 | return size.width <= 0 || size.height <= 0; 158 | } 159 | 160 | /// 将一个CGSize像素对齐 161 | CG_INLINE CGSize 162 | CGSizeFlatted(CGSize size) { 163 | return CGSizeMake(flat(size.width), flat(size.height)); 164 | } 165 | 166 | /// 将一个 CGSize 以 pt 为单位向上取整 167 | CG_INLINE CGSize 168 | CGSizeCeil(CGSize size) { 169 | return CGSizeMake(ceil(size.width), ceil(size.height)); 170 | } 171 | 172 | /// 将一个 CGSize 以 pt 为单位向下取整 173 | CG_INLINE CGSize 174 | CGSizeFloor(CGSize size) { 175 | return CGSizeMake(floor(size.width), floor(size.height)); 176 | } 177 | 178 | #pragma mark - CGRect 179 | 180 | /// 判断一个CGRect是否存在NaN 181 | CG_INLINE BOOL 182 | CGRectIsNaN(CGRect rect) { 183 | return isnan(rect.origin.x) || isnan(rect.origin.y) || isnan(rect.size.width) || isnan(rect.size.height); 184 | } 185 | 186 | /// 创建一个像素对齐的CGRect 187 | CG_INLINE CGRect 188 | CGRectFlatMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height) { 189 | return CGRectMake(flat(x), flat(y), flat(width), flat(height)); 190 | } 191 | 192 | /// 对CGRect的x/y、width/height都调用一次flat,以保证像素对齐 193 | CG_INLINE CGRect 194 | CGRectFlatted(CGRect rect) { 195 | return CGRectMake(flat(rect.origin.x), flat(rect.origin.y), flat(rect.size.width), flat(rect.size.height)); 196 | } 197 | 198 | /// 为一个CGRect叠加scale计算 199 | CG_INLINE CGRect 200 | CGRectApplyScale(CGRect rect, CGFloat scale) { 201 | return CGRectFlatted(CGRectMake(CGRectGetMinX(rect) * scale, CGRectGetMinY(rect) * scale, CGRectGetWidth(rect) * scale, CGRectGetHeight(rect) * scale)); 202 | } 203 | 204 | /// 计算view的水平居中,传入父view和子view的frame,返回子view在水平居中时的x值 205 | CG_INLINE CGFloat 206 | CGRectGetMinXHorizontallyCenterInParentRect(CGRect parentRect, CGRect childRect) { 207 | return flat((CGRectGetWidth(parentRect) - CGRectGetWidth(childRect)) / 2.0); 208 | } 209 | 210 | /// 计算view的垂直居中,传入父view和子view的frame,返回子view在垂直居中时的y值 211 | CG_INLINE CGFloat 212 | CGRectGetMinYVerticallyCenterInParentRect(CGRect parentRect, CGRect childRect) { 213 | return flat((CGRectGetHeight(parentRect) - CGRectGetHeight(childRect)) / 2.0); 214 | } 215 | 216 | /// 返回值:同一个坐标系内,想要layoutingRect和已布局完成的referenceRect保持垂直居中时,layoutingRect的originY 217 | CG_INLINE CGFloat 218 | CGRectGetMinYVerticallyCenter(CGRect referenceRect, CGRect layoutingRect) { 219 | return CGRectGetMinY(referenceRect) + CGRectGetMinYVerticallyCenterInParentRect(referenceRect, layoutingRect); 220 | } 221 | 222 | /// 返回值:同一个坐标系内,想要layoutingRect和已布局完成的referenceRect保持水平居中时,layoutingRect的originX 223 | CG_INLINE CGFloat 224 | CGRectGetMinXHorizontallyCenter(CGRect referenceRect, CGRect layoutingRect) { 225 | return CGRectGetMinX(referenceRect) + CGRectGetMinXHorizontallyCenterInParentRect(referenceRect, layoutingRect); 226 | } 227 | 228 | /// 为给定的rect往内部缩小insets的大小 229 | CG_INLINE CGRect 230 | CGRectInsetEdges(CGRect rect, UIEdgeInsets insets) { 231 | rect.origin.x += insets.left; 232 | rect.origin.y += insets.top; 233 | rect.size.width -= UIEdgeInsetsGetHorizontalValue(insets); 234 | rect.size.height -= UIEdgeInsetsGetVerticalValue(insets); 235 | return rect; 236 | } 237 | 238 | /// 传入size,返回一个x/y为0的CGRect 239 | CG_INLINE CGRect 240 | CGRectMakeWithSize(CGSize size) { 241 | return CGRectMake(0, 0, size.width, size.height); 242 | } 243 | 244 | CG_INLINE CGRect 245 | CGRectFloatTop(CGRect rect, CGFloat top) { 246 | rect.origin.y = top; 247 | return rect; 248 | } 249 | 250 | CG_INLINE CGRect 251 | CGRectFloatBottom(CGRect rect, CGFloat bottom) { 252 | rect.origin.y = bottom - CGRectGetHeight(rect); 253 | return rect; 254 | } 255 | 256 | CG_INLINE CGRect 257 | CGRectFloatRight(CGRect rect, CGFloat right) { 258 | rect.origin.x = right - CGRectGetWidth(rect); 259 | return rect; 260 | } 261 | 262 | CG_INLINE CGRect 263 | CGRectFloatLeft(CGRect rect, CGFloat left) { 264 | rect.origin.x = left; 265 | return rect; 266 | } 267 | 268 | /// 保持rect的左边缘不变,改变其宽度,使右边缘靠在right上 269 | CG_INLINE CGRect 270 | CGRectLimitRight(CGRect rect, CGFloat rightLimit) { 271 | rect.size.width = rightLimit - rect.origin.x; 272 | return rect; 273 | } 274 | 275 | /// 保持rect右边缘不变,改变其宽度和origin.x,使其左边缘靠在left上。只适合那种右边缘不动的view 276 | /// 先改变origin.x,让其靠在offset上 277 | /// 再改变size.width,减少同样的宽度,以抵消改变origin.x带来的view移动,从而保证view的右边缘是不动的 278 | CG_INLINE CGRect 279 | CGRectLimitLeft(CGRect rect, CGFloat leftLimit) { 280 | CGFloat subOffset = leftLimit - rect.origin.x; 281 | rect.origin.x = leftLimit; 282 | rect.size.width = rect.size.width - subOffset; 283 | return rect; 284 | } 285 | 286 | /// 限制rect的宽度,超过最大宽度则截断,否则保持rect的宽度不变 287 | CG_INLINE CGRect 288 | CGRectLimitMaxWidth(CGRect rect, CGFloat maxWidth) { 289 | CGFloat width = CGRectGetWidth(rect); 290 | rect.size.width = width > maxWidth ? maxWidth : width; 291 | return rect; 292 | } 293 | 294 | CG_INLINE CGRect 295 | CGRectSetX(CGRect rect, CGFloat x) { 296 | rect.origin.x = flat(x); 297 | return rect; 298 | } 299 | 300 | CG_INLINE CGRect 301 | CGRectSetY(CGRect rect, CGFloat y) { 302 | rect.origin.y = flat(y); 303 | return rect; 304 | } 305 | 306 | CG_INLINE CGRect 307 | CGRectSetXY(CGRect rect, CGFloat x, CGFloat y) { 308 | rect.origin.x = flat(x); 309 | rect.origin.y = flat(y); 310 | return rect; 311 | } 312 | 313 | CG_INLINE CGRect 314 | CGRectSetWidth(CGRect rect, CGFloat width) { 315 | rect.size.width = flat(width); 316 | return rect; 317 | } 318 | 319 | CG_INLINE CGRect 320 | CGRectSetHeight(CGRect rect, CGFloat height) { 321 | rect.size.height = flat(height); 322 | return rect; 323 | } 324 | 325 | CG_INLINE CGRect 326 | CGRectSetSize(CGRect rect, CGSize size) { 327 | rect.size = CGSizeFlatted(size); 328 | return rect; 329 | } 330 | -------------------------------------------------------------------------------- /FSCustomButtonDemo/FSCustomBUtton/FSCustomButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // FSCustomButton.h 3 | // FSCustomButtonDemo 4 | // 5 | // Created by huim on 2017/6/13. 6 | // Copyright © 2017年 fengshun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum : NSUInteger { 12 | FSCustomButtonImagePositionLeft,//图片在左 13 | FSCustomButtonImagePositionRight,//图片在右 14 | FSCustomButtonImagePositionTop,//图片在上 15 | FSCustomButtonImagePositionBottom,//图片在下 16 | } FSCustomButtonImagePosition; 17 | 18 | @interface FSCustomButton : UIButton 19 | 20 | /** 21 | 图片位置 22 | */ 23 | @property (nonatomic, assign) FSCustomButtonImagePosition buttonImagePosition; 24 | 25 | /** 26 | 文字颜色自动跟随tintColor调整,default NO 27 | */ 28 | @property(nonatomic, assign) BOOL adjustsTitleTintColorAutomatically; 29 | 30 | /** 31 | 图片颜色自动跟随tintColor调整,default NO 32 | */ 33 | @property(nonatomic, assign) BOOL adjustsImageTintColorAutomatically; 34 | 35 | /** 36 | default YES;相当于系统的adjustsImageWhenHighlighted 37 | */ 38 | @property(nonatomic, assign) BOOL adjustsButtonWhenHighlighted; 39 | 40 | /** 41 | default YES,相当于系统的adjustsImageWhenDisabled 42 | */ 43 | @property(nonatomic, assign) BOOL adjustsButtonWhenDisabled; 44 | 45 | /** 46 | 高亮状态button背景色,default nil,设置此属性后默认adjustsButtonWhenHighlighted=NO 47 | */ 48 | @property(nonatomic, strong) UIColor *highlightedBackgroundColor; 49 | 50 | /** 51 | 高亮状态边框颜色,default nil,设置此属性后默认adjustsButtonWhenHighlighted=NO 52 | */ 53 | @property(nonatomic, strong) UIColor *highlightedBorderColor; 54 | 55 | @property(nonatomic, copy) NSString *badgeNumber; 56 | 57 | @end 58 | 59 | @interface FSBadgeLab : UILabel 60 | 61 | @property(nonatomic, assign) UIEdgeInsets contentEdgeInsets; 62 | 63 | @end 64 | 65 | -------------------------------------------------------------------------------- /FSCustomButtonDemo/FSCustomBUtton/FSCustomButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // FSCustomButton.m 3 | // FSCustomButtonDemo 4 | // 5 | // Created by huim on 2017/6/13. 6 | // Copyright © 2017年 fengshun. All rights reserved. 7 | // 8 | 9 | #import "FSCustomButton.h" 10 | #import "FSCommenDefine.h" 11 | #import "CALayer+FSUI.h" 12 | 13 | @interface FSCustomButton () 14 | @property(nonatomic, strong) CALayer *highlightedBackgroundLayer; 15 | @property(nonatomic, strong) UIColor *originBorderColor; 16 | @property (nonatomic,strong) FSBadgeLab *badgeLabel; 17 | @end 18 | 19 | @implementation FSCustomButton 20 | 21 | - (instancetype)initWithFrame:(CGRect)frame 22 | { 23 | self = [super initWithFrame:frame]; 24 | if (self) { 25 | [self initialization]; 26 | } 27 | return self; 28 | } 29 | 30 | - (void)initialization 31 | { 32 | self.adjustsTitleTintColorAutomatically = NO; 33 | self.adjustsImageTintColorAutomatically = NO; 34 | self.tintColor = [UIColor colorWithRed:43/255 green:133/255 blue:208/255 alpha:1]; 35 | if (!self.adjustsTitleTintColorAutomatically) { 36 | [self setTitleColor:[UIColor colorWithRed:43/255 green:133/255 blue:208/255 alpha:1] forState:UIControlStateNormal]; 37 | } 38 | 39 | // 默认接管highlighted和disabled的表现,去掉系统默认的表现 40 | self.adjustsImageWhenHighlighted = NO; 41 | self.adjustsImageWhenDisabled = NO; 42 | self.adjustsButtonWhenHighlighted = YES; 43 | self.adjustsButtonWhenDisabled = YES; 44 | 45 | // iOS7以后的button,sizeToFit后默认会自带一个上下的contentInsets,为了保证按钮大小即为内容大小,这里直接去掉,改为一个最小的值。 46 | // 不能设为0,否则无效;也不能设置为小数点,否则无法像素对齐 47 | self.contentEdgeInsets = UIEdgeInsetsMake(1, 0, 1, 0); 48 | 49 | self.buttonImagePosition = FSCustomButtonImagePositionLeft;//默认布局 50 | 51 | } 52 | 53 | - (void)layoutSubviews 54 | { 55 | [super layoutSubviews]; 56 | //size存在空的情况 57 | if (CGRectIsEmpty(self.bounds)) { 58 | return; 59 | } 60 | //默认布局不用处理 61 | if (self.buttonImagePosition == FSCustomButtonImagePositionLeft) { 62 | return; 63 | } 64 | //content的实际size 65 | CGSize contentSize = CGSizeMake(CGRectGetWidth(self.bounds) - UIEdgeInsetsGetHorizontalValue(self.contentEdgeInsets), CGRectGetHeight(self.bounds) - UIEdgeInsetsGetVerticalValue(self.contentEdgeInsets)); 66 | //垂直布局 67 | if (self.buttonImagePosition == FSCustomButtonImagePositionTop || self.buttonImagePosition == FSCustomButtonImagePositionBottom) { 68 | 69 | CGFloat imageLimitWidth = contentSize.width - UIEdgeInsetsGetHorizontalValue(self.imageEdgeInsets); 70 | CGSize imageSize = [self.imageView sizeThatFits:CGSizeMake(imageLimitWidth, CGFLOAT_MAX)];// 假设图片高度必定完整显示 71 | CGRect imageFrame = CGRectMakeWithSize(imageSize); 72 | 73 | CGSize titleLimitSize = CGSizeMake(contentSize.width - UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets), contentSize.height - UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets) - imageSize.height - UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets)); 74 | CGSize titleSize = [self.titleLabel sizeThatFits:titleLimitSize]; 75 | titleSize.height = fminf(titleSize.height, titleLimitSize.height); 76 | CGRect titleFrame = CGRectMakeWithSize(titleSize); 77 | //到这里image和title都是扣除了偏移量后的实际size,frame重置为x/y=0 78 | 79 | 80 | switch (self.contentHorizontalAlignment) {//重置X坐标 81 | case UIControlContentHorizontalAlignmentLeft: 82 | imageFrame = CGRectSetX(imageFrame, self.contentEdgeInsets.left + self.imageEdgeInsets.left); 83 | titleFrame = CGRectSetX(titleFrame, self.contentEdgeInsets.left + self.titleEdgeInsets.left); 84 | break; 85 | case UIControlContentHorizontalAlignmentCenter: 86 | imageFrame = CGRectSetX(imageFrame, self.contentEdgeInsets.left + self.imageEdgeInsets.left + CGFloatGetCenter(imageLimitWidth, imageSize.width)); 87 | titleFrame = CGRectSetX(titleFrame, self.contentEdgeInsets.left + self.titleEdgeInsets.left + CGFloatGetCenter(titleLimitSize.width, titleSize.width)); 88 | break; 89 | case UIControlContentHorizontalAlignmentRight: 90 | imageFrame = CGRectSetX(imageFrame, CGRectGetWidth(self.bounds) - self.contentEdgeInsets.right - self.imageEdgeInsets.right - imageSize.width); 91 | titleFrame = CGRectSetX(titleFrame, CGRectGetWidth(self.bounds) - self.contentEdgeInsets.right - self.titleEdgeInsets.right - titleSize.width); 92 | break; 93 | case UIControlContentHorizontalAlignmentFill://此时要铺满button,所以要重置width 94 | imageFrame = CGRectSetX(imageFrame, self.contentEdgeInsets.left + self.imageEdgeInsets.left); 95 | imageFrame = CGRectSetWidth(imageFrame, imageLimitWidth); 96 | titleFrame = CGRectSetX(titleFrame, self.contentEdgeInsets.left + self.titleEdgeInsets.left); 97 | titleFrame = CGRectSetWidth(titleFrame, titleLimitSize.width); 98 | break; 99 | } 100 | 101 | if (self.buttonImagePosition == FSCustomButtonImagePositionTop) {//重置Y坐标 102 | switch (self.contentVerticalAlignment) { 103 | case UIControlContentVerticalAlignmentTop: 104 | imageFrame = CGRectSetY(imageFrame, self.contentEdgeInsets.top + self.imageEdgeInsets.top); 105 | titleFrame = CGRectSetY(titleFrame, CGRectGetMaxY(imageFrame) + self.imageEdgeInsets.bottom + self.titleEdgeInsets.top); 106 | break; 107 | case UIControlContentVerticalAlignmentCenter: { 108 | CGFloat contentHeight = CGRectGetHeight(imageFrame) + UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets) + CGRectGetHeight(titleFrame) + UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets); 109 | CGFloat minY = CGFloatGetCenter(contentSize.height, contentHeight) + self.contentEdgeInsets.top; 110 | imageFrame = CGRectSetY(imageFrame, minY + self.imageEdgeInsets.top); 111 | titleFrame = CGRectSetY(titleFrame, CGRectGetMaxY(imageFrame) + self.imageEdgeInsets.bottom + self.titleEdgeInsets.top); 112 | } 113 | break; 114 | case UIControlContentVerticalAlignmentBottom: 115 | titleFrame = CGRectSetY(titleFrame, CGRectGetHeight(self.bounds) - self.contentEdgeInsets.bottom - self.titleEdgeInsets.bottom - CGRectGetHeight(titleFrame)); 116 | imageFrame = CGRectSetY(imageFrame, CGRectGetMinY(titleFrame) - self.titleEdgeInsets.top - self.imageEdgeInsets.bottom - CGRectGetHeight(imageFrame)); 117 | break; 118 | case UIControlContentVerticalAlignmentFill: 119 | // 图片按自身大小显示,剩余空间由标题占满 120 | imageFrame = CGRectSetY(imageFrame, self.contentEdgeInsets.top + self.imageEdgeInsets.top); 121 | titleFrame = CGRectSetY(titleFrame, CGRectGetMaxY(imageFrame) + self.imageEdgeInsets.bottom + self.titleEdgeInsets.top); 122 | titleFrame = CGRectSetHeight(titleFrame, CGRectGetHeight(self.bounds) - self.contentEdgeInsets.bottom - self.titleEdgeInsets.bottom - CGRectGetMinY(titleFrame)); 123 | break; 124 | } 125 | } else { 126 | switch (self.contentVerticalAlignment) { 127 | case UIControlContentVerticalAlignmentTop: 128 | titleFrame = CGRectSetY(titleFrame, self.contentEdgeInsets.top + self.titleEdgeInsets.top); 129 | imageFrame = CGRectSetY(imageFrame, CGRectGetMaxY(titleFrame) + self.titleEdgeInsets.bottom + self.imageEdgeInsets.top); 130 | break; 131 | case UIControlContentVerticalAlignmentCenter: { 132 | CGFloat contentHeight = CGRectGetHeight(titleFrame) + UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets) + CGRectGetHeight(imageFrame) + UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets); 133 | CGFloat minY = CGFloatGetCenter(contentSize.height, contentHeight) + self.contentEdgeInsets.top; 134 | titleFrame = CGRectSetY(titleFrame, minY + self.titleEdgeInsets.top); 135 | imageFrame = CGRectSetY(imageFrame, CGRectGetMaxY(titleFrame) + self.titleEdgeInsets.bottom + self.imageEdgeInsets.top); 136 | } 137 | break; 138 | case UIControlContentVerticalAlignmentBottom: 139 | imageFrame = CGRectSetY(imageFrame, CGRectGetHeight(self.bounds) - self.contentEdgeInsets.bottom - self.imageEdgeInsets.bottom - CGRectGetHeight(imageFrame)); 140 | titleFrame = CGRectSetY(titleFrame, CGRectGetMinY(imageFrame) - self.imageEdgeInsets.top - self.titleEdgeInsets.bottom - CGRectGetHeight(titleFrame)); 141 | 142 | break; 143 | case UIControlContentVerticalAlignmentFill: 144 | // 图片按自身大小显示,剩余空间由标题占满 145 | imageFrame = CGRectSetY(imageFrame, CGRectGetHeight(self.bounds) - self.contentEdgeInsets.bottom - self.imageEdgeInsets.bottom - CGRectGetHeight(imageFrame)); 146 | titleFrame = CGRectSetY(titleFrame, self.contentEdgeInsets.top + self.titleEdgeInsets.top); 147 | titleFrame = CGRectSetHeight(titleFrame, CGRectGetMinY(imageFrame) - self.imageEdgeInsets.top - self.titleEdgeInsets.bottom - CGRectGetMinY(titleFrame)); 148 | break; 149 | } 150 | } 151 | 152 | self.imageView.frame = CGRectFlatted(imageFrame); 153 | self.titleLabel.frame = CGRectFlatted(titleFrame); 154 | 155 | } else if (self.buttonImagePosition == FSCustomButtonImagePositionRight) {//水平布局 156 | 157 | CGFloat imageLimitHeight = contentSize.height - UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets); 158 | CGSize imageSize = [self.imageView sizeThatFits:CGSizeMake(CGFLOAT_MAX, imageLimitHeight)];// 假设图片宽度必定完整显示,高度不超过按钮内容 159 | CGRect imageFrame = CGRectMakeWithSize(imageSize); 160 | 161 | CGSize titleLimitSize = CGSizeMake(contentSize.width - UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets) - CGRectGetWidth(imageFrame) - UIEdgeInsetsGetHorizontalValue(self.imageEdgeInsets), contentSize.height - UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets)); 162 | CGSize titleSize = [self.titleLabel sizeThatFits:titleLimitSize]; 163 | titleSize.height = fminf(titleSize.height, titleLimitSize.height); 164 | CGRect titleFrame = CGRectMakeWithSize(titleSize); 165 | 166 | switch (self.contentHorizontalAlignment) { 167 | case UIControlContentHorizontalAlignmentLeft: 168 | titleFrame = CGRectSetX(titleFrame, self.contentEdgeInsets.left + self.titleEdgeInsets.left); 169 | imageFrame = CGRectSetX(imageFrame, CGRectGetMaxX(titleFrame) + self.titleEdgeInsets.right + self.imageEdgeInsets.left); 170 | break; 171 | case UIControlContentHorizontalAlignmentCenter: { 172 | CGFloat contentWidth = CGRectGetWidth(titleFrame) + UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets) + CGRectGetWidth(imageFrame) + UIEdgeInsetsGetHorizontalValue(self.imageEdgeInsets); 173 | CGFloat minX = self.contentEdgeInsets.left + CGFloatGetCenter(contentSize.width, contentWidth); 174 | titleFrame = CGRectSetX(titleFrame, minX + self.titleEdgeInsets.left); 175 | imageFrame = CGRectSetX(imageFrame, CGRectGetMaxX(titleFrame) + self.titleEdgeInsets.right + self.imageEdgeInsets.left); 176 | } 177 | break; 178 | case UIControlContentHorizontalAlignmentRight: 179 | imageFrame = CGRectSetX(imageFrame, CGRectGetWidth(self.bounds) - self.contentEdgeInsets.right - self.imageEdgeInsets.right - CGRectGetWidth(imageFrame)); 180 | titleFrame = CGRectSetX(titleFrame, CGRectGetMinX(imageFrame) - self.imageEdgeInsets.left - self.titleEdgeInsets.right - CGRectGetWidth(titleFrame)); 181 | break; 182 | case UIControlContentHorizontalAlignmentFill: 183 | // 图片按自身大小显示,剩余空间由标题占满 184 | imageFrame = CGRectSetX(imageFrame, CGRectGetWidth(self.bounds) - self.contentEdgeInsets.right - self.imageEdgeInsets.right - CGRectGetWidth(imageFrame)); 185 | titleFrame = CGRectSetX(titleFrame, self.contentEdgeInsets.left + self.titleEdgeInsets.left); 186 | titleFrame = CGRectSetWidth(titleFrame, CGRectGetMinX(imageFrame) - self.imageEdgeInsets.left - self.titleEdgeInsets.right - CGRectGetMinX(titleFrame)); 187 | break; 188 | } 189 | 190 | switch (self.contentVerticalAlignment) { 191 | case UIControlContentVerticalAlignmentTop: 192 | titleFrame = CGRectSetY(titleFrame, self.contentEdgeInsets.top + self.titleEdgeInsets.top); 193 | imageFrame = CGRectSetY(imageFrame, self.contentEdgeInsets.top + self.imageEdgeInsets.top); 194 | break; 195 | case UIControlContentVerticalAlignmentCenter: 196 | titleFrame = CGRectSetY(titleFrame, self.contentEdgeInsets.top + self.titleEdgeInsets.top + CGFloatGetCenter(contentSize.height, CGRectGetHeight(titleFrame) + UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets))); 197 | imageFrame = CGRectSetY(imageFrame, self.contentEdgeInsets.top + self.imageEdgeInsets.top + CGFloatGetCenter(contentSize.height, CGRectGetHeight(imageFrame) + UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets))); 198 | break; 199 | case UIControlContentVerticalAlignmentBottom: 200 | titleFrame = CGRectSetY(titleFrame, CGRectGetHeight(self.bounds) - self.contentEdgeInsets.bottom - self.titleEdgeInsets.bottom - CGRectGetHeight(titleFrame)); 201 | imageFrame = CGRectSetY(imageFrame, CGRectGetHeight(self.bounds) - self.contentEdgeInsets.bottom - self.imageEdgeInsets.bottom - CGRectGetHeight(imageFrame)); 202 | break; 203 | case UIControlContentVerticalAlignmentFill: 204 | titleFrame = CGRectSetY(titleFrame, self.contentEdgeInsets.top + self.titleEdgeInsets.top); 205 | titleFrame = CGRectSetHeight(titleFrame, CGRectGetHeight(self.bounds) - self.contentEdgeInsets.bottom - self.titleEdgeInsets.bottom - CGRectGetMinY(titleFrame)); 206 | imageFrame = CGRectSetY(imageFrame, self.contentEdgeInsets.top + self.imageEdgeInsets.top); 207 | imageFrame = CGRectSetHeight(imageFrame, CGRectGetHeight(self.bounds) - self.contentEdgeInsets.bottom - self.imageEdgeInsets.bottom - CGRectGetMinY(imageFrame)); 208 | break; 209 | } 210 | 211 | self.imageView.frame = CGRectFlatted(imageFrame); 212 | self.titleLabel.frame = CGRectFlatted(titleFrame); 213 | } 214 | } 215 | 216 | - (CGSize)sizeThatFits:(CGSize)size { 217 | // 如果调用 sizeToFit,那么传进来的 size 就是当前按钮的 size,此时的计算不要去限制宽高 218 | if (CGSizeEqualToSize(self.bounds.size, size)) { 219 | size = CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX); 220 | } 221 | 222 | CGSize resultSize = CGSizeZero; 223 | CGSize contentLimitSize = CGSizeMake(size.width - UIEdgeInsetsGetHorizontalValue(self.contentEdgeInsets), size.height - UIEdgeInsetsGetVerticalValue(self.contentEdgeInsets)); 224 | switch (self.buttonImagePosition) { 225 | case FSCustomButtonImagePositionTop: 226 | case FSCustomButtonImagePositionBottom: { 227 | // 图片和文字上下排版时,宽度以文字或图片的最大宽度为最终宽度 228 | CGFloat imageLimitWidth = contentLimitSize.width - UIEdgeInsetsGetHorizontalValue(self.imageEdgeInsets); 229 | CGSize imageSize = [self.imageView sizeThatFits:CGSizeMake(imageLimitWidth, CGFLOAT_MAX)];// 假设图片高度必定完整显示 230 | 231 | CGSize titleLimitSize = CGSizeMake(contentLimitSize.width - UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets), contentLimitSize.height - UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets) - imageSize.height - UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets)); 232 | CGSize titleSize = [self.titleLabel sizeThatFits:titleLimitSize]; 233 | titleSize.height = fminf(titleSize.height, titleLimitSize.height); 234 | 235 | resultSize.width = UIEdgeInsetsGetHorizontalValue(self.contentEdgeInsets); 236 | resultSize.width += fmaxf(UIEdgeInsetsGetHorizontalValue(self.imageEdgeInsets) + imageSize.width, UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets) + titleSize.width); 237 | resultSize.height = UIEdgeInsetsGetVerticalValue(self.contentEdgeInsets) + UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets) + imageSize.height + UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets) + titleSize.height; 238 | } 239 | break; 240 | 241 | case FSCustomButtonImagePositionLeft: 242 | case FSCustomButtonImagePositionRight: { 243 | if (self.buttonImagePosition == FSCustomButtonImagePositionLeft && self.titleLabel.numberOfLines == 1) { 244 | 245 | resultSize = [super sizeThatFits:size]; 246 | 247 | } else { 248 | // 图片和文字水平排版时,高度以文字或图片的最大高度为最终高度 249 | 250 | CGFloat imageLimitHeight = contentLimitSize.height - UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets); 251 | CGSize imageSize = [self.imageView sizeThatFits:CGSizeMake(CGFLOAT_MAX, imageLimitHeight)];// 假设图片宽度必定完整显示,高度不超过按钮内容 252 | 253 | CGSize titleLimitSize = CGSizeMake(contentLimitSize.width - UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets) - imageSize.width - UIEdgeInsetsGetHorizontalValue(self.imageEdgeInsets), contentLimitSize.height - UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets)); 254 | CGSize titleSize = [self.titleLabel sizeThatFits:titleLimitSize]; 255 | titleSize.height = fminf(titleSize.height, titleLimitSize.height); 256 | 257 | resultSize.width = UIEdgeInsetsGetHorizontalValue(self.contentEdgeInsets) + UIEdgeInsetsGetHorizontalValue(self.imageEdgeInsets) + imageSize.width + UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsets) + titleSize.width; 258 | resultSize.height = UIEdgeInsetsGetVerticalValue(self.contentEdgeInsets); 259 | resultSize.height += fmaxf(UIEdgeInsetsGetVerticalValue(self.imageEdgeInsets) + imageSize.height, UIEdgeInsetsGetVerticalValue(self.titleEdgeInsets) + titleSize.height); 260 | } 261 | } 262 | break; 263 | } 264 | return resultSize; 265 | } 266 | 267 | #pragma mark setter 268 | 269 | - (void)setBadgeNumber:(NSString *)badgeNumber 270 | { 271 | _badgeNumber = badgeNumber; 272 | if (badgeNumber.length == 0) { 273 | self.badgeLabel.hidden = YES; 274 | } else { 275 | self.badgeLabel.hidden = NO; 276 | } 277 | self.badgeLabel.text = badgeNumber; 278 | [self.badgeLabel sizeToFit]; 279 | if (self.badgeLabel.text.length == 1) { 280 | CGFloat diameter = fmax(CGRectGetWidth(self.badgeLabel.bounds), CGRectGetHeight(self.badgeLabel.bounds)); 281 | self.badgeLabel.frame = CGRectMake(CGRectGetMinX(self.badgeLabel.frame), CGRectGetMinY(self.badgeLabel.frame), diameter, diameter); 282 | } 283 | self.badgeLabel.layer.cornerRadius = flat(CGRectGetHeight(self.badgeLabel.bounds) / 2.0); 284 | self.badgeLabel.frame = CGRectSetXY(self.badgeLabel.frame, CGRectGetMaxX(self.imageView.frame) - 8, CGRectGetMinY(self.imageView.frame) - 5); 285 | } 286 | 287 | - (void)setButtonImagePosition:(FSCustomButtonImagePosition)buttonImagePosition 288 | { 289 | _buttonImagePosition = buttonImagePosition; 290 | [self setNeedsLayout]; 291 | } 292 | 293 | - (void)setHighlightedBackgroundColor:(UIColor *)highlightedBackgroundColor { 294 | _highlightedBackgroundColor = highlightedBackgroundColor; 295 | if (_highlightedBackgroundColor) { 296 | // 只要开启了highlightedBackgroundColor,就默认不需要alpha的高亮 297 | self.adjustsButtonWhenHighlighted = NO; 298 | } 299 | } 300 | 301 | - (void)setHighlightedBorderColor:(UIColor *)highlightedBorderColor { 302 | _highlightedBorderColor = highlightedBorderColor; 303 | if (_highlightedBorderColor) { 304 | // 只要开启了highlightedBorderColor,就默认不需要alpha的高亮 305 | self.adjustsButtonWhenHighlighted = NO; 306 | } 307 | } 308 | 309 | - (void)setHighlighted:(BOOL)highlighted { 310 | [super setHighlighted:highlighted]; 311 | 312 | if (highlighted && !self.originBorderColor) { 313 | // 手指按在按钮上会不断触发setHighlighted:,所以这里做了保护,设置过一次就不用再设置了 314 | self.originBorderColor = [UIColor colorWithCGColor:self.layer.borderColor]; 315 | } 316 | 317 | // 渲染背景色 318 | if (self.highlightedBackgroundColor || self.highlightedBorderColor) { 319 | [self adjustsButtonHighlighted]; 320 | } 321 | // 如果此时是disabled,则disabled的样式优先 322 | if (!self.enabled) { 323 | return; 324 | } 325 | // 自定义highlighted样式 326 | if (self.adjustsButtonWhenHighlighted) { 327 | if (highlighted) { 328 | self.alpha = 0.5f; 329 | } else { 330 | [UIView animateWithDuration:0.25f animations:^{ 331 | self.alpha = 1; 332 | }]; 333 | } 334 | } 335 | } 336 | 337 | - (void)setEnabled:(BOOL)enabled { 338 | [super setEnabled:enabled]; 339 | if (!enabled && self.adjustsButtonWhenDisabled) { 340 | self.alpha = 0.5f;//disabled的透明度 341 | } else { 342 | [UIView animateWithDuration:0.25f animations:^{ 343 | self.alpha = 1; 344 | }]; 345 | } 346 | } 347 | 348 | - (void)adjustsButtonHighlighted { 349 | if (self.highlightedBackgroundColor) { 350 | if (!self.highlightedBackgroundLayer) { 351 | self.highlightedBackgroundLayer = [CALayer layer];//创建一个新的不带动画的layer 352 | [self.highlightedBackgroundLayer FS_removeDefaultAnimations];//移除系统默认的隐性动画 353 | [self.layer insertSublayer:self.highlightedBackgroundLayer atIndex:0];//替换为自定义的layer 354 | } 355 | //为新的layer添加属性 356 | self.highlightedBackgroundLayer.frame = self.bounds; 357 | self.highlightedBackgroundLayer.cornerRadius = self.layer.cornerRadius; 358 | self.highlightedBackgroundLayer.backgroundColor = self.highlighted ? self.highlightedBackgroundColor.CGColor : [UIColor colorWithRed:1 green:1 blue:1 alpha:0].CGColor; 359 | } 360 | 361 | if (self.highlightedBorderColor) { 362 | self.layer.borderColor = self.highlighted ? self.highlightedBorderColor.CGColor : self.originBorderColor.CGColor; 363 | } 364 | } 365 | 366 | - (void)setAdjustsTitleTintColorAutomatically:(BOOL)adjustsTitleTintColorAutomatically { 367 | _adjustsTitleTintColorAutomatically = adjustsTitleTintColorAutomatically; 368 | [self updateTitleColorIfNeeded]; 369 | } 370 | //title的color变化时调用 371 | - (void)updateTitleColorIfNeeded { 372 | if (self.adjustsTitleTintColorAutomatically && self.currentTitleColor) { 373 | [self setTitleColor:self.tintColor forState:UIControlStateNormal]; 374 | } 375 | if (self.adjustsTitleTintColorAutomatically && self.currentAttributedTitle) {//title为属性字符串的时候 376 | NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.currentAttributedTitle]; 377 | [attributedString addAttribute:NSForegroundColorAttributeName value:self.tintColor range:NSMakeRange(0, attributedString.length)]; 378 | [self setAttributedTitle:attributedString forState:UIControlStateNormal]; 379 | } 380 | } 381 | //设置图片的渲染颜色与tintColor一样 382 | - (void)setAdjustsImageTintColorAutomatically:(BOOL)adjustsImageTintColorAutomatically { 383 | BOOL valueDifference = _adjustsImageTintColorAutomatically != adjustsImageTintColorAutomatically; 384 | _adjustsImageTintColorAutomatically = adjustsImageTintColorAutomatically; 385 | 386 | if (valueDifference) { 387 | [self updateImageRenderingModeIfNeeded]; 388 | } 389 | } 390 | 391 | - (void)updateImageRenderingModeIfNeeded { 392 | if (self.currentImage) { 393 | NSArray *states = @[@(UIControlStateNormal), @(UIControlStateHighlighted), @(UIControlStateDisabled)]; 394 | for (NSNumber *number in states) { 395 | UIImage *image = [self imageForState:[number unsignedIntegerValue]]; 396 | if (!image) { 397 | continue; 398 | } 399 | 400 | if (self.adjustsImageTintColorAutomatically) { 401 | // 这里的image不用做renderingMode的处理,而是放到重写的setImage:forState里去做 402 | [self setImage:image forState:[number unsignedIntegerValue]]; 403 | } else { 404 | // 如果不需要用template的模式渲染,并且之前是使用template的,则把renderingMode改回Original 405 | [self setImage:[image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:[number unsignedIntegerValue]]; 406 | } 407 | } 408 | } 409 | } 410 | 411 | - (void)setImage:(UIImage *)image forState:(UIControlState)state { 412 | if (self.adjustsImageTintColorAutomatically) { 413 | image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 414 | } 415 | [super setImage:image forState:state]; 416 | } 417 | 418 | - (void)tintColorDidChange { 419 | [super tintColorDidChange]; 420 | 421 | [self updateTitleColorIfNeeded]; 422 | 423 | if (self.adjustsImageTintColorAutomatically) { 424 | [self updateImageRenderingModeIfNeeded]; 425 | } 426 | } 427 | 428 | #pragma mark LazyLoad 429 | 430 | - (FSBadgeLab *)badgeLabel 431 | { 432 | if (!_badgeLabel) { 433 | _badgeLabel = [[FSBadgeLab alloc]init]; 434 | [self addSubview:self.badgeLabel]; 435 | _badgeLabel.backgroundColor = [UIColor colorWithRed:240.0f/255.0f green:71.0f/255.0f blue:71.0f/255.0f alpha:1.0f]; 436 | _badgeLabel.textColor = [UIColor whiteColor]; 437 | _badgeLabel.textAlignment = NSTextAlignmentCenter; 438 | _badgeLabel.contentEdgeInsets = UIEdgeInsetsMake(2, 5, 2, 5); 439 | _badgeLabel.clipsToBounds = YES; 440 | _badgeLabel.font = [UIFont systemFontOfSize:11]; 441 | } 442 | return _badgeLabel; 443 | } 444 | 445 | @end 446 | 447 | @implementation FSBadgeLab 448 | 449 | - (void)setContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets { 450 | _contentEdgeInsets = contentEdgeInsets; 451 | [self setNeedsDisplay]; 452 | } 453 | 454 | - (CGSize)sizeThatFits:(CGSize)size { 455 | size = [super sizeThatFits:CGSizeMake(size.width - UIEdgeInsetsGetHorizontalValue(self.contentEdgeInsets), size.height - UIEdgeInsetsGetVerticalValue(self.contentEdgeInsets))]; 456 | size.width += UIEdgeInsetsGetHorizontalValue(self.contentEdgeInsets); 457 | size.height += UIEdgeInsetsGetVerticalValue(self.contentEdgeInsets); 458 | return size; 459 | } 460 | 461 | @end 462 | 463 | -------------------------------------------------------------------------------- /FSCustomButtonDemo/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 | -------------------------------------------------------------------------------- /FSCustomButtonDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // FSCustomButtonDemo 4 | // 5 | // Created by huim on 2017/6/13. 6 | // Copyright © 2017年 fengshun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /FSCustomButtonDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // FSCustomButtonDemo 4 | // 5 | // Created by huim on 2017/6/13. 6 | // Copyright © 2017年 fengshun. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "FSCustomButton.h" 11 | #define UIColorMake(r, g, b) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1] 12 | #define UIColorMakeWithRGBA(r, g, b, a) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a/1.0] 13 | 14 | @interface ViewController () 15 | 16 | @end 17 | 18 | @implementation ViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view, typically from a nib. 23 | FSCustomButton *button = [[FSCustomButton alloc] initWithFrame:CGRectMake(100, 20, 100, 80)]; 24 | button.adjustsTitleTintColorAutomatically = YES; 25 | [button setTintColor:UIColorMake(27, 31, 35)]; 26 | button.titleLabel.font = [UIFont boldSystemFontOfSize:14]; 27 | [button setTitle:@"图片在上" forState:UIControlStateNormal]; 28 | button.backgroundColor = UIColorMake(222, 234, 214); 29 | [button setImage:[UIImage imageNamed:@"my_ic_coupon"] forState:UIControlStateNormal]; 30 | button.layer.cornerRadius = 4; 31 | button.buttonImagePosition = FSCustomButtonImagePositionTop; 32 | button.titleEdgeInsets = UIEdgeInsetsMake(10, 0, 0, 0); 33 | [self.view addSubview:button]; 34 | button.badgeNumber = @"18"; 35 | // [button sizeToFit]; 36 | 37 | FSCustomButton *button2 = [[FSCustomButton alloc] initWithFrame:CGRectMake(100, 120, 100, 80)]; 38 | button2.adjustsTitleTintColorAutomatically = YES; 39 | [button2 setTintColor:UIColorMake(27, 31, 35)]; 40 | button2.titleLabel.font = [UIFont boldSystemFontOfSize:14]; 41 | [button2 setTitle:@"图片在下" forState:UIControlStateNormal]; 42 | button2.backgroundColor = UIColorMake(222, 234, 214); 43 | [button2 setImage:[UIImage imageNamed:@"my_ic_coupon"] forState:UIControlStateNormal]; 44 | button2.layer.cornerRadius = 4; 45 | button2.buttonImagePosition = FSCustomButtonImagePositionBottom; 46 | button2.imageEdgeInsets = UIEdgeInsetsMake(10, 0, 0, 0); 47 | [self.view addSubview:button2]; 48 | button2.badgeNumber = @"99+"; 49 | 50 | FSCustomButton *button3 = [[FSCustomButton alloc] initWithFrame:CGRectMake(220, 20, 100, 80)]; 51 | button3.adjustsTitleTintColorAutomatically = YES; 52 | [button3 setTintColor:UIColorMake(27, 31, 35)]; 53 | button3.titleLabel.font = [UIFont boldSystemFontOfSize:14]; 54 | [button3 setTitle:@"sizeToFit" forState:UIControlStateNormal]; 55 | button3.backgroundColor = UIColorMake(222, 234, 214); 56 | [button3 setImage:[UIImage imageNamed:@"my_ic_coupon"] forState:UIControlStateNormal]; 57 | button3.layer.cornerRadius = 4; 58 | button3.buttonImagePosition = FSCustomButtonImagePositionBottom; 59 | button3.imageEdgeInsets = UIEdgeInsetsMake(10, 0, 0, 0); 60 | [self.view addSubview:button3]; 61 | [button3 sizeToFit]; 62 | button3.badgeNumber = @"9"; 63 | 64 | FSCustomButton *button4 = [[FSCustomButton alloc] initWithFrame:CGRectMake(220, 120, 100, 80)]; 65 | button4.adjustsTitleTintColorAutomatically = YES; 66 | [button4 setTintColor:UIColorMake(27, 31, 35)]; 67 | button4.titleLabel.font = [UIFont boldSystemFontOfSize:14]; 68 | [button4 setTitle:@"sizeToFit" forState:UIControlStateNormal]; 69 | button4.backgroundColor = UIColorMake(222, 234, 214); 70 | [button4 setImage:[UIImage imageNamed:@"my_ic_coupon"] forState:UIControlStateNormal]; 71 | button4.layer.cornerRadius = 4; 72 | button4.buttonImagePosition = FSCustomButtonImagePositionBottom; 73 | button4.imageEdgeInsets = UIEdgeInsetsMake(10, 0, 0, 0); 74 | [self.view addSubview:button4]; 75 | [button4 sizeToFit]; 76 | 77 | FSCustomButton *button5 = [[FSCustomButton alloc] initWithFrame:CGRectMake(100, 220, 200, 40)]; 78 | button5.titleLabel.font = [UIFont boldSystemFontOfSize:14]; 79 | [button5 setTitle:@"高亮背景色" forState:UIControlStateNormal]; 80 | [button5 setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 81 | button5.backgroundColor = UIColorMake(222, 234, 214); 82 | button5.highlightedBackgroundColor = UIColorMake(0, 168, 225);// 高亮时的背景色 83 | [button5 setImage:[UIImage imageNamed:@"my_ic_coupon"] forState:UIControlStateNormal]; 84 | button5.layer.cornerRadius = 4; 85 | button5.titleEdgeInsets = UIEdgeInsetsMake(0, 5, 0, 0); 86 | [self.view addSubview:button5]; 87 | 88 | FSCustomButton *button6 = [[FSCustomButton alloc] initWithFrame:CGRectMake(100, 280, 200, 40)]; 89 | button6.titleLabel.font = [UIFont boldSystemFontOfSize:14]; 90 | [button6 setTitle:@"高亮边框色" forState:UIControlStateNormal]; 91 | [button6 setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 92 | button6.backgroundColor = UIColorMake(222, 234, 214); 93 | button6.highlightedBackgroundColor = UIColorMake(0, 168, 225);// 高亮时的背景色 94 | [button6 setImage:[UIImage imageNamed:@"my_ic_coupon"] forState:UIControlStateNormal]; 95 | button6.layer.cornerRadius = 4; 96 | button6.layer.borderWidth = 2; 97 | button6.highlightedBorderColor = [UIColor redColor]; 98 | button6.titleEdgeInsets = UIEdgeInsetsMake(0, 5, 0, 0); 99 | [self.view addSubview:button6]; 100 | } 101 | 102 | 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /FSCustomButtonDemo/btn_voice_play@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shunFSKi/FSCustomButtonDemo/a24e410af8a52635210800f29375794c51ccda36/FSCustomButtonDemo/btn_voice_play@2x.png -------------------------------------------------------------------------------- /FSCustomButtonDemo/graphicreview_group_star_red@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shunFSKi/FSCustomButtonDemo/a24e410af8a52635210800f29375794c51ccda36/FSCustomButtonDemo/graphicreview_group_star_red@2x.png -------------------------------------------------------------------------------- /FSCustomButtonDemo/home_ic_pick@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shunFSKi/FSCustomButtonDemo/a24e410af8a52635210800f29375794c51ccda36/FSCustomButtonDemo/home_ic_pick@2x.png -------------------------------------------------------------------------------- /FSCustomButtonDemo/jkj_sort_ic_baby_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shunFSKi/FSCustomButtonDemo/a24e410af8a52635210800f29375794c51ccda36/FSCustomButtonDemo/jkj_sort_ic_baby_s.png -------------------------------------------------------------------------------- /FSCustomButtonDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // FSCustomButtonDemo 4 | // 5 | // Created by huim on 2017/6/13. 6 | // Copyright © 2017年 fengshun. 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 | -------------------------------------------------------------------------------- /FSCustomButtonDemo/my_ic_coupon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shunFSKi/FSCustomButtonDemo/a24e410af8a52635210800f29375794c51ccda36/FSCustomButtonDemo/my_ic_coupon@2x.png -------------------------------------------------------------------------------- /FSCustomButtonDemo/my_ic_coupon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shunFSKi/FSCustomButtonDemo/a24e410af8a52635210800f29375794c51ccda36/FSCustomButtonDemo/my_ic_coupon@3x.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 shunFSKi 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 | # FSCustomButtonDemo 2 | 自定义按钮布局样式 3 | 4 | **详细分析文档请点这里**:[iOS一步步实现一个高度自定义UIButton控件](http://www.jianshu.com/p/4603e9bbba56) 5 | ## 效果图 6 | ![FSCustomButtonDemo.gif](http://upload-images.jianshu.io/upload_images/1792044-beb61c6dc89a4485.gif?imageMogr2/auto-orient/strip) 7 | ![FSCustomButton](https://github.com/shunFSKi/ImageResources/blob/master/FSCustomButton.png) 8 | ## 使用方式 9 | 将demo中的FSCustomButton文件夹拖入项目,导入**FSCustomButton.h** 10 | 11 | ## API使用 12 | 13 | ```objc 14 | typedef enum : NSUInteger { 15 | FSCustomButtonImagePositionLeft,//图片在左 16 | FSCustomButtonImagePositionRight,//图片在右 17 | FSCustomButtonImagePositionTop,//图片在上 18 | FSCustomButtonImagePositionBottom,//图片在下 19 | } FSCustomButtonImagePosition; 20 | 21 | @interface FSCustomButton : UIButton 22 | 23 | /** 24 | 图片位置 25 | */ 26 | @property (nonatomic, assign) FSCustomButtonImagePosition buttonImagePosition; 27 | 28 | /** 29 | 文字颜色自动跟随tintColor调整,default NO 30 | */ 31 | @property(nonatomic, assign) BOOL adjustsTitleTintColorAutomatically; 32 | 33 | /** 34 | 图片颜色自动跟随tintColor调整,default NO 35 | */ 36 | @property(nonatomic, assign) BOOL adjustsImageTintColorAutomatically; 37 | 38 | /** 39 | default YES;相当于系统的adjustsImageWhenHighlighted 40 | */ 41 | @property(nonatomic, assign) BOOL adjustsButtonWhenHighlighted; 42 | 43 | /** 44 | default YES,相当于系统的adjustsImageWhenDisabled 45 | */ 46 | @property(nonatomic, assign) BOOL adjustsButtonWhenDisabled; 47 | 48 | /** 49 | 高亮状态button背景色,default nil,设置此属性后默认adjustsButtonWhenHighlighted=NO 50 | */ 51 | @property(nonatomic, strong) UIColor *highlightedBackgroundColor; 52 | 53 | /** 54 | 高亮状态边框颜色,default nil,设置此属性后默认adjustsButtonWhenHighlighted=NO 55 | */ 56 | @property(nonatomic, strong) UIColor *highlightedBorderColor; 57 | ``` 58 | 59 | ## 使用示例 60 | 详细使用建议查看demo 61 | 62 | ```objc 63 | FSCustomButton *button = [[FSCustomButton alloc] initWithFrame:CGRectMake(100, 20, 100, 80)]; 64 | button.adjustsTitleTintColorAutomatically = YES; 65 | [button setTintColor:UIColorMake(27, 31, 35)]; 66 | button.titleLabel.font = [UIFont boldSystemFontOfSize:14]; 67 | [button setTitle:@"图片在上" forState:UIControlStateNormal]; 68 | button.backgroundColor = UIColorMake(222, 234, 214); 69 | [button setImage:[UIImage imageNamed:@"my_ic_coupon"] forState:UIControlStateNormal]; 70 | button.layer.cornerRadius = 4; 71 | button.buttonImagePosition = FSCustomButtonImagePositionTop; 72 | button.titleEdgeInsets = UIEdgeInsetsMake(10, 0, 0, 0); 73 | [self.view addSubview:button]; 74 | // [button sizeToFit]; 75 | 76 | FSCustomButton *button2 = [[FSCustomButton alloc] initWithFrame:CGRectMake(100, 120, 100, 80)]; 77 | button2.adjustsTitleTintColorAutomatically = YES; 78 | [button2 setTintColor:UIColorMake(27, 31, 35)]; 79 | button2.titleLabel.font = [UIFont boldSystemFontOfSize:14]; 80 | [button2 setTitle:@"图片在下" forState:UIControlStateNormal]; 81 | button2.backgroundColor = UIColorMake(222, 234, 214); 82 | [button2 setImage:[UIImage imageNamed:@"my_ic_coupon"] forState:UIControlStateNormal]; 83 | button2.layer.cornerRadius = 4; 84 | button2.buttonImagePosition = FSCustomButtonImagePositionBottom; 85 | button2.imageEdgeInsets = UIEdgeInsetsMake(10, 0, 0, 0); 86 | [self.view addSubview:button2]; 87 | ``` 88 | 89 | 90 | 91 | --------------------------------------------------------------------------------