├── .gitignore ├── AKExcelDemo ├── AKExcelDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── wushali.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── wushali.xcuserdatad │ │ └── xcschemes │ │ ├── AKExcelDemo.xcscheme │ │ └── xcschememanagement.plist └── AKExcelDemo │ ├── 9.gif │ ├── AKExcelView │ ├── AKExcelCollectionViewCell.swift │ ├── AKExcelDateManager.swift │ └── AKExcelView.swift │ ├── AppDelegate.swift │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── Model.swift │ ├── ViewController.swift │ └── demo.gif ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4BBE51A61FE3ABAC000EDA57 /* 9.gif in Resources */ = {isa = PBXBuildFile; fileRef = 4BBE51A51FE3ABAC000EDA57 /* 9.gif */; }; 11 | 4BDDB58B1EDE71EB00B746C4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDB58A1EDE71EB00B746C4 /* AppDelegate.swift */; }; 12 | 4BDDB58D1EDE71EB00B746C4 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDB58C1EDE71EB00B746C4 /* ViewController.swift */; }; 13 | 4BDDB5901EDE71EB00B746C4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4BDDB58E1EDE71EB00B746C4 /* Main.storyboard */; }; 14 | 4BDDB5921EDE71EB00B746C4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4BDDB5911EDE71EB00B746C4 /* Assets.xcassets */; }; 15 | 4BDDB5951EDE71EB00B746C4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4BDDB5931EDE71EB00B746C4 /* LaunchScreen.storyboard */; }; 16 | 4BDDB59D1EDE720400B746C4 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDB59C1EDE720400B746C4 /* Model.swift */; }; 17 | 4BDDB5A21EDE725F00B746C4 /* AKExcelCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDB59F1EDE725F00B746C4 /* AKExcelCollectionViewCell.swift */; }; 18 | 4BDDB5A31EDE725F00B746C4 /* AKExcelDateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDB5A01EDE725F00B746C4 /* AKExcelDateManager.swift */; }; 19 | 4BDDB5A41EDE725F00B746C4 /* AKExcelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDB5A11EDE725F00B746C4 /* AKExcelView.swift */; }; 20 | 4BDDB5A61EDE9FB100B746C4 /* demo.gif in Resources */ = {isa = PBXBuildFile; fileRef = 4BDDB5A51EDE9FB100B746C4 /* demo.gif */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 4BBE51A51FE3ABAC000EDA57 /* 9.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = 9.gif; sourceTree = ""; }; 25 | 4BDDB5871EDE71EB00B746C4 /* AKExcelDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AKExcelDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 26 | 4BDDB58A1EDE71EB00B746C4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 27 | 4BDDB58C1EDE71EB00B746C4 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 28 | 4BDDB58F1EDE71EB00B746C4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 29 | 4BDDB5911EDE71EB00B746C4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 30 | 4BDDB5941EDE71EB00B746C4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 31 | 4BDDB5961EDE71EB00B746C4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 32 | 4BDDB59C1EDE720400B746C4 /* Model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = ""; }; 33 | 4BDDB59F1EDE725F00B746C4 /* AKExcelCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKExcelCollectionViewCell.swift; sourceTree = ""; }; 34 | 4BDDB5A01EDE725F00B746C4 /* AKExcelDateManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKExcelDateManager.swift; sourceTree = ""; }; 35 | 4BDDB5A11EDE725F00B746C4 /* AKExcelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AKExcelView.swift; sourceTree = ""; }; 36 | 4BDDB5A51EDE9FB100B746C4 /* demo.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = demo.gif; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 4BDDB5841EDE71EB00B746C4 /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | 4BDDB57E1EDE71EB00B746C4 = { 51 | isa = PBXGroup; 52 | children = ( 53 | 4BDDB5891EDE71EB00B746C4 /* AKExcelDemo */, 54 | 4BDDB5881EDE71EB00B746C4 /* Products */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | 4BDDB5881EDE71EB00B746C4 /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 4BDDB5871EDE71EB00B746C4 /* AKExcelDemo.app */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | 4BDDB5891EDE71EB00B746C4 /* AKExcelDemo */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 4BDDB59E1EDE725300B746C4 /* AKExcelView */, 70 | 4BDDB58A1EDE71EB00B746C4 /* AppDelegate.swift */, 71 | 4BDDB58C1EDE71EB00B746C4 /* ViewController.swift */, 72 | 4BDDB59C1EDE720400B746C4 /* Model.swift */, 73 | 4BDDB58E1EDE71EB00B746C4 /* Main.storyboard */, 74 | 4BDDB5911EDE71EB00B746C4 /* Assets.xcassets */, 75 | 4BDDB5931EDE71EB00B746C4 /* LaunchScreen.storyboard */, 76 | 4BDDB5A51EDE9FB100B746C4 /* demo.gif */, 77 | 4BDDB5961EDE71EB00B746C4 /* Info.plist */, 78 | 4BBE51A51FE3ABAC000EDA57 /* 9.gif */, 79 | ); 80 | path = AKExcelDemo; 81 | sourceTree = ""; 82 | }; 83 | 4BDDB59E1EDE725300B746C4 /* AKExcelView */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 4BDDB59F1EDE725F00B746C4 /* AKExcelCollectionViewCell.swift */, 87 | 4BDDB5A01EDE725F00B746C4 /* AKExcelDateManager.swift */, 88 | 4BDDB5A11EDE725F00B746C4 /* AKExcelView.swift */, 89 | ); 90 | path = AKExcelView; 91 | sourceTree = ""; 92 | }; 93 | /* End PBXGroup section */ 94 | 95 | /* Begin PBXNativeTarget section */ 96 | 4BDDB5861EDE71EB00B746C4 /* AKExcelDemo */ = { 97 | isa = PBXNativeTarget; 98 | buildConfigurationList = 4BDDB5991EDE71EB00B746C4 /* Build configuration list for PBXNativeTarget "AKExcelDemo" */; 99 | buildPhases = ( 100 | 4BDDB5831EDE71EB00B746C4 /* Sources */, 101 | 4BDDB5841EDE71EB00B746C4 /* Frameworks */, 102 | 4BDDB5851EDE71EB00B746C4 /* Resources */, 103 | ); 104 | buildRules = ( 105 | ); 106 | dependencies = ( 107 | ); 108 | name = AKExcelDemo; 109 | productName = AKExcelDemo; 110 | productReference = 4BDDB5871EDE71EB00B746C4 /* AKExcelDemo.app */; 111 | productType = "com.apple.product-type.application"; 112 | }; 113 | /* End PBXNativeTarget section */ 114 | 115 | /* Begin PBXProject section */ 116 | 4BDDB57F1EDE71EB00B746C4 /* Project object */ = { 117 | isa = PBXProject; 118 | attributes = { 119 | LastSwiftUpdateCheck = 0830; 120 | LastUpgradeCheck = 0910; 121 | ORGANIZATIONNAME = alasku; 122 | TargetAttributes = { 123 | 4BDDB5861EDE71EB00B746C4 = { 124 | CreatedOnToolsVersion = 8.3.2; 125 | DevelopmentTeam = 4BJ85S653K; 126 | LastSwiftMigration = 0910; 127 | ProvisioningStyle = Automatic; 128 | }; 129 | }; 130 | }; 131 | buildConfigurationList = 4BDDB5821EDE71EB00B746C4 /* Build configuration list for PBXProject "AKExcelDemo" */; 132 | compatibilityVersion = "Xcode 3.2"; 133 | developmentRegion = English; 134 | hasScannedForEncodings = 0; 135 | knownRegions = ( 136 | en, 137 | Base, 138 | ); 139 | mainGroup = 4BDDB57E1EDE71EB00B746C4; 140 | productRefGroup = 4BDDB5881EDE71EB00B746C4 /* Products */; 141 | projectDirPath = ""; 142 | projectRoot = ""; 143 | targets = ( 144 | 4BDDB5861EDE71EB00B746C4 /* AKExcelDemo */, 145 | ); 146 | }; 147 | /* End PBXProject section */ 148 | 149 | /* Begin PBXResourcesBuildPhase section */ 150 | 4BDDB5851EDE71EB00B746C4 /* Resources */ = { 151 | isa = PBXResourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | 4BDDB5A61EDE9FB100B746C4 /* demo.gif in Resources */, 155 | 4BDDB5951EDE71EB00B746C4 /* LaunchScreen.storyboard in Resources */, 156 | 4BBE51A61FE3ABAC000EDA57 /* 9.gif in Resources */, 157 | 4BDDB5921EDE71EB00B746C4 /* Assets.xcassets in Resources */, 158 | 4BDDB5901EDE71EB00B746C4 /* Main.storyboard in Resources */, 159 | ); 160 | runOnlyForDeploymentPostprocessing = 0; 161 | }; 162 | /* End PBXResourcesBuildPhase section */ 163 | 164 | /* Begin PBXSourcesBuildPhase section */ 165 | 4BDDB5831EDE71EB00B746C4 /* Sources */ = { 166 | isa = PBXSourcesBuildPhase; 167 | buildActionMask = 2147483647; 168 | files = ( 169 | 4BDDB5A41EDE725F00B746C4 /* AKExcelView.swift in Sources */, 170 | 4BDDB59D1EDE720400B746C4 /* Model.swift in Sources */, 171 | 4BDDB5A31EDE725F00B746C4 /* AKExcelDateManager.swift in Sources */, 172 | 4BDDB58D1EDE71EB00B746C4 /* ViewController.swift in Sources */, 173 | 4BDDB5A21EDE725F00B746C4 /* AKExcelCollectionViewCell.swift in Sources */, 174 | 4BDDB58B1EDE71EB00B746C4 /* AppDelegate.swift in Sources */, 175 | ); 176 | runOnlyForDeploymentPostprocessing = 0; 177 | }; 178 | /* End PBXSourcesBuildPhase section */ 179 | 180 | /* Begin PBXVariantGroup section */ 181 | 4BDDB58E1EDE71EB00B746C4 /* Main.storyboard */ = { 182 | isa = PBXVariantGroup; 183 | children = ( 184 | 4BDDB58F1EDE71EB00B746C4 /* Base */, 185 | ); 186 | name = Main.storyboard; 187 | sourceTree = ""; 188 | }; 189 | 4BDDB5931EDE71EB00B746C4 /* LaunchScreen.storyboard */ = { 190 | isa = PBXVariantGroup; 191 | children = ( 192 | 4BDDB5941EDE71EB00B746C4 /* Base */, 193 | ); 194 | name = LaunchScreen.storyboard; 195 | sourceTree = ""; 196 | }; 197 | /* End PBXVariantGroup section */ 198 | 199 | /* Begin XCBuildConfiguration section */ 200 | 4BDDB5971EDE71EB00B746C4 /* Debug */ = { 201 | isa = XCBuildConfiguration; 202 | buildSettings = { 203 | ALWAYS_SEARCH_USER_PATHS = NO; 204 | CLANG_ANALYZER_NONNULL = YES; 205 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 206 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 207 | CLANG_CXX_LIBRARY = "libc++"; 208 | CLANG_ENABLE_MODULES = YES; 209 | CLANG_ENABLE_OBJC_ARC = YES; 210 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 211 | CLANG_WARN_BOOL_CONVERSION = YES; 212 | CLANG_WARN_COMMA = YES; 213 | CLANG_WARN_CONSTANT_CONVERSION = YES; 214 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 215 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 216 | CLANG_WARN_EMPTY_BODY = YES; 217 | CLANG_WARN_ENUM_CONVERSION = YES; 218 | CLANG_WARN_INFINITE_RECURSION = YES; 219 | CLANG_WARN_INT_CONVERSION = YES; 220 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 221 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 222 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 223 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 224 | CLANG_WARN_STRICT_PROTOTYPES = YES; 225 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 226 | CLANG_WARN_UNREACHABLE_CODE = YES; 227 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 228 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 229 | COPY_PHASE_STRIP = NO; 230 | DEBUG_INFORMATION_FORMAT = dwarf; 231 | ENABLE_STRICT_OBJC_MSGSEND = YES; 232 | ENABLE_TESTABILITY = YES; 233 | GCC_C_LANGUAGE_STANDARD = gnu99; 234 | GCC_DYNAMIC_NO_PIC = NO; 235 | GCC_NO_COMMON_BLOCKS = YES; 236 | GCC_OPTIMIZATION_LEVEL = 0; 237 | GCC_PREPROCESSOR_DEFINITIONS = ( 238 | "DEBUG=1", 239 | "$(inherited)", 240 | ); 241 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 242 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 243 | GCC_WARN_UNDECLARED_SELECTOR = YES; 244 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 245 | GCC_WARN_UNUSED_FUNCTION = YES; 246 | GCC_WARN_UNUSED_VARIABLE = YES; 247 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 248 | MTL_ENABLE_DEBUG_INFO = YES; 249 | ONLY_ACTIVE_ARCH = YES; 250 | SDKROOT = iphoneos; 251 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 252 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 253 | TARGETED_DEVICE_FAMILY = "1,2"; 254 | }; 255 | name = Debug; 256 | }; 257 | 4BDDB5981EDE71EB00B746C4 /* Release */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | ALWAYS_SEARCH_USER_PATHS = NO; 261 | CLANG_ANALYZER_NONNULL = YES; 262 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 263 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 264 | CLANG_CXX_LIBRARY = "libc++"; 265 | CLANG_ENABLE_MODULES = YES; 266 | CLANG_ENABLE_OBJC_ARC = YES; 267 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 268 | CLANG_WARN_BOOL_CONVERSION = YES; 269 | CLANG_WARN_COMMA = YES; 270 | CLANG_WARN_CONSTANT_CONVERSION = YES; 271 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 272 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 273 | CLANG_WARN_EMPTY_BODY = YES; 274 | CLANG_WARN_ENUM_CONVERSION = YES; 275 | CLANG_WARN_INFINITE_RECURSION = YES; 276 | CLANG_WARN_INT_CONVERSION = YES; 277 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 278 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 279 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 280 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 281 | CLANG_WARN_STRICT_PROTOTYPES = YES; 282 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 283 | CLANG_WARN_UNREACHABLE_CODE = YES; 284 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 285 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 286 | COPY_PHASE_STRIP = NO; 287 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 288 | ENABLE_NS_ASSERTIONS = NO; 289 | ENABLE_STRICT_OBJC_MSGSEND = YES; 290 | GCC_C_LANGUAGE_STANDARD = gnu99; 291 | GCC_NO_COMMON_BLOCKS = YES; 292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 294 | GCC_WARN_UNDECLARED_SELECTOR = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 296 | GCC_WARN_UNUSED_FUNCTION = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 299 | MTL_ENABLE_DEBUG_INFO = NO; 300 | SDKROOT = iphoneos; 301 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 302 | TARGETED_DEVICE_FAMILY = "1,2"; 303 | VALIDATE_PRODUCT = YES; 304 | }; 305 | name = Release; 306 | }; 307 | 4BDDB59A1EDE71EB00B746C4 /* Debug */ = { 308 | isa = XCBuildConfiguration; 309 | buildSettings = { 310 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 311 | DEVELOPMENT_TEAM = 4BJ85S653K; 312 | INFOPLIST_FILE = AKExcelDemo/Info.plist; 313 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 314 | PRODUCT_BUNDLE_IDENTIFIER = com.alasku.AKExcelDemo; 315 | PRODUCT_NAME = "$(TARGET_NAME)"; 316 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 317 | SWIFT_VERSION = 4.0; 318 | }; 319 | name = Debug; 320 | }; 321 | 4BDDB59B1EDE71EB00B746C4 /* Release */ = { 322 | isa = XCBuildConfiguration; 323 | buildSettings = { 324 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 325 | DEVELOPMENT_TEAM = 4BJ85S653K; 326 | INFOPLIST_FILE = AKExcelDemo/Info.plist; 327 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 328 | PRODUCT_BUNDLE_IDENTIFIER = com.alasku.AKExcelDemo; 329 | PRODUCT_NAME = "$(TARGET_NAME)"; 330 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 331 | SWIFT_VERSION = 4.0; 332 | }; 333 | name = Release; 334 | }; 335 | /* End XCBuildConfiguration section */ 336 | 337 | /* Begin XCConfigurationList section */ 338 | 4BDDB5821EDE71EB00B746C4 /* Build configuration list for PBXProject "AKExcelDemo" */ = { 339 | isa = XCConfigurationList; 340 | buildConfigurations = ( 341 | 4BDDB5971EDE71EB00B746C4 /* Debug */, 342 | 4BDDB5981EDE71EB00B746C4 /* Release */, 343 | ); 344 | defaultConfigurationIsVisible = 0; 345 | defaultConfigurationName = Release; 346 | }; 347 | 4BDDB5991EDE71EB00B746C4 /* Build configuration list for PBXNativeTarget "AKExcelDemo" */ = { 348 | isa = XCConfigurationList; 349 | buildConfigurations = ( 350 | 4BDDB59A1EDE71EB00B746C4 /* Debug */, 351 | 4BDDB59B1EDE71EB00B746C4 /* Release */, 352 | ); 353 | defaultConfigurationIsVisible = 0; 354 | defaultConfigurationName = Release; 355 | }; 356 | /* End XCConfigurationList section */ 357 | }; 358 | rootObject = 4BDDB57F1EDE71EB00B746C4 /* Project object */; 359 | } 360 | -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo.xcodeproj/project.xcworkspace/xcuserdata/wushali.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlasKuNull/SwiftyExcelView/e22a8514b455e1423b5a2afa7ebb5f515f54714b/AKExcelDemo/AKExcelDemo.xcodeproj/project.xcworkspace/xcuserdata/wushali.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo.xcodeproj/xcuserdata/wushali.xcuserdatad/xcschemes/AKExcelDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo.xcodeproj/xcuserdata/wushali.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AKExcelDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 4BDDB5861EDE71EB00B746C4 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo/9.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlasKuNull/SwiftyExcelView/e22a8514b455e1423b5a2afa7ebb5f515f54714b/AKExcelDemo/AKExcelDemo/9.gif -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo/AKExcelView/AKExcelCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AKExcelCollectionViewCell.swift 3 | // YunYingSwift 4 | // 5 | // Created by AlasKu on 17/2/10. 6 | // Copyright © 2017年 innostic. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AKExcelCollectionViewCell: UICollectionViewCell { 12 | 13 | var horizontalMargin : CGFloat?{ 14 | didSet{ 15 | setupFrame() 16 | } 17 | } 18 | var customView : UIView? { 19 | didSet{ 20 | 21 | if customView != nil { 22 | customView?.frame = bounds 23 | contentView.insertSubview(customView!, at: 0) 24 | textLabel.text = "" 25 | } 26 | } 27 | } 28 | 29 | lazy var textLabel : UILabel = { 30 | 31 | let label = UILabel.init() 32 | label.numberOfLines = 0 33 | label.textAlignment = .center 34 | label.backgroundColor = UIColor.clear 35 | return label 36 | }() 37 | 38 | private lazy var separatorLayer : CAShapeLayer = { 39 | 40 | let lay = CAShapeLayer.init() 41 | lay.strokeColor = UIColor.lightGray.cgColor 42 | lay.lineWidth = 0.5 43 | return lay 44 | }() 45 | 46 | override init(frame: CGRect) { 47 | super.init(frame: frame) 48 | setup() 49 | } 50 | 51 | required init?(coder aDecoder: NSCoder) { 52 | fatalError("init(coder:) has not been implemented") 53 | } 54 | 55 | func setup() { 56 | addSubview(textLabel) 57 | contentView.layer.addSublayer(separatorLayer) 58 | } 59 | 60 | override func layoutSubviews() { 61 | super.layoutSubviews() 62 | setupFrame() 63 | } 64 | 65 | func setupFrame() { 66 | 67 | let newFrame = CGRect.init(x: horizontalMargin!, y: 0, width: bounds.size.width - 2*horizontalMargin!, height: bounds.size.height) 68 | 69 | textLabel.frame = newFrame 70 | 71 | let path = UIBezierPath.init() 72 | path.move(to: CGPoint.init(x: bounds.size.width - 0.5, y: 0)) 73 | path.addLine(to: CGPoint.init(x: bounds.size.width - 0.5, y: bounds.size.height)) 74 | 75 | separatorLayer.path = path.cgPath 76 | } 77 | override func prepareForReuse() { 78 | super.prepareForReuse() 79 | customView?.removeFromSuperview() 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo/AKExcelView/AKExcelDateManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AKExcelDateManager.swift 3 | // YunYingSwift 4 | // 5 | // Created by AlasKu on 17/2/10. 6 | // Copyright © 2017年 innostic. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AKExcelDataManager: NSObject { 12 | 13 | //MARK: - Properties 14 | 15 | /// excelView 16 | var excelView : AKExcelView? 17 | /// AllExcel Data 18 | var dataArray : [[String]]? 19 | /// freezeCollection Width 20 | var freezeWidth : CGFloat = 0 21 | /// freezeColectionView cells Size 22 | var freezeItemSize = [String]() 23 | /// slideCollectionView Cells Size 24 | var slideItemSize = [String]() 25 | /// slideCollectionView Cells Size 26 | var slideWidth : CGFloat = 0 27 | /// headFreezeCollectionView Data 28 | var headFreezeData = [String]() 29 | /// headSlideCollectionView Data 30 | var headSlideData = [String]() 31 | /// contentFreezeCollectionView Data 32 | var contentFreezeData = [[String]]() 33 | /// contentSlideCollectionView Data 34 | var contentSlideData = [[String]]() 35 | /// slideItemOffSetX 36 | var slideItemOffSetX = [CGFloat]() 37 | 38 | //MARK: - Private Method 39 | private func loadData() { 40 | 41 | var arrM = [[String]]() 42 | 43 | if ((excelView?.headerTitles) != nil) { 44 | arrM.append((excelView?.headerTitles)!) 45 | } 46 | if ((excelView?.contentData) != nil) { 47 | for model in (excelView?.contentData)! { 48 | 49 | arrM.append(model.valuesFor(excelView?.properties)) 50 | } 51 | } 52 | dataArray = arrM 53 | } 54 | 55 | private func configData() { 56 | var freezeData = [[String]]() 57 | var slideData = [[String]]() 58 | let count = dataArray!.count 59 | 60 | for i in 0 ..< count { 61 | var freezeArray = [String]() 62 | var slideArray = [String]() 63 | 64 | let arr : [String] = dataArray![i] 65 | let cou = arr.count 66 | 67 | for j in 0 ..< cou { 68 | let value = arr[j]; 69 | 70 | if (j < (excelView?.leftFreezeColumn)!) { 71 | freezeArray.append(value) 72 | } else { 73 | slideArray.append(value) 74 | } 75 | } 76 | freezeData.append(freezeArray) 77 | slideData.append(slideArray) 78 | } 79 | 80 | if ((excelView?.headerTitles) != nil) { 81 | headFreezeData = (dataArray?.first)! 82 | headSlideData = (dataArray?.first)! 83 | 84 | let fArray = Array(freezeData[1..= colW { 125 | colW = targetWidth; 126 | } 127 | 128 | colW = max((excelView?.itemMinWidth)!, min((excelView?.itemMaxWidth)!, colW)) 129 | } 130 | // 滑动scroll节点 131 | slideItemOffSetX.append(slideWidth) 132 | if (i < (excelView?.leftFreezeColumn)!) { 133 | 134 | fItemSize.append(NSStringFromCGSize(CGSize.init(width: colW, height: (excelView?.itemHeight)! - 1))) 135 | freezeWidth += colW 136 | 137 | } else { 138 | sItemSize.append(NSStringFromCGSize(CGSize.init(width: colW, height: (excelView?.itemHeight)! - 1))) 139 | slideWidth += colW 140 | } 141 | } 142 | freezeItemSize = fItemSize 143 | slideItemSize = sItemSize 144 | } 145 | 146 | //MARK: - Public Method 147 | 148 | func caculateData() { 149 | loadData() 150 | configData() 151 | caculateWidths() 152 | } 153 | } 154 | 155 | //MARK: - String Extension 156 | extension String { 157 | 158 | func getTextSize(font:UIFont,size:CGSize) -> CGSize { 159 | 160 | let dic = NSDictionary(object: font, forKey: NSAttributedStringKey.font as NSCopying) 161 | 162 | let stringSize = self.boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: dic as? [NSAttributedStringKey : Any] , context:nil).size 163 | 164 | return stringSize 165 | } 166 | } 167 | 168 | 169 | extension NSObject 170 | { 171 | func propertyNames() -> [String] { 172 | 173 | return Mirror(reflecting: self).children.flatMap { $0.label } 174 | } 175 | 176 | func valueFor(_ property:String) -> String? { 177 | 178 | var value : String? 179 | for case let (label?, anyValue) in Mirror(reflecting:self).children { 180 | if label.isEqual(property) { 181 | value = anyValue as? String 182 | // return anyValue as? String 183 | } 184 | } 185 | return value 186 | } 187 | 188 | func valuesFor(_ properties:[String]?) -> [String] { 189 | 190 | if let pros = properties { 191 | var arrM = [String]() 192 | for pro in pros { 193 | arrM.append(valueFor(pro)!) 194 | } 195 | return arrM 196 | } 197 | 198 | var values = [String]() 199 | for case let (_?, anyValue) in Mirror(reflecting:self).children { 200 | 201 | values.append(anyValue as? String ?? "") 202 | } 203 | return values 204 | } 205 | } 206 | 207 | -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo/AKExcelView/AKExcelView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExcelViewSwifty.swift 3 | // YunYingSwift 4 | // 5 | // Created by AlasKu on 17/2/10. 6 | // Copyright © 2017年 innostic. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | let AKCollectionViewCellIdentifier = "AKCollectionView_Cell" 12 | let AKCollectionViewHeaderIdentifier = "AKCollectionView_Header" 13 | 14 | @objc protocol AKExcelViewDelegate : NSObjectProtocol { 15 | 16 | @objc optional func excelView(_ excelView: AKExcelView, didSelectItemAt indexPath: IndexPath) 17 | @objc optional func excelView(_ excelView: AKExcelView, viewAt indexPath: IndexPath) -> UIView? 18 | @objc optional func excelView(_ excelView: AKExcelView, attributedStringAt indexPath: IndexPath) -> NSAttributedString? 19 | } 20 | 21 | class AKExcelView: UIView , UICollectionViewDelegate , UICollectionViewDataSource , UIScrollViewDelegate , UICollectionViewDelegateFlowLayout{ 22 | 23 | /// Delegate 24 | weak var delegate : AKExcelViewDelegate? 25 | /// CellTextMargin 26 | var textMargin : CGFloat = 5 27 | /// Cell Max width 28 | var itemMaxWidth : CGFloat = 200 29 | /// cell Min width 30 | var itemMinWidth : CGFloat = 50 31 | /// cell heihth 32 | var itemHeight : CGFloat = 44 33 | /// header Height 34 | var headerHeight : CGFloat = 44 35 | /// header BackgroundColor 36 | var headerBackgroundColor : UIColor = UIColor.lightGray 37 | /// header Text Color 38 | var headerTextColor : UIColor = UIColor.black 39 | /// header Text Font 40 | var headerTextFontSize : UIFont = UIFont.systemFont(ofSize: 15) 41 | /// contenTCell TextColor 42 | var contentTextColor : UIColor = UIColor.black 43 | /// content backgroung Color 44 | var contentBackgroundColor : UIColor = UIColor.white 45 | /// content Text Font 46 | var contentTextFontSize : UIFont = UIFont.systemFont(ofSize: 13) 47 | /// letf freeze column 48 | var leftFreezeColumn : Int = 1 49 | /// header Titles 50 | var headerTitles : Array? 51 | /// content Data 52 | var contentData : Array? 53 | /// set Column widths 54 | var columnWidthSetting : Dictionary? 55 | /// CelledgeInset 56 | var itemInsets : UIEdgeInsets? 57 | /// showsProperties 58 | var properties : Array? 59 | /// autoScrollItem default is false 60 | var autoScrollToNearItem : Bool = false 61 | /// showNoDataView default is false 62 | var showNoDataView : Bool = false { 63 | didSet{ 64 | self.addSubview(alertLabel) 65 | alertLabel.center = self.center 66 | } 67 | } 68 | var noDataDescription : String = " - 暂无数据 - " { 69 | didSet{ 70 | alertLabel.text = noDataDescription 71 | } 72 | } 73 | var alertLabel : UILabel = { 74 | let alertLabel = UILabel() 75 | alertLabel.text = " - 暂无数据 - " 76 | alertLabel.textColor = .lightGray 77 | alertLabel.sizeToFit() 78 | return alertLabel 79 | }() 80 | 81 | fileprivate lazy var horizontalShadow : CAShapeLayer = { 82 | let s = CAShapeLayer() 83 | s.strokeColor = UIColor.lightGray.cgColor; 84 | s.shadowColor = UIColor.black.cgColor; 85 | s.shadowOffset = CGSize.init(width: 2, height: 0) 86 | s.shadowOpacity = 1; 87 | return s 88 | }() 89 | 90 | fileprivate lazy var veritcalShadow : CAShapeLayer = { 91 | let s = CAShapeLayer() 92 | s.strokeColor = UIColor.lightGray.cgColor; 93 | s.shadowColor = UIColor.black.cgColor; 94 | s.shadowOffset = CGSize.init(width: 0, height: 3) 95 | s.shadowOpacity = 1; 96 | return s 97 | }() 98 | 99 | //MARK: - Public Method 100 | override init(frame: CGRect) { 101 | super.init(frame: frame) 102 | setup() 103 | } 104 | 105 | required init?(coder aDecoder: NSCoder) { 106 | fatalError("init(coder:) has not been implemented") 107 | } 108 | 109 | override func layoutSubviews() { 110 | super.layoutSubviews() 111 | setUpFrames() 112 | } 113 | 114 | func reloadData() { 115 | DispatchQueue.global().async { 116 | 117 | self.dataManager.caculateData() 118 | DispatchQueue.main.async { 119 | 120 | self.headFreezeCollectionView.reloadData() 121 | self.headMovebleCollectionView.reloadData() 122 | self.contentFreezeCollectionView.reloadData() 123 | self.contentMoveableCollectionView.reloadData() 124 | 125 | self.setUpFrames() 126 | } 127 | } 128 | } 129 | 130 | func reloadDataCompleteHandler(complete:@escaping () -> Void) { 131 | DispatchQueue.global().async { 132 | 133 | self.dataManager.caculateData() 134 | DispatchQueue.main.async { 135 | 136 | self.headFreezeCollectionView.reloadData() 137 | self.headMovebleCollectionView.reloadData() 138 | self.contentFreezeCollectionView.reloadData() 139 | self.contentMoveableCollectionView.reloadData() 140 | 141 | self.setUpFrames() 142 | complete() 143 | } 144 | } 145 | } 146 | 147 | 148 | func sizeForItem(item: Int) -> CGSize { 149 | 150 | if item < leftFreezeColumn { 151 | return CGSizeFromString(self.dataManager.freezeItemSize[item]); 152 | } else { 153 | return CGSizeFromString(self.dataManager.slideItemSize[item - leftFreezeColumn]); 154 | } 155 | } 156 | 157 | //MARK: - Private Method 158 | private func setup() { 159 | 160 | dataManager.excelView = self 161 | clipsToBounds = true 162 | 163 | 164 | addSubview(headFreezeCollectionView) 165 | addSubview(contentFreezeCollectionView) 166 | addSubview(contentScrollView) 167 | 168 | contentScrollView.addSubview(headMovebleCollectionView) 169 | contentScrollView.addSubview(contentMoveableCollectionView) 170 | 171 | contentMoveableCollectionView.contentInset = UIEdgeInsetsMake(-1, 0, 0, 0) 172 | contentFreezeCollectionView.contentInset = UIEdgeInsetsMake(-1, 0, 0, 0) 173 | 174 | layer.addSublayer(horizontalShadow) 175 | contentScrollView.layer.addSublayer(veritcalShadow) 176 | } 177 | 178 | fileprivate func showHorizontalDivideShadowLayer() { 179 | if horizontalShadow.path == nil { 180 | let path = UIBezierPath() 181 | path.move(to: CGPoint.init(x: 0, y: headerHeight)) 182 | path.addLine(to: CGPoint.init(x: min(self.bounds.size.width, self.dataManager.freezeWidth + self.dataManager.slideWidth), y: headerHeight)) 183 | path.lineWidth = 0.5 184 | horizontalShadow.path = path.cgPath 185 | } 186 | } 187 | 188 | fileprivate func dismissHorizontalDivideShadowLayer() { 189 | horizontalShadow.path = nil 190 | } 191 | 192 | fileprivate func showVerticalDivideShadowLayer() { 193 | if veritcalShadow.path == nil { 194 | let path = UIBezierPath() 195 | let heigh = contentScrollView.contentSize.height + headFreezeCollectionView.contentSize.height 196 | path.move(to: CGPoint.init(x: 0, y:0)) 197 | path.addLine(to: CGPoint.init(x: 0, y: heigh)) 198 | path.lineWidth = 0.5 199 | veritcalShadow.path = path.cgPath 200 | } 201 | } 202 | 203 | fileprivate func dismissVerticalDivideShadowLayer() { 204 | veritcalShadow.path = nil 205 | } 206 | 207 | fileprivate func setUpFrames() { 208 | 209 | let width = bounds.size.width 210 | let height = bounds.size.height 211 | 212 | if headerTitles != nil { 213 | 214 | headFreezeCollectionView.frame = CGRect.init(x: 0, y: 0, width: dataManager.freezeWidth, height: headerHeight) 215 | contentFreezeCollectionView.frame = CGRect.init(x: 0, y: headerHeight, width: dataManager.freezeWidth, height: height - headerHeight) 216 | 217 | contentScrollView.frame = CGRect.init(x: dataManager.freezeWidth, y: 0, width: width - dataManager.freezeWidth, height: height) 218 | contentScrollView.contentSize = CGSize.init(width: dataManager.slideWidth, height: height) 219 | 220 | headMovebleCollectionView.frame = CGRect.init(x: 0, y: 0, width: dataManager.slideWidth, height: headerHeight) 221 | contentMoveableCollectionView.frame = CGRect.init(x: 0, y: headerHeight, width: dataManager.slideWidth, height: height - headerHeight) 222 | 223 | }else{ 224 | 225 | contentFreezeCollectionView.frame = CGRect.init(x: 0, y: 0, width: dataManager.freezeWidth, height: height - headerHeight) 226 | 227 | contentScrollView.frame = CGRect.init(x: dataManager.freezeWidth, y: 0, width: width - dataManager.freezeWidth, height: height) 228 | contentScrollView.contentSize = CGSize.init(width: dataManager.slideWidth, height: height) 229 | 230 | contentMoveableCollectionView.frame = CGRect.init(x: 0, y: 0, width: dataManager.slideWidth, height: height - headerHeight) 231 | } 232 | if showNoDataView { 233 | self.alertLabel.isHidden = self.contentData?.count == 0 ? false : true 234 | } 235 | 236 | } 237 | 238 | //MARK: - 懒加载 239 | fileprivate let dataManager : AKExcelDataManager = AKExcelDataManager() 240 | 241 | fileprivate lazy var headFreezeCollectionView : UICollectionView = { 242 | 243 | return UICollectionView.init(delelate: self, datasource: self) 244 | }() 245 | 246 | fileprivate lazy var headMovebleCollectionView : UICollectionView = { 247 | 248 | return UICollectionView.init(delelate: self, datasource: self) 249 | }() 250 | 251 | fileprivate lazy var contentFreezeCollectionView : UICollectionView = { 252 | 253 | return UICollectionView.init(delelate: self, datasource: self) 254 | }() 255 | 256 | fileprivate lazy var contentMoveableCollectionView : UICollectionView = { 257 | 258 | return UICollectionView.init(delelate: self, datasource: self) 259 | }() 260 | 261 | fileprivate lazy var contentScrollView : UIScrollView = { 262 | 263 | let slideScrollView = UIScrollView() 264 | slideScrollView.bounces = false 265 | slideScrollView.showsHorizontalScrollIndicator = true 266 | slideScrollView.delegate = self 267 | 268 | return slideScrollView 269 | }() 270 | 271 | } 272 | 273 | //MARK: - UICollectionView Delegate & DataSource & collectionPrivate 274 | extension AKExcelView { 275 | 276 | func numberOfSections(in collectionView: UICollectionView) -> Int { 277 | 278 | if collectionView == headMovebleCollectionView || collectionView == headFreezeCollectionView { 279 | return 1 280 | } 281 | return (contentData?.count)! 282 | } 283 | 284 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 285 | 286 | if collectionView == headFreezeCollectionView || collectionView == contentFreezeCollectionView { 287 | return leftFreezeColumn 288 | } else { 289 | if let firstBodyData = self.contentData?.first { 290 | 291 | if let pros = properties { 292 | return pros.count - leftFreezeColumn 293 | } 294 | let slideColumn = (firstBodyData.propertyNames().count) - leftFreezeColumn; 295 | return slideColumn 296 | }else{ 297 | return 0 298 | } 299 | } 300 | } 301 | 302 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 303 | 304 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: AKCollectionViewCellIdentifier, for: indexPath) as! AKExcelCollectionViewCell 305 | cell.horizontalMargin = self.textMargin 306 | self.configCells(collectionView: collectionView, cell: cell, indexPath: indexPath) 307 | 308 | return cell 309 | } 310 | 311 | private func configCells(collectionView:UICollectionView ,cell:AKExcelCollectionViewCell ,indexPath: IndexPath) { 312 | 313 | var targetIndexPath = indexPath 314 | 315 | if collectionView == headFreezeCollectionView { 316 | cell.textLabel.text = headerTitles?[leftFreezeColumn - 1] 317 | cell.backgroundColor = headerBackgroundColor 318 | cell.textLabel.font = headerTextFontSize 319 | } else if collectionView == headMovebleCollectionView { 320 | 321 | if indexPath.item + leftFreezeColumn < (headerTitles?.count)! { 322 | cell.backgroundColor = headerBackgroundColor 323 | cell.textLabel.text = headerTitles?[indexPath.item + leftFreezeColumn] 324 | cell.textLabel.font = headerTextFontSize 325 | targetIndexPath = NSIndexPath.init(item: indexPath.item + leftFreezeColumn, section: indexPath.section) as IndexPath 326 | 327 | } 328 | }else if (collectionView == contentFreezeCollectionView) { 329 | let text = dataManager.contentFreezeData[indexPath.section][indexPath.item]; 330 | cell.textLabel.text = text; 331 | cell.backgroundColor = contentBackgroundColor; 332 | cell.textLabel.textColor = contentTextColor; 333 | cell.textLabel.font = contentTextFontSize; 334 | 335 | targetIndexPath = NSIndexPath.init(item: indexPath.item, section: indexPath.section + 1) as IndexPath 336 | } else { 337 | let text = dataManager.contentSlideData[indexPath.section][indexPath.item]; 338 | cell.textLabel.text = text; 339 | cell.backgroundColor = contentBackgroundColor; 340 | cell.textLabel.textColor = contentTextColor; 341 | cell.textLabel.font = contentTextFontSize; 342 | 343 | targetIndexPath = NSIndexPath.init(item: indexPath.item + leftFreezeColumn, section: indexPath.section + 1) as IndexPath 344 | } 345 | 346 | self.customViewInCell(cell: cell, indexPath: targetIndexPath) 347 | self.attributeStringInCell(cell: cell, indexPath: targetIndexPath) 348 | } 349 | 350 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 351 | 352 | if (contentData?.count == 0) { 353 | return CGSize.zero 354 | } else { 355 | if (collectionView == headFreezeCollectionView || 356 | collectionView == contentFreezeCollectionView) { 357 | 358 | return CGSizeFromString(self.dataManager.freezeItemSize[indexPath.item]); 359 | } else { 360 | 361 | return CGSizeFromString(self.dataManager.slideItemSize[indexPath.item]); 362 | } 363 | } 364 | } 365 | 366 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 367 | 368 | var targetIndexPath = indexPath 369 | if collectionView == headFreezeCollectionView { 370 | 371 | } else if (collectionView == headMovebleCollectionView) { 372 | targetIndexPath = NSIndexPath.init(item: indexPath.item + leftFreezeColumn, section: indexPath.section) as IndexPath 373 | } else if (collectionView == contentFreezeCollectionView) { 374 | targetIndexPath = NSIndexPath.init(item: indexPath.item, section: indexPath.section + 1) as IndexPath 375 | } else { 376 | targetIndexPath = NSIndexPath.init(item: indexPath.item + leftFreezeColumn, section: indexPath.section + 1) as IndexPath 377 | } 378 | self.delegate?.excelView?(self, didSelectItemAt: targetIndexPath) 379 | } 380 | 381 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { 382 | 383 | if itemInsets != nil { 384 | return itemInsets! 385 | } 386 | return UIEdgeInsets.zero 387 | } 388 | } 389 | 390 | //MARK: - UISCrollViewDelegate 391 | extension AKExcelView { 392 | 393 | func scrollViewDidScroll(_ scrollView: UIScrollView) { 394 | 395 | if scrollView != contentScrollView { 396 | contentFreezeCollectionView.contentOffset = scrollView.contentOffset 397 | contentMoveableCollectionView.contentOffset = scrollView.contentOffset 398 | 399 | if scrollView.contentOffset.y > 0 { 400 | showHorizontalDivideShadowLayer() 401 | } else { 402 | dismissHorizontalDivideShadowLayer() 403 | } 404 | 405 | }else{ 406 | 407 | if (scrollView.contentOffset.x > 0) { 408 | showVerticalDivideShadowLayer() 409 | } else { 410 | dismissVerticalDivideShadowLayer() 411 | } 412 | CATransaction.begin() 413 | CATransaction.setDisableActions(true) 414 | veritcalShadow.transform = CATransform3DMakeTranslation(scrollView.contentOffset.x, 0, 0) 415 | CATransaction.commit() 416 | } 417 | } 418 | 419 | func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { 420 | 421 | if autoScrollToNearItem && scrollView == contentScrollView && !decelerate { 422 | scrollEndAnimation(scrollView: scrollView) 423 | 424 | } 425 | } 426 | 427 | func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 428 | if scrollView == contentScrollView && autoScrollToNearItem { 429 | scrollEndAnimation(scrollView: scrollView) 430 | } 431 | } 432 | 433 | fileprivate func scrollEndAnimation(scrollView: UIScrollView) { 434 | 435 | let offSetX = scrollView.contentOffset.x 436 | 437 | let deltaOffSets = dataManager.slideItemOffSetX.flatMap({ (offset) -> CGFloat in 438 | 439 | return abs(offset - offSetX) 440 | }) 441 | 442 | var min:CGFloat = deltaOffSets[0] 443 | 444 | for i in 0.. Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // 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. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // 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. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo/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 | } -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo/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 | -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo/Model.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Model.swift 3 | // AKExcelDemo 4 | // 5 | // Created by 吴莎莉 on 2017/5/31. 6 | // Copyright © 2017年 alasku. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class Model: NSObject { 12 | 13 | var productNo : String? 14 | var productName : String? 15 | var specification : String? 16 | var quantity : String? 17 | var note : String? 18 | var pro : String? 19 | 20 | init(dict:[String : AnyObject]) { 21 | super.init() 22 | setValuesForKeys(dict) 23 | } 24 | override init() { 25 | super.init() 26 | } 27 | override func setValue(_ value: Any?, forUndefinedKey key: String) {} 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // AKExcelDemo 4 | // 5 | // Created by 吴莎莉 on 2017/5/31. 6 | // Copyright © 2017年 alasku. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | let AKScreenWidth = UIScreen.main.bounds.size.width 12 | let AKScreenHeight = UIScreen.main.bounds.size.height 13 | 14 | 15 | class ViewController: UIViewController , AKExcelViewDelegate { 16 | 17 | 18 | override func viewDidLoad() { 19 | 20 | super.viewDidLoad() 21 | view.backgroundColor = .white 22 | title = "我的统计" 23 | if #available(iOS 11.0, *) { 24 | 25 | }else{ 26 | 27 | automaticallyAdjustsScrollViewInsets = false 28 | } 29 | 30 | let excelView : AKExcelView = AKExcelView.init(frame: CGRect.init(x: 0, y: 20, width: AKScreenWidth, height: AKScreenHeight - 20)) 31 | // 自动滚到最近的一列 32 | excelView.autoScrollToNearItem = true 33 | // 设置表头背景色 34 | excelView.headerBackgroundColor = UIColor.cyan 35 | // 设置表头 36 | excelView.headerTitles = ["货号","品名","规格","数量","说明"] 37 | // 设置间隙 38 | excelView.textMargin = 20 39 | // 设置左侧冻结栏数 40 | excelView.leftFreezeColumn = 1 41 | // 设置对应模型里面的属性 按顺序 42 | excelView.properties = ["productNo","productName","specification","quantity","note"] 43 | excelView.delegate = self 44 | // 指定列 设置 指定宽度 [column:width,...] 45 | excelView.columnWidthSetting = [3:180] 46 | excelView.showNoDataView = true 47 | var arrM = [Model]() 48 | for i in 0 ..< 50 { 49 | 50 | let model = Model() 51 | model.productNo = String.init("货号 - \(i)") 52 | model.productName = String.init("品名 - \(i)") 53 | model.specification = String.init("规格 - \(i)") 54 | model.quantity = String.init("数量 - \(i)") 55 | model.note = String.init("说明说明说明说明说明说明说明说明 - \(i)") 56 | model.pro = "others ..." 57 | 58 | arrM.append(model) 59 | } 60 | excelView.contentData = arrM 61 | view.addSubview(excelView) 62 | 63 | // excelView.reloadData() 64 | 65 | excelView.reloadDataCompleteHandler { 66 | print(" reload complete") 67 | 68 | } 69 | } 70 | } 71 | 72 | //MARK: - AKExcelViewDelegate 73 | 74 | extension UIViewController { 75 | // 代理方法 点击cell 76 | @objc func excelView(_ excelView: AKExcelView, didSelectItemAt indexPath: IndexPath) { 77 | print("section: \(indexPath.section) - item: \(indexPath.item)") 78 | 79 | let alertVc = UIAlertController.init(title: "Title", message: "tap section: \(indexPath.section) - item: \(indexPath.item)", preferredStyle: .alert) 80 | let action = UIAlertAction.init(title: "OK", style: .default, handler: nil) 81 | alertVc.addAction(action) 82 | self.present(alertVc, animated: true, completion: nil) 83 | } 84 | 85 | // 替换item View的代理方法 86 | @objc func excelView(_ excelView: AKExcelView, viewAt indexPath: IndexPath) -> UIView? { 87 | 88 | // 获取当前cell的size 89 | // let size = excelView.sizeForRow(row: indexPath.row) 90 | 91 | // print(size) 92 | 93 | if indexPath.section == 5 && indexPath.row == 3 { 94 | 95 | let customView = UIView() 96 | customView.backgroundColor = UIColor.blue 97 | 98 | return customView 99 | } 100 | return nil 101 | } 102 | 103 | } 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /AKExcelDemo/AKExcelDemo/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlasKuNull/SwiftyExcelView/e22a8514b455e1423b5a2afa7ebb5f515f54714b/AKExcelDemo/AKExcelDemo/demo.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 AlasKuNull 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftyExcelView 2 | This example project shows a way to show A Form like Excel in Swift. 3 | # Screenshot 4 | ![image](https://github.com/AlasKuNull/SwiftyExcelView/blob/master/AKExcelDemo/AKExcelDemo/9.gif) 5 | # Useage 6 | Drag AKExcelCollectionViewCell.swift , AKExcelDateManager.swift and AKExcelView.swift into your project. 7 | Demo shows detail how to use this. 8 | ``` 9 | let excelView : AKExcelView = AKExcelView.init(frame: CGRect.init(x: 0, y: 20, width: AKScreenWidth, height: AKScreenHeight - 20)) 10 | // 自动滚到最近的一列 11 | excelView.autoScrollToNearItem = true 12 | // 设置表头背景色 13 | excelView.headerBackgroundColor = UIColor.cyan 14 | // 设置表头 15 | excelView.headerTitles = ["货号","品名","规格","数量","说明"] 16 | // 设置间隙 17 | excelView.textMargin = 20 18 | // 设置左侧冻结栏数 19 | excelView.leftFreezeColumn = 1 20 | // 设置对应模型里面的属性 按顺序 21 | excelView.properties = ["productNo","productName","specification","quantity","note"] 22 | excelView.delegate = self 23 | // 指定列 设置 指定宽度 [column:width,...] 24 | excelView.columnWidthSetting = [3:180] 25 | var arrM = [Model]() 26 | for i in 0 ..< 50 { 27 | 28 | let model = Model() 29 | model.productNo = String.init("货号 - \(i)") 30 | model.productName = String.init("品名 - \(i)") 31 | model.specification = String.init("规格 - \(i)") 32 | model.quantity = String.init("数量 - \(i)") 33 | model.note = String.init("说明说明说明说明说明说明说明说明 - \(i)") 34 | model.pro = "others ..." 35 | 36 | arrM.append(model) 37 | } 38 |        // 设置数据 39 |        excelView.contentData = arrM 40 | 41 | view.addSubview(excelView) 42 |        // 刷新 43 |        excelView.reloadData() 44 | 45 | ``` 46 | # Thanks 47 | [BSNumbersView](https://github.com/blurryssky/BSNumbersView) , A form like Excel in Objective-C 48 | --------------------------------------------------------------------------------