├── .DS_Store ├── .gitignore ├── LCChartView.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── LCChartView ├── .DS_Store ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── Info.plist ├── LCChartView │ ├── LCArcView.h │ ├── LCArcView.m │ ├── LCChartView.h │ ├── LCChartView.m │ ├── LCChartViewModel.h │ ├── LCChartViewModel.m │ ├── LCMethod.h │ ├── LCMethod.m │ ├── LCPieView.h │ ├── LCPieView.m │ ├── LCPieViewModel.h │ └── LCPieViewModel.m ├── LCChartViewController.h ├── LCChartViewController.m ├── UIView+LCLayout.h ├── UIView+LCLayout.m └── main.m ├── README.md ├── arc.gif ├── bar.gif ├── line.gif └── pie.gif /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rochang/LCChartView/2ab692bbc0a59441755fd9f4db947243e61e9318/.DS_Store -------------------------------------------------------------------------------- /.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 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | 55 | #Code Injection 56 | # 57 | # After new code Injection tools there's a generated folder /iOSInjectionProject 58 | # https://github.com/johnno1962/injectionforxcode 59 | 60 | iOSInjectionProject/ 61 | -------------------------------------------------------------------------------- /LCChartView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A32F728F1E28DE930078CAC5 /* LCChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A32F728E1E28DE930078CAC5 /* LCChartViewController.m */; }; 11 | A331535F1E28D9F300A9281C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A331535E1E28D9F300A9281C /* main.m */; }; 12 | A33153621E28D9F300A9281C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A33153611E28D9F300A9281C /* AppDelegate.m */; }; 13 | A331536A1E28D9F300A9281C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A33153691E28D9F300A9281C /* Assets.xcassets */; }; 14 | A331536D1E28D9F300A9281C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A331536B1E28D9F300A9281C /* LaunchScreen.storyboard */; }; 15 | A3D114BB1F32C40B0062EDEE /* UIView+LCLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = A3D114BA1F32C40B0062EDEE /* UIView+LCLayout.m */; }; 16 | A3D114C71F32C43D0062EDEE /* LCArcView.m in Sources */ = {isa = PBXBuildFile; fileRef = A3D114BE1F32C43D0062EDEE /* LCArcView.m */; }; 17 | A3D114C81F32C43D0062EDEE /* LCChartView.m in Sources */ = {isa = PBXBuildFile; fileRef = A3D114C01F32C43D0062EDEE /* LCChartView.m */; }; 18 | A3D114C91F32C43D0062EDEE /* LCChartViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A3D114C21F32C43D0062EDEE /* LCChartViewModel.m */; }; 19 | A3D114CA1F32C43D0062EDEE /* LCPieView.m in Sources */ = {isa = PBXBuildFile; fileRef = A3D114C41F32C43D0062EDEE /* LCPieView.m */; }; 20 | A3D114CB1F32C43D0062EDEE /* LCPieViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = A3D114C61F32C43D0062EDEE /* LCPieViewModel.m */; }; 21 | A3D114CE1F32C52B0062EDEE /* LCMethod.m in Sources */ = {isa = PBXBuildFile; fileRef = A3D114CD1F32C52B0062EDEE /* LCMethod.m */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXFileReference section */ 25 | A32F728D1E28DE930078CAC5 /* LCChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCChartViewController.h; sourceTree = ""; }; 26 | A32F728E1E28DE930078CAC5 /* LCChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCChartViewController.m; sourceTree = ""; }; 27 | A331535A1E28D9F300A9281C /* LCChartView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LCChartView.app; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | A331535E1E28D9F300A9281C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 29 | A33153601E28D9F300A9281C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 30 | A33153611E28D9F300A9281C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 31 | A33153691E28D9F300A9281C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 32 | A331536C1E28D9F300A9281C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 33 | A331536E1E28D9F300A9281C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | A3D114B91F32C40B0062EDEE /* UIView+LCLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+LCLayout.h"; sourceTree = ""; }; 35 | A3D114BA1F32C40B0062EDEE /* UIView+LCLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+LCLayout.m"; sourceTree = ""; }; 36 | A3D114BD1F32C43D0062EDEE /* LCArcView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCArcView.h; sourceTree = ""; }; 37 | A3D114BE1F32C43D0062EDEE /* LCArcView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCArcView.m; sourceTree = ""; }; 38 | A3D114BF1F32C43D0062EDEE /* LCChartView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCChartView.h; sourceTree = ""; }; 39 | A3D114C01F32C43D0062EDEE /* LCChartView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCChartView.m; sourceTree = ""; }; 40 | A3D114C11F32C43D0062EDEE /* LCChartViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCChartViewModel.h; sourceTree = ""; }; 41 | A3D114C21F32C43D0062EDEE /* LCChartViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCChartViewModel.m; sourceTree = ""; }; 42 | A3D114C31F32C43D0062EDEE /* LCPieView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCPieView.h; sourceTree = ""; }; 43 | A3D114C41F32C43D0062EDEE /* LCPieView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCPieView.m; sourceTree = ""; }; 44 | A3D114C51F32C43D0062EDEE /* LCPieViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCPieViewModel.h; sourceTree = ""; }; 45 | A3D114C61F32C43D0062EDEE /* LCPieViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCPieViewModel.m; sourceTree = ""; }; 46 | A3D114CC1F32C52B0062EDEE /* LCMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCMethod.h; sourceTree = ""; }; 47 | A3D114CD1F32C52B0062EDEE /* LCMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCMethod.m; sourceTree = ""; }; 48 | /* End PBXFileReference section */ 49 | 50 | /* Begin PBXFrameworksBuildPhase section */ 51 | A33153571E28D9F300A9281C /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXFrameworksBuildPhase section */ 59 | 60 | /* Begin PBXGroup section */ 61 | A33153511E28D9F300A9281C = { 62 | isa = PBXGroup; 63 | children = ( 64 | A331535C1E28D9F300A9281C /* LCChartView */, 65 | A331535B1E28D9F300A9281C /* Products */, 66 | ); 67 | sourceTree = ""; 68 | }; 69 | A331535B1E28D9F300A9281C /* Products */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | A331535A1E28D9F300A9281C /* LCChartView.app */, 73 | ); 74 | name = Products; 75 | sourceTree = ""; 76 | }; 77 | A331535C1E28D9F300A9281C /* LCChartView */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | A3D114B91F32C40B0062EDEE /* UIView+LCLayout.h */, 81 | A3D114BA1F32C40B0062EDEE /* UIView+LCLayout.m */, 82 | A3D114BC1F32C43D0062EDEE /* LCChartView */, 83 | A33153601E28D9F300A9281C /* AppDelegate.h */, 84 | A33153611E28D9F300A9281C /* AppDelegate.m */, 85 | A32F728D1E28DE930078CAC5 /* LCChartViewController.h */, 86 | A32F728E1E28DE930078CAC5 /* LCChartViewController.m */, 87 | A33153691E28D9F300A9281C /* Assets.xcassets */, 88 | A331536B1E28D9F300A9281C /* LaunchScreen.storyboard */, 89 | A331536E1E28D9F300A9281C /* Info.plist */, 90 | A331535D1E28D9F300A9281C /* Supporting Files */, 91 | ); 92 | path = LCChartView; 93 | sourceTree = ""; 94 | }; 95 | A331535D1E28D9F300A9281C /* Supporting Files */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | A331535E1E28D9F300A9281C /* main.m */, 99 | ); 100 | name = "Supporting Files"; 101 | sourceTree = ""; 102 | }; 103 | A3D114BC1F32C43D0062EDEE /* LCChartView */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | A3D114BD1F32C43D0062EDEE /* LCArcView.h */, 107 | A3D114BE1F32C43D0062EDEE /* LCArcView.m */, 108 | A3D114BF1F32C43D0062EDEE /* LCChartView.h */, 109 | A3D114C01F32C43D0062EDEE /* LCChartView.m */, 110 | A3D114C11F32C43D0062EDEE /* LCChartViewModel.h */, 111 | A3D114C21F32C43D0062EDEE /* LCChartViewModel.m */, 112 | A3D114C31F32C43D0062EDEE /* LCPieView.h */, 113 | A3D114C41F32C43D0062EDEE /* LCPieView.m */, 114 | A3D114C51F32C43D0062EDEE /* LCPieViewModel.h */, 115 | A3D114C61F32C43D0062EDEE /* LCPieViewModel.m */, 116 | A3D114CC1F32C52B0062EDEE /* LCMethod.h */, 117 | A3D114CD1F32C52B0062EDEE /* LCMethod.m */, 118 | ); 119 | path = LCChartView; 120 | sourceTree = ""; 121 | }; 122 | /* End PBXGroup section */ 123 | 124 | /* Begin PBXNativeTarget section */ 125 | A33153591E28D9F300A9281C /* LCChartView */ = { 126 | isa = PBXNativeTarget; 127 | buildConfigurationList = A33153711E28D9F300A9281C /* Build configuration list for PBXNativeTarget "LCChartView" */; 128 | buildPhases = ( 129 | A33153561E28D9F300A9281C /* Sources */, 130 | A33153571E28D9F300A9281C /* Frameworks */, 131 | A33153581E28D9F300A9281C /* Resources */, 132 | ); 133 | buildRules = ( 134 | ); 135 | dependencies = ( 136 | ); 137 | name = LCChartView; 138 | productName = LCChartView; 139 | productReference = A331535A1E28D9F300A9281C /* LCChartView.app */; 140 | productType = "com.apple.product-type.application"; 141 | }; 142 | /* End PBXNativeTarget section */ 143 | 144 | /* Begin PBXProject section */ 145 | A33153521E28D9F300A9281C /* Project object */ = { 146 | isa = PBXProject; 147 | attributes = { 148 | LastUpgradeCheck = 0820; 149 | ORGANIZATIONNAME = Rochang; 150 | TargetAttributes = { 151 | A33153591E28D9F300A9281C = { 152 | CreatedOnToolsVersion = 8.2; 153 | DevelopmentTeam = S3M9K8BUYE; 154 | ProvisioningStyle = Automatic; 155 | }; 156 | }; 157 | }; 158 | buildConfigurationList = A33153551E28D9F300A9281C /* Build configuration list for PBXProject "LCChartView" */; 159 | compatibilityVersion = "Xcode 3.2"; 160 | developmentRegion = English; 161 | hasScannedForEncodings = 0; 162 | knownRegions = ( 163 | en, 164 | Base, 165 | ); 166 | mainGroup = A33153511E28D9F300A9281C; 167 | productRefGroup = A331535B1E28D9F300A9281C /* Products */; 168 | projectDirPath = ""; 169 | projectRoot = ""; 170 | targets = ( 171 | A33153591E28D9F300A9281C /* LCChartView */, 172 | ); 173 | }; 174 | /* End PBXProject section */ 175 | 176 | /* Begin PBXResourcesBuildPhase section */ 177 | A33153581E28D9F300A9281C /* Resources */ = { 178 | isa = PBXResourcesBuildPhase; 179 | buildActionMask = 2147483647; 180 | files = ( 181 | A331536D1E28D9F300A9281C /* LaunchScreen.storyboard in Resources */, 182 | A331536A1E28D9F300A9281C /* Assets.xcassets in Resources */, 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | }; 186 | /* End PBXResourcesBuildPhase section */ 187 | 188 | /* Begin PBXSourcesBuildPhase section */ 189 | A33153561E28D9F300A9281C /* Sources */ = { 190 | isa = PBXSourcesBuildPhase; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | A3D114C91F32C43D0062EDEE /* LCChartViewModel.m in Sources */, 194 | A3D114C81F32C43D0062EDEE /* LCChartView.m in Sources */, 195 | A3D114BB1F32C40B0062EDEE /* UIView+LCLayout.m in Sources */, 196 | A3D114CE1F32C52B0062EDEE /* LCMethod.m in Sources */, 197 | A33153621E28D9F300A9281C /* AppDelegate.m in Sources */, 198 | A3D114C71F32C43D0062EDEE /* LCArcView.m in Sources */, 199 | A32F728F1E28DE930078CAC5 /* LCChartViewController.m in Sources */, 200 | A3D114CA1F32C43D0062EDEE /* LCPieView.m in Sources */, 201 | A331535F1E28D9F300A9281C /* main.m in Sources */, 202 | A3D114CB1F32C43D0062EDEE /* LCPieViewModel.m in Sources */, 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | }; 206 | /* End PBXSourcesBuildPhase section */ 207 | 208 | /* Begin PBXVariantGroup section */ 209 | A331536B1E28D9F300A9281C /* LaunchScreen.storyboard */ = { 210 | isa = PBXVariantGroup; 211 | children = ( 212 | A331536C1E28D9F300A9281C /* Base */, 213 | ); 214 | name = LaunchScreen.storyboard; 215 | sourceTree = ""; 216 | }; 217 | /* End PBXVariantGroup section */ 218 | 219 | /* Begin XCBuildConfiguration section */ 220 | A331536F1E28D9F300A9281C /* Debug */ = { 221 | isa = XCBuildConfiguration; 222 | buildSettings = { 223 | ALWAYS_SEARCH_USER_PATHS = NO; 224 | CLANG_ANALYZER_NONNULL = YES; 225 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 226 | CLANG_CXX_LIBRARY = "libc++"; 227 | CLANG_ENABLE_MODULES = YES; 228 | CLANG_ENABLE_OBJC_ARC = YES; 229 | CLANG_WARN_BOOL_CONVERSION = YES; 230 | CLANG_WARN_CONSTANT_CONVERSION = YES; 231 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 232 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 233 | CLANG_WARN_EMPTY_BODY = YES; 234 | CLANG_WARN_ENUM_CONVERSION = YES; 235 | CLANG_WARN_INFINITE_RECURSION = YES; 236 | CLANG_WARN_INT_CONVERSION = YES; 237 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 238 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 239 | CLANG_WARN_UNREACHABLE_CODE = YES; 240 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 241 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 242 | COPY_PHASE_STRIP = NO; 243 | DEBUG_INFORMATION_FORMAT = dwarf; 244 | ENABLE_STRICT_OBJC_MSGSEND = YES; 245 | ENABLE_TESTABILITY = YES; 246 | GCC_C_LANGUAGE_STANDARD = gnu99; 247 | GCC_DYNAMIC_NO_PIC = NO; 248 | GCC_NO_COMMON_BLOCKS = YES; 249 | GCC_OPTIMIZATION_LEVEL = 0; 250 | GCC_PREPROCESSOR_DEFINITIONS = ( 251 | "DEBUG=1", 252 | "$(inherited)", 253 | ); 254 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 255 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 256 | GCC_WARN_UNDECLARED_SELECTOR = YES; 257 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 258 | GCC_WARN_UNUSED_FUNCTION = YES; 259 | GCC_WARN_UNUSED_VARIABLE = YES; 260 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 261 | MTL_ENABLE_DEBUG_INFO = YES; 262 | ONLY_ACTIVE_ARCH = YES; 263 | SDKROOT = iphoneos; 264 | TARGETED_DEVICE_FAMILY = "1,2"; 265 | }; 266 | name = Debug; 267 | }; 268 | A33153701E28D9F300A9281C /* Release */ = { 269 | isa = XCBuildConfiguration; 270 | buildSettings = { 271 | ALWAYS_SEARCH_USER_PATHS = NO; 272 | CLANG_ANALYZER_NONNULL = YES; 273 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 274 | CLANG_CXX_LIBRARY = "libc++"; 275 | CLANG_ENABLE_MODULES = YES; 276 | CLANG_ENABLE_OBJC_ARC = YES; 277 | CLANG_WARN_BOOL_CONVERSION = YES; 278 | CLANG_WARN_CONSTANT_CONVERSION = YES; 279 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 280 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 281 | CLANG_WARN_EMPTY_BODY = YES; 282 | CLANG_WARN_ENUM_CONVERSION = YES; 283 | CLANG_WARN_INFINITE_RECURSION = YES; 284 | CLANG_WARN_INT_CONVERSION = YES; 285 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 286 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 287 | CLANG_WARN_UNREACHABLE_CODE = YES; 288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 289 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 290 | COPY_PHASE_STRIP = NO; 291 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 292 | ENABLE_NS_ASSERTIONS = NO; 293 | ENABLE_STRICT_OBJC_MSGSEND = YES; 294 | GCC_C_LANGUAGE_STANDARD = gnu99; 295 | GCC_NO_COMMON_BLOCKS = YES; 296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 298 | GCC_WARN_UNDECLARED_SELECTOR = YES; 299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 300 | GCC_WARN_UNUSED_FUNCTION = YES; 301 | GCC_WARN_UNUSED_VARIABLE = YES; 302 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 303 | MTL_ENABLE_DEBUG_INFO = NO; 304 | SDKROOT = iphoneos; 305 | TARGETED_DEVICE_FAMILY = "1,2"; 306 | VALIDATE_PRODUCT = YES; 307 | }; 308 | name = Release; 309 | }; 310 | A33153721E28D9F300A9281C /* Debug */ = { 311 | isa = XCBuildConfiguration; 312 | buildSettings = { 313 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 314 | DEVELOPMENT_TEAM = S3M9K8BUYE; 315 | INFOPLIST_FILE = LCChartView/Info.plist; 316 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 317 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 318 | PRODUCT_BUNDLE_IDENTIFIER = com.Rochang.LCChartView; 319 | PRODUCT_NAME = "$(TARGET_NAME)"; 320 | }; 321 | name = Debug; 322 | }; 323 | A33153731E28D9F300A9281C /* Release */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 327 | DEVELOPMENT_TEAM = S3M9K8BUYE; 328 | INFOPLIST_FILE = LCChartView/Info.plist; 329 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 330 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 331 | PRODUCT_BUNDLE_IDENTIFIER = com.Rochang.LCChartView; 332 | PRODUCT_NAME = "$(TARGET_NAME)"; 333 | }; 334 | name = Release; 335 | }; 336 | /* End XCBuildConfiguration section */ 337 | 338 | /* Begin XCConfigurationList section */ 339 | A33153551E28D9F300A9281C /* Build configuration list for PBXProject "LCChartView" */ = { 340 | isa = XCConfigurationList; 341 | buildConfigurations = ( 342 | A331536F1E28D9F300A9281C /* Debug */, 343 | A33153701E28D9F300A9281C /* Release */, 344 | ); 345 | defaultConfigurationIsVisible = 0; 346 | defaultConfigurationName = Release; 347 | }; 348 | A33153711E28D9F300A9281C /* Build configuration list for PBXNativeTarget "LCChartView" */ = { 349 | isa = XCConfigurationList; 350 | buildConfigurations = ( 351 | A33153721E28D9F300A9281C /* Debug */, 352 | A33153731E28D9F300A9281C /* Release */, 353 | ); 354 | defaultConfigurationIsVisible = 0; 355 | defaultConfigurationName = Release; 356 | }; 357 | /* End XCConfigurationList section */ 358 | }; 359 | rootObject = A33153521E28D9F300A9281C /* Project object */; 360 | } 361 | -------------------------------------------------------------------------------- /LCChartView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LCChartView/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rochang/LCChartView/2ab692bbc0a59441755fd9f4db947243e61e9318/LCChartView/.DS_Store -------------------------------------------------------------------------------- /LCChartView/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LCChartView 4 | // 5 | // Created by liangrongchang on 2017/1/13. 6 | // Copyright © 2017年 Rochang. 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 | -------------------------------------------------------------------------------- /LCChartView/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LCChartView 4 | // 5 | // Created by liangrongchang on 2017/1/13. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "LCChartViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 21 | LCChartViewController *lineVc = [[LCChartViewController alloc] init]; 22 | UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:lineVc]; 23 | self.window.rootViewController = nav; 24 | [self.window makeKeyAndVisible]; 25 | return YES; 26 | } 27 | 28 | 29 | - (void)applicationWillResignActive:(UIApplication *)application { 30 | // 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. 31 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 32 | } 33 | 34 | 35 | - (void)applicationDidEnterBackground:(UIApplication *)application { 36 | // 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. 37 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 38 | } 39 | 40 | 41 | - (void)applicationWillEnterForeground:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationDidBecomeActive:(UIApplication *)application { 47 | // 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. 48 | } 49 | 50 | 51 | - (void)applicationWillTerminate:(UIApplication *)application { 52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 53 | } 54 | 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /LCChartView/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /LCChartView/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 | -------------------------------------------------------------------------------- /LCChartView/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 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | 33 | UISupportedInterfaceOrientations~ipad 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationPortraitUpsideDown 37 | UIInterfaceOrientationLandscapeLeft 38 | UIInterfaceOrientationLandscapeRight 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /LCChartView/LCChartView/LCArcView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LCArcView.h 3 | // HeJing 4 | // 5 | // Created by liangrongchang on 16/10/20. 6 | // Copyright © 2016年 Rochang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LCArcView : UIView 12 | 13 | @property (nonatomic, assign) CGFloat outerRadius; 14 | @property (nonatomic, assign) CGFloat arcWidth; 15 | @property (nonatomic, strong) UIColor *arcColor; 16 | @property (nonatomic, strong) UIColor *bgArcColor; 17 | @property (nonatomic, assign) NSTimeInterval duration; 18 | @property (nonatomic, assign) BOOL showPercentLabel; 19 | @property (nonatomic, assign) CGPoint centerP; 20 | /** 背景圆环,两环间隙 */ 21 | @property (nonatomic, assign) CGFloat arcsMargin; 22 | 23 | /** 设置percentLabel设置 */ 24 | @property (nonatomic, copy) void (^percentLabelBlock)(UILabel *label); 25 | /** 单圆环 */ 26 | - (void)showArcViewWithStartAngle:(CGFloat)startA endAngle:(CGFloat)endA animaion:(BOOL)animaion; 27 | /** 背景圆环 */ 28 | - (void)showArcViewWithBgStartAngle:(CGFloat)bgStartA endBgAngle:(CGFloat)bgEndA bgAnimation:(BOOL)bgAnimation StartAngle:(CGFloat)startA endAngle:(CGFloat)endA animaion:(BOOL)animaion; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /LCChartView/LCChartView/LCArcView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LCArcView.m 3 | // HeJing 4 | // 5 | // Created by liangrongchang on 16/10/20. 6 | // Copyright © 2016年 Rochang. All rights reserved. 7 | // 8 | 9 | #import "LCArcView.h" 10 | #import "UIView+LCLayout.h" 11 | 12 | static CGFloat startAngle = 0; 13 | static CGFloat endAngle = 0; 14 | 15 | @interface LCArcView () 16 | 17 | @property (nonatomic, strong) CAShapeLayer *arcLayer; 18 | @property (nonatomic, strong) CAShapeLayer *bgArcLayer; 19 | @property (nonatomic, strong) UILabel *percentLabel; 20 | @property (nonatomic, weak) NSTimer *timer; 21 | 22 | @end 23 | 24 | @implementation LCArcView 25 | 26 | - (instancetype)initWithFrame:(CGRect)frame { 27 | if (self = [super initWithFrame:frame]) { 28 | [self initData]; 29 | } 30 | return self; 31 | } 32 | 33 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 34 | if (self = [super initWithCoder:aDecoder]) { 35 | [self initData]; 36 | } 37 | return self; 38 | } 39 | 40 | - (void)initData { 41 | self.outerRadius = MIN(self.LC_width / 2, self.LC_height / 2); 42 | self.arcWidth = 10; 43 | self.bgArcColor = [UIColor grayColor]; 44 | self.arcColor = [UIColor blueColor]; 45 | self.duration = 1.0; 46 | _arcsMargin = 3; 47 | _showPercentLabel = YES; 48 | self.centerP = CGPointMake(self.LC_width / 2, self.LC_height / 2); 49 | } 50 | 51 | - (void)resetData { 52 | if (_arcLayer) { 53 | [_arcLayer removeFromSuperlayer]; 54 | _arcLayer = nil; 55 | } 56 | if (_bgArcLayer) { 57 | [_bgArcLayer removeFromSuperlayer]; 58 | _arcLayer = nil; 59 | } 60 | if (_percentLabel) { 61 | [_percentLabel removeFromSuperview]; 62 | _percentLabel = nil; 63 | } 64 | } 65 | 66 | - (void)showArcViewWithStartAngle:(CGFloat)startA endAngle:(CGFloat)endA animaion:(BOOL)animaion { 67 | [self resetData]; 68 | startAngle = startA; 69 | endAngle = endA; 70 | CGFloat radius = self.outerRadius - self.arcWidth / 2; 71 | self.arcLayer = [self layerWithRadius:radius borderWidth:self.arcWidth strokeColor:self.arcColor startAngle:startA endAngle:endA]; 72 | [self addAnimationForLay:self.arcLayer animation:animaion]; 73 | [self addPercentLabel:_showPercentLabel]; 74 | } 75 | 76 | - (void)showArcViewWithBgStartAngle:(CGFloat)bgStartA endBgAngle:(CGFloat)bgEndA bgAnimation:(BOOL)bgAnimation StartAngle:(CGFloat)startA endAngle:(CGFloat)endA animaion:(BOOL)animaion { 77 | [self resetData]; 78 | startAngle = startA; 79 | endAngle = endA; 80 | CGFloat radius = self.outerRadius - self.arcWidth / 2; 81 | self.bgArcLayer = [self layerWithRadius:radius borderWidth:self.arcWidth strokeColor:self.bgArcColor startAngle:bgStartA endAngle:bgEndA]; 82 | CGFloat interArcWidth = self.arcWidth - self.arcsMargin * 2; 83 | if (interArcWidth <= 0) { 84 | NSLog(@"内圆环为0"); 85 | } 86 | self.arcLayer = [self layerWithRadius:radius borderWidth:interArcWidth strokeColor:self.arcColor startAngle:startA endAngle:endA]; 87 | [self addAnimationForLay:self.bgArcLayer animation:bgAnimation]; 88 | [self addAnimationForLay:self.arcLayer animation:animaion]; 89 | [self addPercentLabel:_showPercentLabel]; 90 | } 91 | 92 | #pragma mark - private 93 | - (void)addPercentLabel:(BOOL)show { 94 | if (!show) return; 95 | [self addSubview:self.percentLabel]; 96 | self.percentLabel.LC_size = CGSizeMake((self.outerRadius - 2 * self.arcWidth) * 2, 20); 97 | self.percentLabel.center = self.centerP; 98 | if (self.percentLabelBlock) { 99 | self.percentLabelBlock(_percentLabel); 100 | } 101 | __weak typeof(self) weakSelf = self; 102 | __block int i = 0; 103 | CGFloat percent = (endAngle - startAngle) / (M_PI * 2) * 100; 104 | self.timer = [NSTimer scheduledTimerWithTimeInterval:_duration / percent repeats:YES block:^(NSTimer * _Nonnull timer) { 105 | weakSelf.percentLabel.text = [NSString stringWithFormat:@"%02d%%", i++]; 106 | if (i > percent) { 107 | [timer invalidate]; 108 | timer = nil; 109 | } 110 | }]; 111 | } 112 | 113 | - (CAShapeLayer *)layerWithRadius:(CGFloat)radius 114 | borderWidth:(CGFloat)borderWidth 115 | strokeColor:(UIColor *)strokeColor 116 | startAngle:(CGFloat)startA 117 | endAngle:(CGFloat)endA { 118 | 119 | CAShapeLayer *layer = [CAShapeLayer layer]; 120 | UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:self.centerP radius:radius startAngle:startA endAngle:endA clockwise:YES]; 121 | path.lineWidth = borderWidth; 122 | layer.fillColor = [UIColor clearColor].CGColor; 123 | layer.strokeColor = strokeColor.CGColor; 124 | layer.lineWidth = borderWidth; 125 | layer.path = path.CGPath; 126 | layer.lineCap = kCALineCapRound; 127 | return layer; 128 | } 129 | 130 | - (void)addAnimationForLay:(CAShapeLayer *)layer animation:(BOOL)animate { 131 | [self.layer addSublayer:layer]; 132 | if (animate) { 133 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 134 | animation.duration = _duration; 135 | animation.fromValue = @0; 136 | animation.toValue = @1; 137 | animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 138 | [layer addAnimation:animation forKey:nil]; 139 | } 140 | } 141 | 142 | - (UILabel *)percentLabel { 143 | if (!_percentLabel) { 144 | _percentLabel = [[UILabel alloc] init]; 145 | _percentLabel.textAlignment = NSTextAlignmentCenter; 146 | _percentLabel.backgroundColor = [UIColor clearColor]; 147 | _percentLabel.adjustsFontSizeToFitWidth = YES; 148 | } 149 | return _percentLabel; 150 | } 151 | 152 | @end 153 | -------------------------------------------------------------------------------- /LCChartView/LCChartView/LCChartView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LCChartView.h 3 | // LCProject 4 | // 5 | // Created by liangrongchang on 2017/1/6. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LCChartViewModel.h" 11 | 12 | typedef NS_ENUM(NSInteger, LCChartViewType) { 13 | LCChartViewTypeLine, 14 | LCChartViewTypeBar 15 | }; 16 | 17 | @interface LCChartView : UIView 18 | 19 | @property (copy, nonatomic) void (^plotClickBlock)(NSInteger index); 20 | 21 | // switch 22 | @property (assign, nonatomic) BOOL showGridding; 23 | @property (assign, nonatomic) BOOL showAnimation; 24 | @property (assign, nonatomic) BOOL showPlotsLabel; 25 | @property (assign, nonatomic) BOOL lineChartFillView; 26 | @property (assign, nonatomic) BOOL showNote; 27 | @property (assign, nonatomic) LCChartViewType chartViewType; 28 | 29 | // data 30 | @property (copy, nonatomic) NSString *title; 31 | @property (copy, nonatomic) NSString *xAxisTitle; 32 | @property (copy, nonatomic) NSString *yAxisTitle; 33 | @property (strong, nonatomic) NSArray *xAxisTitleArray; 34 | 35 | // size 36 | @property (assign, nonatomic) CGFloat axisTitleSizeFont; 37 | @property (assign, nonatomic) CGFloat axisWidth; 38 | @property (assign, nonatomic) CGFloat xAxisTextMargin; 39 | @property (assign, nonatomic) CGFloat yAxisToLeft; 40 | @property (assign, nonatomic) NSInteger yAxisCount; 41 | @property (assign, nonatomic) CGFloat topMargin; 42 | @property (assign, nonatomic) CGFloat xTextToAxis; 43 | @property (assign, nonatomic) CGFloat yTextToAxis; 44 | @property (assign, nonatomic) CGFloat axisFontSize; 45 | @property (assign, nonatomic) CGFloat titleFontSize; 46 | @property (assign, nonatomic) CGFloat plotsLabelFontSize; 47 | @property (assign, nonatomic) CGFloat plotsButtonWH; 48 | @property (assign, nonatomic) CGFloat lineChartWidth; 49 | @property (assign, nonatomic) CGFloat chartViewRightMargin; 50 | @property (assign, nonatomic) CGFloat displayPlotToLabel; 51 | @property (assign, nonatomic) CGFloat barWidth; 52 | @property (assign, nonatomic) CGFloat barMargin; 53 | 54 | // color 55 | @property (strong, nonatomic) UIColor *backColor; 56 | @property (strong, nonatomic) UIColor *yTextColor; 57 | @property (strong, nonatomic) UIColor *xTextColor; 58 | @property (strong, nonatomic) UIColor *axisColor; 59 | @property (strong, nonatomic) UIColor *plotsLabelColor; 60 | @property (strong, nonatomic) UIColor *plotsLabelSelectedColor; 61 | @property (strong, nonatomic) UIColor *lineChartFillViewColor; 62 | 63 | @property (strong, nonatomic) UIColor *plotsButtonColor; 64 | @property (strong, nonatomic) UIColor *plotsButtonSelectedColor; 65 | // 或者图片 66 | @property (strong, nonatomic) NSString *plotsButtonImage; 67 | @property (strong, nonatomic) NSString *plotsButtonSelectedImage; 68 | 69 | /** 设置title属性Block */ 70 | @property (copy, nonatomic) void(^comfigurateTitleLabel)(UILabel *title); 71 | 72 | /** 设置yAxisLabel属性Block */ 73 | @property (copy, nonatomic) void(^yAxisLabelUIBlock)(UILabel *yAxisLabel, NSInteger index); 74 | 75 | /** 设置xAxisLabel属性Block */ 76 | @property (copy, nonatomic) void(^xAxisLabelUIBlock)(UILabel *xAxisLabel, NSInteger index); 77 | 78 | /** 设置yAxisTitleLabel属性Block */ 79 | @property (copy, nonatomic) void(^yAxisTitleLabelUIBlock)(UILabel *yAxisTitleLabel); 80 | 81 | /** 设置xAxisTitleLabel属性Block */ 82 | @property (copy, nonatomic) void(^xAxisTitleLabelUIBlock)(UILabel *xAxisTitleLabel); 83 | 84 | /** 设置xAxisTextMargin */ 85 | - (void)setChartViewXAxisTextMargin:(CGFloat)xAxisTextMargin; 86 | 87 | /** 初始化 */ 88 | + (instancetype)chartViewWithType:(LCChartViewType)type; 89 | - (instancetype)initWithFrame:(CGRect)frame chartViewType:(LCChartViewType)type; 90 | 91 | /** 开始描绘LCChartView */ 92 | - (void)showChartViewWithYAxisMaxValue:(CGFloat)yAxisMaxValue dataSource:(NSArray *)dataSource; 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /LCChartView/LCChartView/LCChartView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LCChartView.m 3 | // LCProject 4 | // 5 | // Created by liangrongchang on 2017/1/6. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import "LCChartView.h" 10 | #import "UIView+LCLayout.h" 11 | #import "LCMethod.h" 12 | 13 | static NSTimeInterval duration = 0.8; 14 | static CGFloat yAxisMaxY = 0; 15 | static CGFloat yTextCenterMargin = 0; 16 | /** 显示数据的区域高度 */ 17 | static CGFloat dataChartHeight = 0; 18 | static CGFloat axisLabelHieght = 0; 19 | static CGFloat xAxisMaxX = 0; 20 | static CGFloat noteViewRowH = 15; 21 | 22 | @interface LCChartView () 23 | // UI 24 | @property (strong, nonatomic) NSMutableArray *yAxisLabels; 25 | @property (strong, nonatomic) NSMutableArray *xAxisLabels; 26 | @property (strong, nonatomic) NSMutableArray *allSubView; 27 | @property (strong, nonatomic) UIScrollView *noteView; 28 | @property (strong, nonatomic) UIScrollView *scrollView; 29 | @property (strong, nonatomic) UILabel *yAxisLabel; 30 | @property (strong, nonatomic) UILabel *xAxisLabel; 31 | 32 | // 数据 33 | @property (assign, nonatomic) CGFloat yAxisMaxValue; 34 | @property (strong, nonatomic) NSArray *dataSource; 35 | 36 | /** 捏合时记录原先X轴点距离 */ 37 | @property (assign, nonatomic) CGFloat orginXAxisMargin; 38 | /** 捏合时记录原先动画flag */ 39 | @property (assign, nonatomic) BOOL orginAnimation; 40 | /** X轴箭头离XAxisLabel距离 */ 41 | @property (assign, nonatomic) CGFloat xArrowsToText; 42 | @property (assign, nonatomic) CGPoint originPoint; 43 | /** 第一次动画 */ 44 | @property (strong, nonatomic) NSMutableArray *firstLayers; 45 | /** 第二次动画 */ 46 | @property (strong, nonatomic) NSMutableArray *scondLayers; 47 | 48 | // response 49 | @property (strong, nonatomic) UITapGestureRecognizer *twoTap; 50 | @property (strong, nonatomic) UIPinchGestureRecognizer *pinch; 51 | 52 | @end 53 | 54 | @implementation LCChartView 55 | 56 | #pragma mark - API 57 | + (instancetype)chartViewWithType:(LCChartViewType)type { 58 | LCChartView *axisView = [[LCChartView alloc] init]; 59 | axisView.chartViewType = type; 60 | return axisView; 61 | } 62 | 63 | - (instancetype)initWithFrame:(CGRect)frame chartViewType:(LCChartViewType)type { 64 | if (self = [super initWithFrame:frame]) { 65 | [self initData]; 66 | self.chartViewType = type; 67 | } 68 | return self; 69 | } 70 | 71 | - (instancetype)initWithFrame:(CGRect)frame { 72 | if (self = [super initWithFrame:frame]) { 73 | [self initData]; 74 | } 75 | return self; 76 | } 77 | 78 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 79 | if (self = [super initWithCoder:aDecoder]) { 80 | [self initData]; 81 | } 82 | return self; 83 | } 84 | 85 | - (void)showChartViewWithYAxisMaxValue:(CGFloat)yAxisMaxValue dataSource:(NSArray *)dataSource { 86 | _yAxisMaxValue = yAxisMaxValue; 87 | self.dataSource = dataSource; 88 | [self showChartView]; 89 | } 90 | 91 | #pragma mark - private method 92 | - (void)initData { 93 | _axisColor = [UIColor darkGrayColor]; 94 | _backColor = [UIColor whiteColor]; 95 | _axisTitleSizeFont = 10; 96 | _plotsLabelSelectedColor = _plotsButtonSelectedColor = _plotsLabelColor = [UIColor redColor]; 97 | _yTextColor = _xTextColor = _plotsButtonColor = _lineChartFillViewColor = [UIColor darkGrayColor]; 98 | _barWidth = 20; 99 | _yAxisMaxValue = 1000; 100 | _chartViewType = LCChartViewTypeLine; 101 | _axisFontSize = 12; 102 | _plotsLabelFontSize = 9; 103 | _barMargin = 20; 104 | _yAxisToLeft = _chartViewRightMargin = _topMargin = 35; 105 | _displayPlotToLabel = 3; 106 | _axisWidth = _lineChartWidth = 1; 107 | _xAxisTextMargin = _orginXAxisMargin = _xArrowsToText = 30; 108 | _yTextToAxis = _yAxisCount = _plotsButtonWH = 5; 109 | _yAxisTitle = @"y"; 110 | _xAxisTitle = @"x"; 111 | _xAxisTitleArray = @[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8" , @"9", @"10", @"11", @"12"]; 112 | _lineChartFillView = _showPlotsLabel = NO; 113 | _showAnimation = _orginAnimation = _showGridding = _showNote = YES; 114 | 115 | } 116 | 117 | - (void)showChartView { 118 | if (self.dataSource.count == 0) { 119 | NSLog(@"请设置展示的点数据"); 120 | return; 121 | }; 122 | if (self.chartViewType == LCChartViewTypeBar && _xAxisTextMargin < _barWidth * self.dataSource.count + self.barMargin) { 123 | _xAxisTextMargin = self.orginXAxisMargin = _barWidth * self.dataSource.count + self.barMargin; 124 | } 125 | 126 | // 截取数据 127 | for (LCChartViewModel *model in self.dataSource) { 128 | if (model.plots.count > self.xAxisTitleArray.count) { 129 | NSLog(@"展示的点数据比X轴默认的点多,请设置xAxisTitleArray"); 130 | model.plots = [model.plots subarrayWithRange:NSMakeRange(0, self.xAxisTitleArray.count)]; 131 | } 132 | } 133 | [self resetDataSource]; 134 | [self drawYAxis]; 135 | [self drawXAxis]; 136 | [self drawTilte]; 137 | [self drawYSeparators]; 138 | if (self.chartViewType == LCChartViewTypeLine) { 139 | [self drawXSeparators]; 140 | [self drawLineChartViewPots]; 141 | [self drawLineChartViewLines]; 142 | } else { 143 | [self drawBarChartViewBars]; 144 | } 145 | [self drawDisplayLabels]; 146 | [self addNote]; 147 | [self addAnimation:self.showAnimation]; 148 | } 149 | 150 | #pragma mark - 重置数据 151 | - (void)resetDataSource { 152 | for (LCChartViewModel *model in self.dataSource) { 153 | if (model.plotButtons) { 154 | [model.plotButtons removeAllObjects]; 155 | [model.plotButtons makeObjectsPerformSelector:@selector(removeFromSuperview)]; 156 | } 157 | } 158 | // 移除所有的Label,CAShapeLayer 159 | if (self.allSubView) { 160 | [self.allSubView makeObjectsPerformSelector:@selector(removeFromSuperview)]; 161 | [self.allSubView removeAllObjects]; 162 | } 163 | if (self.firstLayers.count) { 164 | [self.firstLayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; 165 | [self.firstLayers removeAllObjects]; 166 | } 167 | if (self.scondLayers.count) { 168 | [self.scondLayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; 169 | [self.scondLayers removeAllObjects]; 170 | } 171 | if (self.xAxisLabels.count) { 172 | [self.xAxisLabels makeObjectsPerformSelector:@selector(removeFromSuperview)]; 173 | [self.xAxisLabels removeAllObjects]; 174 | } 175 | if (self.yAxisLabels.count) { 176 | [self.yAxisLabels makeObjectsPerformSelector:@selector(removeFromSuperview)]; 177 | [self.yAxisLabels removeAllObjects]; 178 | } 179 | } 180 | 181 | #pragma mark - 描绘Y轴 182 | - (void)drawYAxis { 183 | axisLabelHieght = [LCMethod sizeWithText:@"x" fontSize:_axisFontSize].height; 184 | // 数据展示的高度 185 | dataChartHeight = self.LC_height - _topMargin - _xTextToAxis - axisLabelHieght; 186 | 187 | // ylabel之间的间隙 188 | yTextCenterMargin = dataChartHeight / _yAxisCount; 189 | yAxisMaxY = MAX(_topMargin - yTextCenterMargin / 2, 0); 190 | UIBezierPath *yAxisPath = [UIBezierPath bezierPath]; 191 | _originPoint = CGPointMake(_yAxisToLeft, self.LC_height - axisLabelHieght - _xTextToAxis); 192 | [yAxisPath moveToPoint:_originPoint]; 193 | [yAxisPath addLineToPoint:CGPointMake(_yAxisToLeft, yAxisMaxY)]; 194 | [yAxisPath addLineToPoint:CGPointMake(_yAxisToLeft - (_axisWidth + 2), yAxisMaxY + (_axisWidth + 2))]; 195 | [yAxisPath moveToPoint:CGPointMake(_yAxisToLeft, yAxisMaxY)]; 196 | [yAxisPath addLineToPoint:CGPointMake(_yAxisToLeft + (_axisWidth + 2), yAxisMaxY + (_axisWidth + 2))]; 197 | 198 | CAShapeLayer *shapeLayer = [self shapeLayerWithPath:yAxisPath lineWidth:_axisWidth fillColor:[UIColor clearColor] strokeColor:_axisColor]; 199 | [self.layer addSublayer:shapeLayer]; 200 | [self.firstLayers addObject:shapeLayer]; 201 | 202 | // 添加Y轴Label 203 | for (int i = 0; i < _yAxisCount + 1; i++) { 204 | CGFloat avgValue = _yAxisMaxValue / (_yAxisCount); 205 | NSString *title = [NSString stringWithFormat:@"%.0f", avgValue * i]; 206 | UILabel *label = [self labelWithTextColor:_yTextColor backColor:[UIColor clearColor] textAlignment:NSTextAlignmentRight lineNumber:1 tiltle:title fontSize:_axisFontSize]; 207 | if (_yAxisLabelUIBlock) { 208 | _yAxisLabelUIBlock(label, i); 209 | } 210 | label.LC_x = 0; 211 | label.LC_height = axisLabelHieght; 212 | label.LC_width = _yAxisToLeft - _yTextToAxis; 213 | label.LC_centerY = _topMargin + (_yAxisCount - i) * yTextCenterMargin; 214 | [self addSubview:label]; 215 | [self.yAxisLabels addObject:label]; 216 | } 217 | [self.allSubView addObjectsFromArray:self.yAxisLabels]; 218 | 219 | // yTitleLabel 220 | _yAxisLabel = [self labelWithTextColor:_axisColor backColor:[UIColor clearColor] textAlignment:NSTextAlignmentLeft lineNumber:1 tiltle:self.yAxisTitle fontSize:_axisTitleSizeFont]; 221 | if (_yAxisTitleLabelUIBlock) { 222 | _yAxisTitleLabelUIBlock(_yAxisLabel); 223 | } 224 | [_yAxisLabel sizeToFit]; 225 | _yAxisLabel.LC_y = yAxisMaxY - _yAxisLabel.LC_height - 5; 226 | _yAxisLabel.LC_centerX = _originPoint.x; 227 | [self addSubview:_yAxisLabel]; 228 | [self.allSubView addObject:_yAxisLabel]; 229 | 230 | // 添加scrollview 231 | [self insertSubview:self.scrollView atIndex:0]; 232 | self.scrollView.frame = CGRectMake(_yAxisToLeft, 0, self.LC_width - _yAxisToLeft, self.LC_height); 233 | 234 | self.scrollView.backgroundColor = self.backgroundColor = _backColor; 235 | } 236 | 237 | #pragma mark - 描绘X轴 238 | - (void)drawXAxis { 239 | // 添加X轴Label 240 | for (int i = 0; i < self.xAxisTitleArray.count; i++) { 241 | NSString *title = self.xAxisTitleArray[i]; 242 | 243 | UILabel *label = [self labelWithTextColor:_xTextColor backColor:[UIColor clearColor] textAlignment:NSTextAlignmentCenter lineNumber:1 tiltle:title fontSize:_axisFontSize]; 244 | if (_xAxisLabelUIBlock) { 245 | _xAxisLabelUIBlock(label, i); 246 | } 247 | CGSize labelSize = [LCMethod sizeWithText:title fontSize:_axisFontSize]; 248 | label.LC_x = (i + 1) * _xAxisTextMargin - labelSize.width / 2; 249 | label.LC_y = self.LC_height - labelSize.height; 250 | label.LC_size = labelSize; 251 | [self.scrollView addSubview:label]; 252 | [self.xAxisLabels addObject:label]; 253 | } 254 | [self.allSubView addObjectsFromArray:self.xAxisLabels]; 255 | // 处理重叠label 256 | [self handleOverlapViewWithViews:self.xAxisLabels]; 257 | // 画轴 258 | UIBezierPath *xAxisPath = [UIBezierPath bezierPath]; 259 | [xAxisPath moveToPoint:CGPointMake(0, _originPoint.y)]; 260 | xAxisMaxX = (self.xAxisTitleArray.count + 1) * _xAxisTextMargin; 261 | 262 | // scrollView的contentSize 263 | self.scrollView.contentSize = CGSizeMake(xAxisMaxX + self.chartViewRightMargin, 0); 264 | 265 | [xAxisPath addLineToPoint:CGPointMake(xAxisMaxX, _originPoint.y)]; 266 | [xAxisPath addLineToPoint:CGPointMake(xAxisMaxX - (_axisWidth + 2), _originPoint.y - (_axisWidth + 2))]; 267 | [xAxisPath moveToPoint:CGPointMake(xAxisMaxX, _originPoint.y)]; 268 | [xAxisPath addLineToPoint:CGPointMake(xAxisMaxX - (_axisWidth + 2) , _originPoint.y + (_axisWidth + 2))]; 269 | CAShapeLayer *xAxisLayer = [self shapeLayerWithPath:xAxisPath lineWidth:_axisWidth fillColor:[UIColor clearColor] strokeColor:_axisColor]; 270 | [self.scrollView.layer addSublayer:xAxisLayer]; 271 | [self.firstLayers addObject:xAxisLayer]; 272 | 273 | // xTitleLabel 274 | _xAxisLabel = [self labelWithTextColor:_axisColor backColor:[UIColor clearColor] textAlignment:NSTextAlignmentCenter lineNumber:1 tiltle:self.xAxisTitle fontSize:_axisTitleSizeFont]; 275 | if (_xAxisTitleLabelUIBlock) { 276 | _xAxisTitleLabelUIBlock(_xAxisLabel); 277 | } 278 | [_xAxisLabel sizeToFit]; 279 | _xAxisLabel.LC_left = xAxisMaxX + 5; 280 | _xAxisLabel.LC_centerY = _originPoint.y; 281 | [self.scrollView addSubview:_xAxisLabel]; 282 | [self.allSubView addObject:_xAxisLabel]; 283 | } 284 | 285 | #pragma mark - 标题 286 | - (void)drawTilte { 287 | // titleLabel 288 | UILabel *titleLabel = [self labelWithTextColor:_axisColor backColor:[UIColor clearColor] textAlignment:NSTextAlignmentCenter lineNumber:1 tiltle:_title fontSize:_titleFontSize]; 289 | if (_comfigurateTitleLabel) { 290 | _comfigurateTitleLabel(titleLabel); 291 | } 292 | [titleLabel sizeToFit]; 293 | titleLabel.LC_centerX = self.LC_width / 2; 294 | titleLabel.LC_y = 5; 295 | [self addSubview:titleLabel]; 296 | [self.allSubView addObject:titleLabel]; 297 | } 298 | 299 | #pragma mark - Y轴分割线 300 | - (void)drawYSeparators { 301 | // 添加Y轴分割线 302 | for (int i = 0; i < self.yAxisLabels.count; i++) { 303 | CAShapeLayer *yshapeLayer = nil; 304 | UIBezierPath *ySeparatorPath = [UIBezierPath bezierPath]; 305 | if (_showGridding) { 306 | [ySeparatorPath moveToPoint:CGPointMake(0, self.yAxisLabels[i].LC_centerY)]; 307 | [ySeparatorPath addLineToPoint:CGPointMake(xAxisMaxX, self.yAxisLabels[i].LC_centerY)]; 308 | yshapeLayer = [self shapeLayerWithPath:ySeparatorPath lineWidth:0.5 fillColor:[UIColor clearColor] strokeColor:_axisColor]; 309 | yshapeLayer.lineDashPattern = @[@(3), @(3)]; 310 | [self.scrollView.layer addSublayer:yshapeLayer]; 311 | } else { 312 | [ySeparatorPath moveToPoint:CGPointMake(_yAxisToLeft, self.yAxisLabels[i].LC_centerY)]; 313 | [ySeparatorPath addLineToPoint:CGPointMake(_yAxisToLeft + 5, self.yAxisLabels[i].LC_centerY)]; 314 | yshapeLayer = [self shapeLayerWithPath:ySeparatorPath lineWidth:_axisWidth fillColor:[UIColor clearColor] strokeColor:_axisColor]; 315 | [self.layer addSublayer:yshapeLayer]; 316 | } 317 | [self.firstLayers addObject:yshapeLayer]; 318 | } 319 | } 320 | 321 | #pragma mark - X轴分割线 322 | - (void)drawXSeparators { 323 | // 添加X轴分割线 324 | for (int i = 0; i < self.xAxisLabels.count; i++) { 325 | CAShapeLayer *xshapeLayer = nil; 326 | UIBezierPath *xSeparatorPath = [UIBezierPath bezierPath]; 327 | [xSeparatorPath moveToPoint:CGPointMake(self.xAxisLabels[i].LC_centerX, _originPoint.y)]; 328 | if (_showGridding) { 329 | [xSeparatorPath addLineToPoint:CGPointMake(self.xAxisLabels[i].LC_centerX, self.yAxisLabels.lastObject.LC_centerY)]; 330 | xshapeLayer = [self shapeLayerWithPath:xSeparatorPath lineWidth:0.5 fillColor:[UIColor clearColor] strokeColor:_axisColor]; 331 | xshapeLayer.lineDashPattern = @[@(3), @(3)]; 332 | } else { 333 | [xSeparatorPath addLineToPoint:CGPointMake(self.xAxisLabels[i].LC_centerX, _originPoint.y - 5)]; 334 | xshapeLayer = [self shapeLayerWithPath:xSeparatorPath lineWidth:_axisWidth fillColor:[UIColor clearColor] strokeColor:_axisColor]; 335 | } 336 | [self.scrollView.layer addSublayer:xshapeLayer]; 337 | [self.firstLayers addObject:xshapeLayer]; 338 | } 339 | } 340 | 341 | #pragma mark - 显示数据label 342 | - (void)drawDisplayLabels { 343 | if (!_showPlotsLabel) { 344 | return; 345 | } 346 | // 多组数据显示label太混乱 347 | if (_chartViewType == LCChartViewTypeLine && self.dataSource.count > 1) { 348 | return; 349 | } 350 | NSInteger centerFlag = self.dataSource.count / 2; 351 | for (int i = 0 ; i < self.dataSource.count; i++) { 352 | LCChartViewModel *model = self.dataSource[i]; 353 | NSMutableArray *plotLabels = [NSMutableArray array]; 354 | for (int j = 0; j < model.plots.count; j++) { 355 | NSString *value = model.plots[j]; 356 | if (value.floatValue < 0) { 357 | value = @"0"; 358 | } 359 | UILabel *label = [self labelWithTextColor:self.plotsLabelColor backColor:[UIColor clearColor] textAlignment:NSTextAlignmentCenter lineNumber:1 tiltle:value fontSize:self.plotsLabelFontSize]; 360 | label.tag = j; 361 | [label sizeToFit]; 362 | switch (self.chartViewType) { 363 | case LCChartViewTypeLine:{ 364 | label.LC_centerX = self.xAxisLabels[j].LC_centerX; 365 | } 366 | break; 367 | case LCChartViewTypeBar:{ 368 | if (self.dataSource.count % 2 == 0) { // 双数组 369 | label.LC_centerX = self.xAxisLabels[j].LC_centerX + ( 1/2.0 + i - centerFlag) * _barWidth; 370 | } else { // 单数组 371 | label.LC_centerX = self.xAxisLabels[j].LC_centerX + (i - centerFlag) * _barWidth; 372 | } 373 | } 374 | break; 375 | 376 | default: 377 | break; 378 | } 379 | label.LC_bottom = [self getValueHeightWith:value] - _displayPlotToLabel; 380 | [self.scrollView addSubview:label]; 381 | [plotLabels addObject:label]; 382 | [self.allSubView addObjectsFromArray:plotLabels]; 383 | // 处理重叠label 384 | [self handleOverlapViewWithViews:plotLabels]; 385 | } 386 | } 387 | } 388 | 389 | #pragma mark - 描绘折线图点和线 390 | /** 描述折线图数据点 */ 391 | - (void)drawLineChartViewPots { 392 | for (int i = 0; i < self.dataSource.count; i++) { 393 | LCChartViewModel *model = self.dataSource[i]; 394 | if (model.plotButtons.count) { 395 | [model.plotButtons removeAllObjects]; 396 | [model.plotButtons makeObjectsPerformSelector:@selector(removeFromSuperview)]; 397 | } 398 | // 画点 399 | for (int j = 0; j < model.plots.count; j++) { 400 | // 添加数据点button 401 | UIButton *button = [[UIButton alloc] init]; 402 | [button addTarget:self action:@selector(plotsButtonDidClick:) forControlEvents:UIControlEventTouchUpInside]; 403 | if (self.plotsButtonImage.length && self.plotsButtonSelectedImage.length) { 404 | [button setBackgroundImage:[UIImage imageNamed:self.plotsButtonImage] forState:UIControlStateNormal]; 405 | [button setBackgroundImage:[UIImage imageNamed:self.plotsButtonSelectedImage] forState:UIControlStateSelected]; 406 | } else { 407 | [button setBackgroundImage:[LCMethod imageFromColor:self.plotsButtonColor rect:CGRectMake(0, 0, 1, 1)] forState:UIControlStateNormal]; 408 | [button setBackgroundImage:[LCMethod imageFromColor:self.plotsButtonSelectedColor rect:CGRectMake(0, 0, 1, 1)] forState:UIControlStateSelected]; 409 | } 410 | button.tag = j; 411 | button.LC_size = CGSizeMake(self.plotsButtonWH, self.plotsButtonWH); 412 | button.center = CGPointMake(self.xAxisLabels[j].LC_centerX, [self getValueHeightWith:model.plots[j]]); 413 | button.layer.cornerRadius = self.plotsButtonWH / 2; 414 | button.layer.masksToBounds = YES; 415 | 416 | [self.allSubView addObject:button]; 417 | [model.plotButtons addObject:button]; 418 | [self.scrollView addSubview:button]; 419 | // 处理重叠点 420 | [self handleOverlapViewWithViews:model.plotButtons]; 421 | } 422 | } 423 | } 424 | 425 | /** 根据数据点画线 */ 426 | - (void)drawLineChartViewLines { 427 | for (LCChartViewModel *model in self.dataSource) { 428 | UIBezierPath *lineChartPath = [UIBezierPath bezierPath]; 429 | // 填充 430 | CAShapeLayer *lineShapeLayer = nil; 431 | if (self.lineChartFillView) { 432 | [lineChartPath moveToPoint:CGPointMake(model.plotButtons.firstObject.center.x, _originPoint.y)]; 433 | for (int i = 0; i < model.plotButtons.count; i++) { 434 | [lineChartPath addLineToPoint:model.plotButtons[i].center]; 435 | } 436 | [lineChartPath addLineToPoint:CGPointMake(model.plotButtons.lastObject.center.x, _originPoint.y)]; 437 | lineShapeLayer = [LCMethod shapeLayerWithPath:lineChartPath lineWidth:self.lineChartWidth fillColor:self.lineChartFillViewColor strokeColor:model.color]; 438 | } else { 439 | // 不填充 440 | [lineChartPath moveToPoint:model.plotButtons.firstObject.center]; 441 | for (int i = 1; i < model.plotButtons.count; i++) { 442 | [lineChartPath addLineToPoint:model.plotButtons[i].center]; 443 | } 444 | lineShapeLayer = [self shapeLayerWithPath:lineChartPath lineWidth:self.lineChartWidth fillColor:[UIColor clearColor] strokeColor:model.color]; 445 | } 446 | [self.scondLayers addObject:lineShapeLayer]; 447 | [self.scrollView.layer insertSublayer:lineShapeLayer below:model.plotButtons.firstObject.layer]; 448 | } 449 | 450 | } 451 | 452 | #pragma mark - ChartViewBar柱状图 453 | /** 根据显示点描绘柱状图 */ 454 | - (void)drawBarChartViewBars { 455 | NSInteger centerFlag = self.dataSource.count / 2; 456 | for (int i = 0; i < self.dataSource.count; i++) { 457 | LCChartViewModel *model = self.dataSource[i]; 458 | for (int j = 0; j < model.plots.count; j++) { 459 | UIBezierPath *barPath = [UIBezierPath bezierPath]; 460 | CGFloat startPointX = 0; 461 | switch (self.dataSource.count % 2) { 462 | case 0:{ // 双数组 463 | startPointX = self.xAxisLabels[j].LC_centerX + ( 1/2.0 + i - centerFlag) * _barWidth; 464 | } 465 | break; 466 | case 1:{ // 单数组 467 | startPointX = self.xAxisLabels[j].LC_centerX + (i - centerFlag) * _barWidth; 468 | } 469 | break; 470 | default: 471 | break; 472 | } 473 | [barPath moveToPoint:CGPointMake(startPointX, _originPoint.y)]; 474 | [barPath addLineToPoint:CGPointMake(startPointX, [self getValueHeightWith:model.plots[j]])]; 475 | CAShapeLayer *barShapeLayer = [self shapeLayerWithPath:barPath lineWidth:_barWidth fillColor:model.color strokeColor:model.color]; 476 | [self.scondLayers addObject:barShapeLayer]; 477 | [self.scrollView.layer addSublayer:barShapeLayer]; 478 | } 479 | } 480 | } 481 | 482 | #pragma mark - private method 483 | /** 处理label重叠显示的情况 */ 484 | - (void)handleOverlapViewWithViews:(NSArray *)views { 485 | // 如果Label的文字有重叠,那么隐藏 486 | UIView *firstView = views.firstObject; 487 | for (int i = 1; i < views.count; i++) { 488 | UIView *view = views[i]; 489 | CGFloat maxX = CGRectGetMaxX(firstView.frame); 490 | if ((maxX + 3) > view.LC_x) { 491 | view.hidden = YES; 492 | }else{ 493 | view.hidden = NO; 494 | firstView = view; 495 | } 496 | } 497 | } 498 | 499 | - (CAShapeLayer *)shapeLayerWithPath:(UIBezierPath *)path lineWidth:(CGFloat)lineWidth fillColor:(UIColor *)fillColor strokeColor:(UIColor *)strokeColor { 500 | CAShapeLayer *shapeLayer = [CAShapeLayer layer]; 501 | shapeLayer.lineWidth = lineWidth; 502 | shapeLayer.fillColor = fillColor.CGColor; 503 | shapeLayer.strokeColor = strokeColor.CGColor; 504 | shapeLayer.lineCap = kCALineCapButt; 505 | shapeLayer.lineJoin = kCALineJoinBevel; 506 | shapeLayer.path = path.CGPath; 507 | return shapeLayer; 508 | } 509 | 510 | /** 数据点高度 */ 511 | - (CGFloat)getValueHeightWith:(NSString *)value { 512 | return dataChartHeight - value.floatValue / _yAxisMaxValue * dataChartHeight + _topMargin; 513 | } 514 | 515 | /** label */ 516 | - (UILabel *)labelWithTextColor:(UIColor *)textColor backColor:(UIColor *)backColor textAlignment:(NSTextAlignment)textAlignment lineNumber:(NSInteger)number tiltle:(NSString *)title fontSize:(CGFloat)fontSize { 517 | UILabel *label = [[UILabel alloc] init]; 518 | label.text = title; 519 | label.textColor = textColor; 520 | if (backColor) { 521 | label.backgroundColor = backColor; 522 | } 523 | label.textAlignment = textAlignment; 524 | label.numberOfLines = number; 525 | if (fontSize != 0) { 526 | label.font = [UIFont systemFontOfSize:fontSize]; 527 | } 528 | 529 | return label; 530 | } 531 | 532 | /** 根据颜色生成图片 */ 533 | + (UIImage *)imageFromColor:(UIColor *)color rect:(CGRect)rect{ 534 | UIGraphicsBeginImageContext(rect.size); 535 | CGContextRef context = UIGraphicsGetCurrentContext(); 536 | CGContextSetFillColorWithColor(context, [color CGColor]); 537 | CGContextFillRect(context, rect); 538 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 539 | UIGraphicsEndImageContext(); 540 | return image; 541 | } 542 | 543 | - (void)addAnimation:(NSArray *)shapeLayers delegate:(id)delegate duration:(NSTimeInterval)duration { 544 | CABasicAnimation *stroke = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 545 | stroke.delegate = delegate; 546 | stroke.duration = duration; 547 | stroke.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 548 | stroke.fromValue = [NSNumber numberWithFloat:0.0f]; 549 | stroke.toValue = [NSNumber numberWithFloat:1.0f]; 550 | for (CAShapeLayer *shapeLayer in shapeLayers) { 551 | [shapeLayer addAnimation:stroke forKey:nil]; 552 | } 553 | } 554 | 555 | #pragma mark - response 556 | 557 | - (void)plotsButtonDidClick:(UIButton *)button { 558 | if (self.plotClickBlock) { 559 | self.plotClickBlock(button.tag); 560 | } 561 | } 562 | 563 | #pragma mark - CAAnimationDelegate 564 | - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 565 | for (CAShapeLayer *layer in self.scondLayers) { 566 | layer.hidden = NO; 567 | } 568 | [self addAnimation:self.scondLayers delegate:nil duration:duration]; 569 | 570 | [self.allSubView enumerateObjectsUsingBlock:^(UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 571 | [UIView animateWithDuration:duration animations:^{ 572 | obj.alpha = 1.0; 573 | }]; 574 | }]; 575 | } 576 | 577 | 578 | #pragma mark - scrollview的手势支持 579 | /** 双击 */ 580 | - (void)tapGesture:(UITapGestureRecognizer *)tap { 581 | _xAxisTextMargin *= 1.5; 582 | self.orginXAxisMargin = _xAxisTextMargin; 583 | [self showChartView]; 584 | } 585 | 586 | /** 捏合 */ 587 | - (void)pinchGesture:(UIPinchGestureRecognizer *)recognizer { 588 | 589 | switch (recognizer.state) { 590 | case UIGestureRecognizerStateBegan:{ 591 | self.orginAnimation = self.showAnimation; 592 | self.showAnimation = NO; 593 | } 594 | break; 595 | case UIGestureRecognizerStateChanged:{ 596 | _xAxisTextMargin = recognizer.scale * self.orginXAxisMargin; 597 | if (self.chartViewType == LCChartViewTypeLine) { 598 | if (_xAxisTextMargin < 10) { 599 | _xAxisTextMargin = 10; 600 | } 601 | } 602 | [self showChartView]; 603 | } 604 | break; 605 | case UIGestureRecognizerStateEnded:{ 606 | self.orginXAxisMargin = _xAxisTextMargin; 607 | self.showAnimation = self.orginAnimation; 608 | } 609 | break; 610 | 611 | default: 612 | break; 613 | } 614 | } 615 | 616 | /** addAnimation */ 617 | - (void)addAnimation:(BOOL)animation { 618 | if (animation) { 619 | [self.allSubView enumerateObjectsUsingBlock:^(UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 620 | obj.alpha = 0.0; 621 | }]; 622 | for (CAShapeLayer *layer in self.scondLayers) { 623 | layer.hidden = YES; 624 | } 625 | [self addAnimation:self.firstLayers delegate:self duration:0.5]; 626 | } 627 | } 628 | 629 | /** 添加注释 */ 630 | - (void)addNote { 631 | if (!_showNote) { 632 | return; 633 | } 634 | if (_noteView) { 635 | [_noteView removeFromSuperview]; 636 | _noteView = nil; 637 | } 638 | [self addSubview:self.noteView]; 639 | for (int i = 0; i < self.dataSource.count; i ++) { 640 | LCChartViewModel *model = self.dataSource[i]; 641 | UIView *view = [[UIView alloc] init]; 642 | view.backgroundColor = [UIColor clearColor]; 643 | view.frame = CGRectMake(0, noteViewRowH * i, self.noteView.LC_width, noteViewRowH); 644 | [self.noteView addSubview:view]; 645 | // label 646 | UILabel *label = [self labelWithTextColor:_yTextColor backColor:[UIColor clearColor] textAlignment:NSTextAlignmentLeft lineNumber:1 tiltle:model.project fontSize:_axisFontSize]; 647 | label.adjustsFontSizeToFitWidth = YES; 648 | label.frame = CGRectMake(view.LC_width / 2 + 10, 0, view.LC_width / 2, view.LC_height); 649 | [view addSubview:label]; 650 | [self.allSubView addObject:label]; 651 | 652 | if (self.chartViewType == LCChartViewTypeLine) { 653 | // 画线 654 | UIBezierPath *path = [UIBezierPath bezierPath]; 655 | [path moveToPoint:CGPointMake(0, view.LC_height / 2)]; 656 | [path addLineToPoint:CGPointMake(self.noteView.LC_width / 2 , view.LC_height / 2)]; 657 | 658 | CAShapeLayer *shapeLayer = [self shapeLayerWithPath:path lineWidth:2 fillColor:[UIColor clearColor] strokeColor:model.color]; 659 | [view.layer addSublayer:shapeLayer]; 660 | [self.firstLayers addObject:shapeLayer]; 661 | } else { 662 | // 方块 663 | UIBezierPath *path = [UIBezierPath bezierPath]; 664 | CGFloat squareH = noteViewRowH - 2 * 2; 665 | [path moveToPoint:CGPointMake(view.LC_width / 2 - squareH, view.LC_height / 2)]; 666 | [path addLineToPoint:CGPointMake(view.LC_width / 2 , view.LC_height / 2)]; 667 | 668 | CAShapeLayer *shapeLayer = [self shapeLayerWithPath:path lineWidth:squareH fillColor:[UIColor clearColor] strokeColor:model.color]; 669 | [view.layer addSublayer:shapeLayer]; 670 | [self.firstLayers addObject:shapeLayer]; 671 | } 672 | } 673 | self.noteView.contentSize = CGSizeMake(0, noteViewRowH * self.dataSource.count); 674 | } 675 | 676 | #pragma mark - setter 677 | 678 | - (void)setXAxisTextMargin:(CGFloat)xAxisTextMargin { 679 | _xAxisTextMargin = xAxisTextMargin; 680 | _xArrowsToText = xAxisTextMargin; 681 | } 682 | 683 | - (void)setChartViewXAxisTextMargin:(CGFloat)xAxisTextMargin { 684 | _xAxisTextMargin = xAxisTextMargin; 685 | _orginXAxisMargin = xAxisTextMargin; 686 | } 687 | 688 | #pragma mark - getter 689 | 690 | - (NSMutableArray *)yAxisLabels { 691 | if (!_yAxisLabels) { 692 | _yAxisLabels = [[NSMutableArray alloc] init]; 693 | } 694 | return _yAxisLabels; 695 | } 696 | 697 | - (NSMutableArray *)xAxisLabels { 698 | if (!_xAxisLabels) { 699 | _xAxisLabels = [[NSMutableArray alloc] init]; 700 | } 701 | return _xAxisLabels; 702 | } 703 | 704 | - (NSMutableArray *)firstLayers { 705 | if (!_firstLayers) { 706 | _firstLayers = [[NSMutableArray alloc] init]; 707 | } 708 | return _firstLayers; 709 | } 710 | 711 | - (NSMutableArray *)scondLayers { 712 | if (!_scondLayers) { 713 | _scondLayers = [[NSMutableArray alloc] init]; 714 | } 715 | return _scondLayers; 716 | } 717 | 718 | - (NSMutableArray *)allSubView { 719 | if (!_allSubView) { 720 | _allSubView = [[NSMutableArray alloc] init]; 721 | } 722 | return _allSubView; 723 | } 724 | 725 | - (UIScrollView *)scrollView { 726 | if (!_scrollView) { 727 | _scrollView = [[UIScrollView alloc] init]; 728 | _scrollView.delegate = self; 729 | _scrollView.showsHorizontalScrollIndicator = NO; 730 | _scrollView.bounces = NO; 731 | // 双击事件 732 | [_scrollView addGestureRecognizer:self.twoTap]; 733 | // 捏合手势 734 | [_scrollView addGestureRecognizer:self.pinch]; 735 | } 736 | return _scrollView; 737 | } 738 | 739 | - (UIScrollView *)noteView { 740 | if (!_noteView) { 741 | _noteView = [[UIScrollView alloc] init]; 742 | _noteView.showsVerticalScrollIndicator = NO; 743 | _noteView.LC_width = 80; 744 | _noteView.LC_height = _topMargin - 2 * 10; 745 | _noteView.LC_right = self.LC_width - 10; 746 | _noteView.LC_y = 10; 747 | } 748 | return _noteView; 749 | } 750 | 751 | - (UITapGestureRecognizer *)twoTap { 752 | if (!_twoTap) { 753 | _twoTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesture:)]; 754 | _twoTap.numberOfTapsRequired = 2; 755 | } 756 | return _twoTap; 757 | } 758 | 759 | - (UIPinchGestureRecognizer *)pinch { 760 | if (!_pinch) { 761 | _pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchGesture:)]; 762 | } 763 | return _pinch ; 764 | } 765 | 766 | - (CGFloat)xArrowsToText { 767 | if (_xArrowsToText < 8) { 768 | return 8; 769 | } 770 | return _xArrowsToText; 771 | } 772 | 773 | @end 774 | -------------------------------------------------------------------------------- /LCChartView/LCChartView/LCChartViewModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // LCChartViewModel.h 3 | // LCProject 4 | // 5 | // Created by liangrongchang on 2017/5/17. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface LCChartViewModel : NSObject 13 | @property (strong, nonatomic) UIColor *color; 14 | /** 项目名称 */ 15 | @property (nonatomic, copy) NSString *project; 16 | @property (strong, nonatomic) NSArray *plots; 17 | @property (strong, nonatomic) NSMutableArray *plotButtons; 18 | 19 | + (instancetype)modelWithColor:(UIColor *)color plots:(NSArray *)plots project:(NSString *)project; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /LCChartView/LCChartView/LCChartViewModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // LCChartViewModel.m 3 | // LCProject 4 | // 5 | // Created by liangrongchang on 2017/5/17. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import "LCChartViewModel.h" 10 | 11 | @implementation LCChartViewModel 12 | 13 | - (instancetype)init { 14 | if (self = [super init]) { 15 | _plotButtons = [NSMutableArray array]; 16 | } 17 | return self; 18 | } 19 | 20 | + (instancetype)modelWithColor:(UIColor *)color plots:(NSArray *)plots project:(NSString *)project { 21 | LCChartViewModel *model = [[self alloc] init]; 22 | model.color = color; 23 | model.project = project; 24 | model.plots = plots; 25 | return model; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /LCChartView/LCChartView/LCMethod.h: -------------------------------------------------------------------------------- 1 | // 2 | // LCMethod.h 3 | // LCChartView 4 | // 5 | // Created by liangrongchang on 2017/8/3. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface LCMethod : NSObject 13 | 14 | + (CGSize)sizeWithText:(NSString *)text fontSize:(CGFloat)fontSize; 15 | 16 | + (UIImage *)imageFromColor:(UIColor *)color rect:(CGRect)rect; 17 | 18 | + (CAShapeLayer *)shapeLayerWithPath:(UIBezierPath *)path lineWidth:(CGFloat)lineWidth fillColor:(UIColor *)fillColor strokeColor:(UIColor *)strokeColor; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /LCChartView/LCChartView/LCMethod.m: -------------------------------------------------------------------------------- 1 | // 2 | // LCMethod.m 3 | // LCChartView 4 | // 5 | // Created by liangrongchang on 2017/8/3. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import "LCMethod.h" 10 | 11 | @implementation LCMethod 12 | 13 | + (CGSize)sizeWithText:(NSString *)text fontSize:(CGFloat)fontSize { 14 | NSDictionary *attr = @{NSFontAttributeName : [UIFont systemFontOfSize:fontSize]}; 15 | return [text sizeWithAttributes:attr]; 16 | } 17 | 18 | + (UIImage *)imageFromColor:(UIColor *)color rect:(CGRect)rect{ 19 | UIGraphicsBeginImageContext(rect.size); 20 | CGContextRef context = UIGraphicsGetCurrentContext(); 21 | CGContextSetFillColorWithColor(context, [color CGColor]); 22 | CGContextFillRect(context, rect); 23 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 24 | UIGraphicsEndImageContext(); 25 | return image; 26 | } 27 | 28 | + (CAShapeLayer *)shapeLayerWithPath:(UIBezierPath *)path lineWidth:(CGFloat)lineWidth fillColor:(UIColor *)fillColor strokeColor:(UIColor *)strokeColor { 29 | CAShapeLayer *shapeLayer = [CAShapeLayer layer]; 30 | shapeLayer.lineWidth = lineWidth; 31 | shapeLayer.fillColor = fillColor.CGColor; 32 | shapeLayer.strokeColor = strokeColor.CGColor; 33 | shapeLayer.lineCap = kCALineCapButt; 34 | shapeLayer.lineJoin = kCALineJoinBevel; 35 | shapeLayer.path = path.CGPath; 36 | return shapeLayer; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /LCChartView/LCChartView/LCPieView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LCPieView.h 3 | // LCPieView 4 | // 5 | // Created by liangrongchang on 2017/5/16. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LCPieViewModel.h" 11 | 12 | @interface LCPieView : UIView 13 | 14 | @property (nonatomic, assign) CGFloat arcWidth; 15 | @property (nonatomic, assign) CGFloat outerArcRadius; 16 | @property (nonatomic, assign) NSTimeInterval duration; 17 | @property (nonatomic, assign) BOOL showText; 18 | @property (nonatomic, assign) BOOL animtion; 19 | @property (nonatomic, assign) BOOL showPercent; 20 | @property (nonatomic, assign) BOOL isCanClick; 21 | 22 | /** 注释文字属性 */ 23 | @property (nonatomic, copy) void (^noteTextBlock)(UILabel *label); 24 | /** 点击pieView的回调 */ 25 | @property (nonatomic, copy) void (^clickPieViewBlock)(LCPieViewModel *model, NSInteger index); 26 | 27 | + (instancetype)pieView; 28 | 29 | - (void)showPieViewWithDataSource:(NSArray *)dataSource; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /LCChartView/LCChartView/LCPieView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LCPieView.m 3 | // LCPieView 4 | // 5 | // Created by liangrongchang on 2017/5/16. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import "LCPieView.h" 10 | #import "UIView+LCLayout.h" 11 | 12 | @interface LCPieView () 13 | 14 | @property (nonatomic, strong) NSArray *endPercents; 15 | @property (nonatomic, strong) NSMutableArray *layers; 16 | @property (nonatomic, strong) CAShapeLayer *touchLayer; 17 | @property (nonatomic, strong) NSMutableArray *textLabels; 18 | @property (strong, nonatomic) UIScrollView *noteView; 19 | @property (nonatomic, strong) NSArray *dataSource; 20 | 21 | @end 22 | 23 | @implementation LCPieView 24 | 25 | + (instancetype)pieView { 26 | LCPieView *pieView = [[LCPieView alloc] init]; 27 | return pieView; 28 | } 29 | 30 | - (instancetype)initWithFrame:(CGRect)frame { 31 | if (self = [super initWithFrame:frame]) { 32 | [self initData]; 33 | } 34 | return self; 35 | } 36 | 37 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 38 | if (self = [super initWithCoder:aDecoder]) { 39 | [self initData]; 40 | } 41 | return self; 42 | } 43 | 44 | - (void)initData { 45 | _arcWidth = 50; 46 | _outerArcRadius = 100; 47 | _duration = 0.8; 48 | _showText = _animtion = _showPercent = _isCanClick = YES; 49 | } 50 | 51 | - (void)showPieViewWithDataSource:(NSArray *)dataSource { 52 | self.dataSource = dataSource; 53 | [self showPieView]; 54 | } 55 | 56 | - (void)showPieView { 57 | if (!self.dataSource.count) { 58 | return; 59 | } 60 | [self resetData]; 61 | [self handleData]; 62 | CGFloat radius = self.outerArcRadius - self.arcWidth / 2; 63 | for (int i = 0; i < self.dataSource.count; i ++) { 64 | LCPieViewModel *model = self.dataSource[i]; 65 | CGFloat startP = i == 0 ? 0 : self.endPercents[i - 1].doubleValue; 66 | CGFloat endP = self.endPercents[i].doubleValue; 67 | 68 | CAShapeLayer *layer = [self layerWithRadius:radius borderWidth:self.arcWidth fillColor:[UIColor clearColor] strokeColor:model.color startPercentage:startP endPercentage:endP]; 69 | 70 | [self.layer addSublayer:layer]; 71 | [self.layers addObject:layer]; 72 | } 73 | [self addAnimation:_animtion]; 74 | if (_showText) { 75 | [self addText]; 76 | } 77 | } 78 | 79 | #pragma mark - response method 80 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 81 | if (!self.isCanClick) { 82 | return; 83 | } 84 | for (UITouch *touch in touches) { 85 | CGPoint touchP = [touch locationInView:self];CGPoint centerP = CGPointMake(self.LC_width / 2, self.LC_height / 2 ); 86 | CGFloat distanceFromCenter = sqrtf(powf((touchP.y - centerP.y),2) + powf((touchP.x - centerP.x),2)); 87 | if (distanceFromCenter > self.outerArcRadius || distanceFromCenter < self.outerArcRadius - self.arcWidth) { 88 | return; 89 | } 90 | CGFloat angePercent = [self anglePercenWithCenter:centerP fromPoint:touchP]; 91 | __block NSUInteger index = 0; 92 | [self.endPercents enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 93 | if (obj.doubleValue > angePercent ) { 94 | index = idx; 95 | *stop = YES; 96 | } 97 | }]; 98 | if (self.clickPieViewBlock) { 99 | self.clickPieViewBlock(self.dataSource[index], index); 100 | } 101 | 102 | if (self.touchLayer) { 103 | [self.touchLayer removeFromSuperlayer]; 104 | self.touchLayer = nil; 105 | } 106 | CGFloat startA = index == 0 ? 0 : self.endPercents[index - 1].doubleValue; 107 | CGFloat endA = self.endPercents[index].doubleValue; 108 | self.touchLayer = [self layerWithRadius:self.outerArcRadius + 8 borderWidth:16 fillColor:[UIColor clearColor] strokeColor:[self.dataSource[index].color colorWithAlphaComponent:0.5] startPercentage:startA endPercentage:endA]; 109 | [self.layer addSublayer:self.touchLayer]; 110 | } 111 | } 112 | 113 | #pragma mark - private method 114 | - (CGFloat)anglePercenWithCenter:(CGPoint)center fromPoint:(CGPoint)reference { 115 | CGFloat angleOfLine = atanf((reference.y - center.y) / (reference.x - center.x)); 116 | CGFloat percentage = (angleOfLine + M_PI/2)/(2 * M_PI); 117 | return (reference.x - center.x) > 0 ? percentage : percentage + .5; 118 | } 119 | 120 | - (void)resetData { 121 | if (self.layers.count) { 122 | [self.layers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; 123 | [self.layers removeAllObjects]; 124 | } 125 | 126 | if (self.touchLayer) { 127 | [self.touchLayer removeFromSuperlayer]; 128 | self.touchLayer = nil; 129 | } 130 | 131 | if (self.textLabels.count) { 132 | [self.textLabels makeObjectsPerformSelector:@selector(removeFromSuperview)]; 133 | [self.textLabels removeAllObjects]; 134 | } 135 | } 136 | 137 | - (void)handleData{ 138 | NSMutableArray *endPercents = [[NSMutableArray alloc] init]; 139 | CGFloat total = [[self.dataSource valueForKeyPath:@"@sum.value"] floatValue]; 140 | for (int i = 0; i < self.dataSource.count; i++) { 141 | LCPieViewModel *model = self.dataSource[i]; 142 | if (i == 0) { 143 | [endPercents addObject:@(model.value / total)]; 144 | } else { 145 | [endPercents addObject:@(model.value / total + endPercents.lastObject.doubleValue)]; 146 | } 147 | } 148 | self.endPercents = endPercents.copy; 149 | } 150 | 151 | - (void)addAnimation:(BOOL)animate { 152 | if (animate) { 153 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 154 | animation.duration = _duration; 155 | animation.fromValue = @0; 156 | animation.toValue = @1; 157 | animation.delegate = self; 158 | animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 159 | for (CAShapeLayer *layer in self.layers) { 160 | [layer addAnimation:animation forKey:@"circleAnimation"]; 161 | } 162 | } else { 163 | [self.textLabels enumerateObjectsUsingBlock:^(UILabel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 164 | obj.alpha = 1.0; 165 | }]; 166 | } 167 | } 168 | 169 | - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 170 | [self.textLabels enumerateObjectsUsingBlock:^(UILabel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 171 | [UIView animateWithDuration:0.3 animations:^{ 172 | obj.alpha = 1.0; 173 | }]; 174 | }]; 175 | } 176 | 177 | - (void)addText { 178 | for (int i = 0; i < self.dataSource.count; i++) { 179 | LCPieViewModel *model = self.dataSource[i]; 180 | UILabel *label = [[UILabel alloc] init]; 181 | label.numberOfLines = 0; 182 | label.textColor = [UIColor whiteColor]; 183 | label.font = [UIFont systemFontOfSize:12]; 184 | label.textAlignment = NSTextAlignmentCenter; 185 | label.backgroundColor = [UIColor clearColor]; 186 | if (_showPercent) { 187 | CGFloat total = [[self.dataSource valueForKeyPath:@"@sum.value"] floatValue]; 188 | label.text = [NSString stringWithFormat:@"%.0f%%\n%@", self.dataSource[i].value / total * 100, model.text]; 189 | } else { 190 | label.text = [NSString stringWithFormat:@"%.0f\n%@", model.value, model.text]; 191 | } 192 | if (self.noteTextBlock) { 193 | self.noteTextBlock(label); 194 | } 195 | label.alpha = 0.0; 196 | [label sizeToFit]; 197 | CGFloat distance = self.outerArcRadius - self.arcWidth / 2; 198 | CGFloat startA = i == 0 ? 0 : self.endPercents[i - 1].doubleValue; 199 | CGFloat endA = self.endPercents[i].doubleValue; 200 | CGFloat angle = (startA + endA) / 2 * M_PI * 2; 201 | CGPoint centerPoint = CGPointMake(self.LC_width / 2 + distance * sin(angle), self.LC_height / 2 - distance * cos(angle)); 202 | label.center = centerPoint; 203 | [self addSubview:label]; 204 | [self.textLabels addObject:label]; 205 | } 206 | } 207 | 208 | - (CAShapeLayer *)layerWithRadius:(CGFloat)radius borderWidth:(CGFloat)borderWidth fillColor:(UIColor *)fillColor strokeColor:(UIColor *)strokeColor startPercentage:(CGFloat)startP endPercentage:(CGFloat)endP{ 209 | CAShapeLayer *layer = [CAShapeLayer layer]; 210 | CGPoint center = CGPointMake(self.LC_width / 2, self.LC_height / 2); 211 | UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:-M_PI_2 endAngle:M_PI_2 * 3 clockwise:YES]; 212 | layer.fillColor = fillColor.CGColor; 213 | layer.strokeColor = strokeColor.CGColor; 214 | layer.strokeStart = startP; 215 | layer.strokeEnd = endP; 216 | layer.lineWidth = borderWidth; 217 | layer.path = path.CGPath; 218 | 219 | return layer; 220 | } 221 | 222 | #pragma mark - getter 223 | - (NSMutableArray *)layers { 224 | if (!_layers) { 225 | _layers = [NSMutableArray array]; 226 | } 227 | return _layers; 228 | } 229 | 230 | - (NSMutableArray *)textLabels { 231 | if (!_textLabels) { 232 | _textLabels = [NSMutableArray array]; 233 | } 234 | return _textLabels; 235 | } 236 | 237 | - (UIScrollView *)noteView { 238 | if (!_noteView) { 239 | _noteView = [[UIScrollView alloc] init]; 240 | _noteView.backgroundColor = [UIColor orangeColor]; 241 | _noteView.showsVerticalScrollIndicator = NO; 242 | _noteView.LC_width = 80; 243 | _noteView.LC_height = self.LC_height; 244 | _noteView.LC_right = self.LC_width - 10; 245 | _noteView.LC_y = 10; 246 | } 247 | return _noteView; 248 | } 249 | 250 | @end 251 | -------------------------------------------------------------------------------- /LCChartView/LCChartView/LCPieViewModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // LCPieViewModel.h 3 | // PieView 4 | // 5 | // Created by liangrongchang on 2017/5/17. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface LCPieViewModel : NSObject 13 | 14 | @property (nonatomic, assign) CGFloat value; 15 | @property (nonatomic, strong) UIColor *color; 16 | @property (nonatomic, copy) NSString *text; 17 | 18 | + (instancetype)modelWithValue:(CGFloat)value color:(UIColor *)color text:(NSString *)text; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /LCChartView/LCChartView/LCPieViewModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // LCPieViewModel.m 3 | // PieView 4 | // 5 | // Created by liangrongchang on 2017/5/17. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import "LCPieViewModel.h" 10 | 11 | @implementation LCPieViewModel 12 | 13 | + (instancetype)modelWithValue:(CGFloat)value color:(UIColor *)color text:(NSString *)text{ 14 | LCPieViewModel *model = [[LCPieViewModel alloc] init]; 15 | model.value = value; 16 | model.color = color; 17 | model.text = text; 18 | return model; 19 | } 20 | 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /LCChartView/LCChartViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LCChartViewController.h 3 | // LCChartView 4 | // 5 | // Created by liangrongchang on 2017/1/13. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LCChartViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /LCChartView/LCChartViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LCChartViewController.m 3 | // LCChartView 4 | // 5 | // Created by liangrongchang on 2017/1/13. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import "LCChartViewController.h" 10 | #import "LCChartView.h" 11 | #import "LCPieView.h" 12 | #import "LCArcView.h" 13 | #import "UIView+LCLayout.h" 14 | 15 | static BOOL isDouble = YES; 16 | #define RandomColor [UIColor colorWithRed:arc4random_uniform(255)/255.0 green:arc4random_uniform(255)/255.0 blue:arc4random_uniform(255)/255.0 alpha:1] 17 | 18 | @interface LCChartViewController () 19 | 20 | @property (nonatomic, strong) UIScrollView *contentView; 21 | @property (strong, nonatomic) LCChartView *chartViewLine; 22 | @property (strong, nonatomic) LCChartView *chartViewBar; 23 | @property (strong, nonatomic) LCPieView *pieView; 24 | @property (nonatomic, strong) LCArcView *arcView; 25 | 26 | @end 27 | 28 | @implementation LCChartViewController 29 | 30 | - (void)viewDidLoad { 31 | [super viewDidLoad]; 32 | self.view.backgroundColor = [UIColor whiteColor]; 33 | self.navigationItem.title = @"LCChartView"; 34 | self.automaticallyAdjustsScrollViewInsets = NO; 35 | [self setupNavBar]; 36 | [self.view addSubview:self.contentView]; 37 | [self.contentView addSubview:self.chartViewLine]; 38 | [self.contentView addSubview:self.chartViewBar]; 39 | [self.contentView addSubview:self.pieView]; 40 | [self.contentView addSubview:self.arcView]; 41 | [self resetData]; 42 | self.contentView.contentSize = CGSizeMake(0, self.arcView.LC_bottom + 50); 43 | } 44 | 45 | #pragma mark - reponse 46 | - (void)exchange { 47 | isDouble = isDouble == YES ? NO : YES; 48 | [self resetData]; 49 | } 50 | 51 | - (void)resetData { 52 | if (isDouble) { 53 | LCChartViewModel *model = [LCChartViewModel modelWithColor:RandomColor plots:[self randomArrayWithCount:12] project:@"1组"]; 54 | LCChartViewModel *model1 = [LCChartViewModel modelWithColor:RandomColor plots:[self randomArrayWithCount:12] project:@"2组"]; 55 | LCChartViewModel *model2 = [LCChartViewModel modelWithColor:RandomColor plots:[self randomArrayWithCount:12] project:@"3组"]; 56 | LCChartViewModel *model3 = [LCChartViewModel modelWithColor:RandomColor plots:[self randomArrayWithCount:12] project:@"4组"]; 57 | LCChartViewModel *model4 = [LCChartViewModel modelWithColor:RandomColor plots:[self randomArrayWithCount:12] project:@"5组"]; 58 | NSArray *dataSource = @[model, model1, model2, model3, model4]; 59 | self.chartViewLine.title = @"折线图"; 60 | [self.chartViewLine showChartViewWithYAxisMaxValue:1000 dataSource:@[model, model1]]; 61 | 62 | self.chartViewBar.title = @"柱状图"; 63 | [self.chartViewBar showChartViewWithYAxisMaxValue:1000 dataSource:dataSource]; 64 | 65 | } else { 66 | LCChartViewModel *model = [LCChartViewModel modelWithColor:RandomColor plots:[self randomArrayWithCount:12] project:@"单组"]; 67 | [self.chartViewLine showChartViewWithYAxisMaxValue:1000 dataSource:@[model]]; 68 | 69 | [self.chartViewBar showChartViewWithYAxisMaxValue:1000 dataSource:@[model]]; 70 | } 71 | 72 | LCPieViewModel *modelP0 = [LCPieViewModel modelWithValue:arc4random_uniform(100) color:[UIColor redColor] text:@"1组"]; 73 | LCPieViewModel *modelP1 = [LCPieViewModel modelWithValue:arc4random_uniform(100) color:[UIColor grayColor] text:@"2组"]; 74 | LCPieViewModel *modelP2 = [LCPieViewModel modelWithValue:arc4random_uniform(100) color:[UIColor blueColor] text:@"3组"]; 75 | [self.pieView showPieViewWithDataSource:@[modelP0, modelP1, modelP2]]; 76 | 77 | [self.arcView showArcViewWithBgStartAngle:0 endBgAngle:M_PI * 2 bgAnimation:NO StartAngle:0 endAngle:M_PI animaion:YES]; 78 | 79 | } 80 | 81 | #pragma mark - private mothed 82 | - (void)setupNavBar { 83 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"刷新数据" style:UIBarButtonItemStylePlain target:self action:@selector(exchange)]; 84 | } 85 | 86 | - (NSArray *)randomArrayWithCount:(NSInteger)dataCounts { 87 | NSMutableArray *array = [[NSMutableArray alloc] init]; 88 | for (int i = 0; i < dataCounts; i++) { 89 | NSString *number = [NSString stringWithFormat:@"%d",arc4random_uniform(1000)]; 90 | [array addObject:number]; 91 | } 92 | return array.copy; 93 | } 94 | 95 | 96 | #pragma mark - getter 97 | - (LCChartView *)chartViewLine { 98 | if (!_chartViewLine) { 99 | _chartViewLine = [LCChartView chartViewWithType:LCChartViewTypeLine]; 100 | _chartViewLine.frame = CGRectMake(0, 64, self.view.LC_width, 250); 101 | } 102 | return _chartViewLine; 103 | } 104 | 105 | - (LCChartView *)chartViewBar { 106 | if (!_chartViewBar) { 107 | _chartViewBar = [[LCChartView alloc] initWithFrame:CGRectMake(0, 350, self.view.LC_width, 250) chartViewType:LCChartViewTypeBar]; 108 | } 109 | return _chartViewBar; 110 | } 111 | 112 | - (LCPieView *)pieView { 113 | if (!_pieView) { 114 | _pieView = [LCPieView pieView]; 115 | _pieView.frame = CGRectMake(0, 650, self.view.LC_width, 200); 116 | } 117 | return _pieView; 118 | } 119 | 120 | - (LCArcView *)arcView { 121 | if (!_arcView) { 122 | _arcView = [[LCArcView alloc] initWithFrame:CGRectMake(0, 900, self.view.LC_width, 150)]; 123 | } 124 | return _arcView; 125 | } 126 | 127 | - (UIScrollView *)contentView { 128 | if (!_contentView) { 129 | _contentView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.LC_width, self.view.LC_height - 49)]; 130 | } 131 | return _contentView; 132 | } 133 | 134 | @end 135 | -------------------------------------------------------------------------------- /LCChartView/UIView+LCLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+LCLayout.h 3 | // LCPlayer 4 | // 5 | // Created by liangrongchang on 2017/3/8. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIView (LCLayout) 12 | 13 | @property (nonatomic, assign) CGFloat LC_left; 14 | @property (nonatomic, assign) CGFloat LC_top; 15 | @property (nonatomic, assign) CGFloat LC_y; 16 | @property (nonatomic, assign) CGFloat LC_x; 17 | @property (nonatomic, assign) CGFloat LC_right; 18 | @property (nonatomic, assign) CGFloat LC_bottom; 19 | @property (nonatomic, assign) CGFloat LC_width; 20 | @property (nonatomic, assign) CGFloat LC_height; 21 | @property (nonatomic, assign) CGFloat LC_centerX; 22 | @property (nonatomic, assign) CGFloat LC_centerY; 23 | @property (nonatomic, assign) CGPoint LC_origin; 24 | @property (nonatomic, assign) CGSize LC_size; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /LCChartView/UIView+LCLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+LCLayout.m 3 | // LCPlayer 4 | // 5 | // Created by liangrongchang on 2017/3/8. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import "UIView+LCLayout.h" 10 | 11 | @implementation UIView (LCLayout) 12 | 13 | - (CGFloat)LC_left { 14 | return self.frame.origin.x; 15 | } 16 | 17 | - (void)setLC_left:(CGFloat)x { 18 | CGRect frame = self.frame; 19 | frame.origin.x = x; 20 | self.frame = frame; 21 | } 22 | 23 | - (CGFloat)LC_top { 24 | return self.frame.origin.y; 25 | } 26 | 27 | - (void)setLC_top:(CGFloat)y { 28 | CGRect frame = self.frame; 29 | frame.origin.y = y; 30 | self.frame = frame; 31 | } 32 | 33 | - (CGFloat)LC_x { 34 | return self.frame.origin.x; 35 | } 36 | 37 | - (void)setLC_x:(CGFloat)x { 38 | CGRect frame = self.frame; 39 | frame.origin.x = x; 40 | self.frame = frame; 41 | } 42 | 43 | - (CGFloat)LC_y { 44 | return self.frame.origin.y; 45 | } 46 | 47 | - (void)setLC_y:(CGFloat)y { 48 | CGRect frame = self.frame; 49 | frame.origin.y = y; 50 | self.frame = frame; 51 | } 52 | 53 | - (CGFloat)LC_right { 54 | return self.frame.origin.x + self.frame.size.width; 55 | } 56 | 57 | - (void)setLC_right:(CGFloat)right { 58 | CGRect frame = self.frame; 59 | frame.origin.x = right - frame.size.width; 60 | self.frame = frame; 61 | } 62 | 63 | - (CGFloat)LC_bottom { 64 | return self.frame.origin.y + self.frame.size.height; 65 | } 66 | 67 | - (void)setLC_bottom:(CGFloat)bottom { 68 | CGRect frame = self.frame; 69 | frame.origin.y = bottom - frame.size.height; 70 | self.frame = frame; 71 | } 72 | 73 | - (CGFloat)LC_width { 74 | return self.frame.size.width; 75 | } 76 | 77 | - (void)setLC_width:(CGFloat)width { 78 | CGRect frame = self.frame; 79 | frame.size.width = width; 80 | self.frame = frame; 81 | } 82 | 83 | - (CGFloat)LC_height { 84 | return self.frame.size.height; 85 | } 86 | 87 | - (void)setLC_height:(CGFloat)height { 88 | CGRect frame = self.frame; 89 | frame.size.height = height; 90 | self.frame = frame; 91 | } 92 | 93 | - (CGFloat)LC_centerX { 94 | return self.center.x; 95 | } 96 | 97 | - (void)setLC_centerX:(CGFloat)centerX { 98 | self.center = CGPointMake(centerX, self.center.y); 99 | } 100 | 101 | - (CGFloat)LC_centerY { 102 | return self.center.y; 103 | } 104 | 105 | - (void)setLC_centerY:(CGFloat)centerY { 106 | self.center = CGPointMake(self.center.x, centerY); 107 | } 108 | 109 | - (CGPoint)LC_origin { 110 | return self.frame.origin; 111 | } 112 | 113 | - (void)setLC_origin:(CGPoint)origin { 114 | CGRect frame = self.frame; 115 | frame.origin = origin; 116 | self.frame = frame; 117 | } 118 | 119 | - (CGSize)LC_size { 120 | return self.frame.size; 121 | } 122 | 123 | - (void)setLC_size:(CGSize)size { 124 | CGRect frame = self.frame; 125 | frame.size = size; 126 | self.frame = frame; 127 | } 128 | 129 | @end 130 | -------------------------------------------------------------------------------- /LCChartView/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LCChartView 4 | // 5 | // Created by liangrongchang on 2017/1/13. 6 | // Copyright © 2017年 Rochang. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LCChartView 2 | 快速展示折线图, 柱状图, 饼状图, 环形图, 其中折线图、柱状图支持tap缩放, pan缩放. 3 | ![arc.gif](https://github.com/Rochang/LCChartView/blob/master/line.gif) 4 | ![arc.gif](https://github.com/Rochang/LCChartView/blob/master/bar.gif) 5 | ![arc.gif](https://github.com/Rochang/LCChartView/blob/master/pie.gif) 6 | ![arc.gif](https://github.com/Rochang/LCChartView/blob/master/arc.gif) 7 | 8 | 9 | # 使用说明 10 | ```obj 11 | // 数据 12 |    LCChartViewModel *model0 = [LCChartViewModel modelWithColor:RandomColor plots:[self randomArrayWithCount:12] project:@"1组"]; 13 | LCChartViewModel *model1 = [LCChartViewModel modelWithColor:RandomColor plots:[self randomArrayWithCount:12] project:@"2组"]; 14 | LCChartViewModel *model2 = [LCChartViewModel modelWithColor:RandomColor plots:[self randomArrayWithCount:12] project:@"3组"]; 15 | LCChartViewModel *model3 = [LCChartViewModel modelWithColor:RandomColor plots:[self randomArrayWithCount:12] project:@"4组"]; 16 | LCChartViewModel *model4 = [LCChartViewModel modelWithColor:RandomColor plots:[self randomArrayWithCount:12] project:@"5组"]; 17 | 18 | // 折线图 19 | LCChartView *chartViewLine = [LCChartView chartViewWithType:LCChartViewTypeLine]; 20 | chartViewLine.frame = CGRectMake(0, 64, self.view.LC_width, 250); 21 |  [self.chartViewLine showChartViewWithYAxisMaxValue:1000 dataSource:@[model0, model1]]; 22 | 23 | // 柱状图 24 | LCChartView *chartViewLine = [LCChartView chartViewWithType:LCChartViewTypeBar]; 25 | chartViewLine.frame = CGRectMake(0, 64, self.view.LC_width, 250); 26 |  [self.chartViewLine showChartViewWithYAxisMaxValue:1000 dataSource:@[model, model1, model2, model3, model4]]; 27 | 28 | // 饼状图 29 | LCPieViewModel *modelP0 = [LCPieViewModel modelWithValue:arc4random_uniform(100) color:[UIColor redColor] text:@"1组"]; 30 | LCPieViewModel *modelP1 = [LCPieViewModel modelWithValue:arc4random_uniform(100) color:[UIColor grayColor] text:@"2组"]; 31 | LCPieViewModel *modelP2 = [LCPieViewModel modelWithValue:arc4random_uniform(100) color:[UIColor blueColor] text:@"3组"]; 32 | 33 | LCPieView *pieView = [LCPieView pieView]; 34 | pieView.frame = CGRectMake(0, 650, self.view.LC_width, 200); 35 | [pieView showPieViewWithDataSource:@[modelP0, modelP1, modelP2]]; 36 |   37 | // 环形图 38 | LCArcView *arcView = [[LCArcView alloc] initWithFrame:CGRectMake(0, 900, self.view.LC_width, 150)]; 39 | [arcView showArcViewWithBgStartAngle:0 endBgAngle:M_PI * 2 bgAnimation:NO StartAngle:0 endAngle:M_PI animaion:YES]; 40 | 41 | /* 每种图形样式都支持颜色,文字的自定义设置,修复对应的属性即可 */ 42 | ``` 43 | -------------------------------------------------------------------------------- /arc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rochang/LCChartView/2ab692bbc0a59441755fd9f4db947243e61e9318/arc.gif -------------------------------------------------------------------------------- /bar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rochang/LCChartView/2ab692bbc0a59441755fd9f4db947243e61e9318/bar.gif -------------------------------------------------------------------------------- /line.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rochang/LCChartView/2ab692bbc0a59441755fd9f4db947243e61e9318/line.gif -------------------------------------------------------------------------------- /pie.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rochang/LCChartView/2ab692bbc0a59441755fd9f4db947243e61e9318/pie.gif --------------------------------------------------------------------------------