diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a69dea1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +## OS X Finder +.DS_Store + +## Build generated +build/ +DerivedData + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata + +## Other +*.xccheckout +*.moved-aside +*.xcuserstate +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa + +# Swift Package Manager +.build/ + +# Carthage +Carthage/Build \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e69de29 diff --git a/DemoFastScroll/DemoFastScroll.xcodeproj/project.pbxproj b/DemoFastScroll/DemoFastScroll.xcodeproj/project.pbxproj new file mode 100644 index 0000000..81d5671 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll.xcodeproj/project.pbxproj @@ -0,0 +1,611 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + BD79A82F20D2A869000BD71C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD79A82E20D2A869000BD71C /* AppDelegate.swift */; }; + BD79A83120D2A869000BD71C /* DemoCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD79A83020D2A869000BD71C /* DemoCollectionViewController.swift */; }; + BD79A83420D2A869000BD71C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BD79A83220D2A869000BD71C /* Main.storyboard */; }; + BD79A83620D2A869000BD71C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BD79A83520D2A869000BD71C /* Assets.xcassets */; }; + BD79A83920D2A869000BD71C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BD79A83720D2A869000BD71C /* LaunchScreen.storyboard */; }; + BD79A87B20D2ADC1000BD71C /* FastScroll.h in Headers */ = {isa = PBXBuildFile; fileRef = BD79A87920D2ADC1000BD71C /* FastScroll.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BD79A87E20D2ADC1000BD71C /* FastScroll.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD79A87720D2ADC1000BD71C /* FastScroll.framework */; }; + BD79A87F20D2ADC1000BD71C /* FastScroll.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BD79A87720D2ADC1000BD71C /* FastScroll.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + BD79A88820D2B7EF000BD71C /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD79A88720D2B7EF000BD71C /* Item.swift */; }; + BD79A88A20D2B7FC000BD71C /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD79A88920D2B7FC000BD71C /* Section.swift */; }; + BD79A88D20D2BD75000BD71C /* ItemCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD79A88C20D2BD75000BD71C /* ItemCollectionViewCell.swift */; }; + BD79A88F20D2BE07000BD71C /* ItemCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BD79A88E20D2BE07000BD71C /* ItemCollectionViewCell.xib */; }; + BD86B24F20D2CEBC005735EE /* SectionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BD86B24E20D2CEBC005735EE /* SectionView.xib */; }; + BD86B25120D2CF89005735EE /* SectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD86B25020D2CF89005735EE /* SectionView.swift */; }; + BD86B26420D3B7FD005735EE /* FastScrollCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD86B26320D3B7FD005735EE /* FastScrollCollectionView.swift */; }; + BD86B26520D3C1BA005735EE /* FastScrollCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD86B26320D3B7FD005735EE /* FastScrollCollectionView.swift */; }; + BD86B2C920D8F339005735EE /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD86B2C820D8F338005735EE /* Utils.swift */; }; + BD86B2CB20D927CB005735EE /* DemoImageCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD86B2CA20D927CB005735EE /* DemoImageCollectionViewController.swift */; }; + BD86B2CD20D92B96005735EE /* DemoDefaultCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD86B2CC20D92B95005735EE /* DemoDefaultCollectionViewController.swift */; }; + BD86B2CF20D940AC005735EE /* DemoNoBubbleCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD86B2CE20D940AC005735EE /* DemoNoBubbleCollectionViewController.swift */; }; + BD86B2D120D940EA005735EE /* DemoLargeBubbleCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD86B2D020D940EA005735EE /* DemoLargeBubbleCollectionViewController.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + BD79A87C20D2ADC1000BD71C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BD79A82320D2A869000BD71C /* Project object */; + proxyType = 1; + remoteGlobalIDString = BD79A87620D2ADC1000BD71C; + remoteInfo = FastScroll; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + BD79A88320D2ADC1000BD71C /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + BD79A87F20D2ADC1000BD71C /* FastScroll.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + BD79A82B20D2A869000BD71C /* DemoFastScroll.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DemoFastScroll.app; sourceTree = BUILT_PRODUCTS_DIR; }; + BD79A82E20D2A869000BD71C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + BD79A83020D2A869000BD71C /* DemoCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoCollectionViewController.swift; sourceTree = ""; }; + BD79A83320D2A869000BD71C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + BD79A83520D2A869000BD71C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + BD79A83820D2A869000BD71C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + BD79A83A20D2A869000BD71C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BD79A87720D2ADC1000BD71C /* FastScroll.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FastScroll.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BD79A87920D2ADC1000BD71C /* FastScroll.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FastScroll.h; sourceTree = ""; }; + BD79A87A20D2ADC1000BD71C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BD79A88720D2B7EF000BD71C /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = ""; }; + BD79A88920D2B7FC000BD71C /* Section.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Section.swift; sourceTree = ""; }; + BD79A88C20D2BD75000BD71C /* ItemCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemCollectionViewCell.swift; sourceTree = ""; }; + BD79A88E20D2BE07000BD71C /* ItemCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ItemCollectionViewCell.xib; sourceTree = ""; }; + BD86B24E20D2CEBC005735EE /* SectionView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SectionView.xib; sourceTree = ""; }; + BD86B25020D2CF89005735EE /* SectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionView.swift; sourceTree = ""; }; + BD86B26320D3B7FD005735EE /* FastScrollCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FastScrollCollectionView.swift; path = ../FastScroll/FastScrollCollectionView.swift; sourceTree = ""; }; + BD86B2C820D8F338005735EE /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; + BD86B2CA20D927CB005735EE /* DemoImageCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoImageCollectionViewController.swift; sourceTree = ""; }; + BD86B2CC20D92B95005735EE /* DemoDefaultCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoDefaultCollectionViewController.swift; sourceTree = ""; }; + BD86B2CE20D940AC005735EE /* DemoNoBubbleCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoNoBubbleCollectionViewController.swift; sourceTree = ""; }; + BD86B2D020D940EA005735EE /* DemoLargeBubbleCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoLargeBubbleCollectionViewController.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + BD79A82820D2A869000BD71C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BD79A87E20D2ADC1000BD71C /* FastScroll.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BD79A87320D2ADC1000BD71C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + BD79A82220D2A869000BD71C = { + isa = PBXGroup; + children = ( + BD86B26320D3B7FD005735EE /* FastScrollCollectionView.swift */, + BD79A82D20D2A869000BD71C /* DemoFastScroll */, + BD79A87820D2ADC1000BD71C /* FastScroll */, + BD79A82C20D2A869000BD71C /* Products */, + BD79A85F20D2ACBC000BD71C /* Frameworks */, + ); + sourceTree = ""; + }; + BD79A82C20D2A869000BD71C /* Products */ = { + isa = PBXGroup; + children = ( + BD79A82B20D2A869000BD71C /* DemoFastScroll.app */, + BD79A87720D2ADC1000BD71C /* FastScroll.framework */, + ); + name = Products; + sourceTree = ""; + }; + BD79A82D20D2A869000BD71C /* DemoFastScroll */ = { + isa = PBXGroup; + children = ( + BD86B2C720D8F324005735EE /* Utils */, + BD79A89020D2BE4E000BD71C /* Components */, + BD79A88620D2B7D0000BD71C /* Model */, + BD79A88420D2B554000BD71C /* View Controllers */, + BD79A82E20D2A869000BD71C /* AppDelegate.swift */, + BD79A83220D2A869000BD71C /* Main.storyboard */, + BD79A83520D2A869000BD71C /* Assets.xcassets */, + BD79A83720D2A869000BD71C /* LaunchScreen.storyboard */, + BD79A83A20D2A869000BD71C /* Info.plist */, + ); + path = DemoFastScroll; + sourceTree = ""; + }; + BD79A85F20D2ACBC000BD71C /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + BD79A87820D2ADC1000BD71C /* FastScroll */ = { + isa = PBXGroup; + children = ( + BD79A87920D2ADC1000BD71C /* FastScroll.h */, + BD79A87A20D2ADC1000BD71C /* Info.plist */, + ); + path = FastScroll; + sourceTree = ""; + }; + BD79A88420D2B554000BD71C /* View Controllers */ = { + isa = PBXGroup; + children = ( + BD79A88520D2B572000BD71C /* CollectionView */, + ); + path = "View Controllers"; + sourceTree = ""; + }; + BD79A88520D2B572000BD71C /* CollectionView */ = { + isa = PBXGroup; + children = ( + BD79A83020D2A869000BD71C /* DemoCollectionViewController.swift */, + BD86B2CA20D927CB005735EE /* DemoImageCollectionViewController.swift */, + BD86B2CC20D92B95005735EE /* DemoDefaultCollectionViewController.swift */, + BD86B2CE20D940AC005735EE /* DemoNoBubbleCollectionViewController.swift */, + BD86B2D020D940EA005735EE /* DemoLargeBubbleCollectionViewController.swift */, + ); + path = CollectionView; + sourceTree = ""; + }; + BD79A88620D2B7D0000BD71C /* Model */ = { + isa = PBXGroup; + children = ( + BD79A88720D2B7EF000BD71C /* Item.swift */, + BD79A88920D2B7FC000BD71C /* Section.swift */, + ); + path = Model; + sourceTree = ""; + }; + BD79A88B20D2BD3C000BD71C /* Cells */ = { + isa = PBXGroup; + children = ( + BD79A88C20D2BD75000BD71C /* ItemCollectionViewCell.swift */, + BD79A88E20D2BE07000BD71C /* ItemCollectionViewCell.xib */, + ); + path = Cells; + sourceTree = ""; + }; + BD79A89020D2BE4E000BD71C /* Components */ = { + isa = PBXGroup; + children = ( + BD86B24D20D2CEA5005735EE /* Views */, + BD79A88B20D2BD3C000BD71C /* Cells */, + ); + path = Components; + sourceTree = ""; + }; + BD86B24D20D2CEA5005735EE /* Views */ = { + isa = PBXGroup; + children = ( + BD86B24E20D2CEBC005735EE /* SectionView.xib */, + BD86B25020D2CF89005735EE /* SectionView.swift */, + ); + path = Views; + sourceTree = ""; + }; + BD86B2C720D8F324005735EE /* Utils */ = { + isa = PBXGroup; + children = ( + BD86B2C820D8F338005735EE /* Utils.swift */, + ); + path = Utils; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + BD79A87420D2ADC1000BD71C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + BD79A87B20D2ADC1000BD71C /* FastScroll.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + BD79A82A20D2A869000BD71C /* DemoFastScroll */ = { + isa = PBXNativeTarget; + buildConfigurationList = BD79A83D20D2A869000BD71C /* Build configuration list for PBXNativeTarget "DemoFastScroll" */; + buildPhases = ( + BD79A82720D2A869000BD71C /* Sources */, + BD79A82820D2A869000BD71C /* Frameworks */, + BD79A82920D2A869000BD71C /* Resources */, + BD79A88320D2ADC1000BD71C /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + BD79A87D20D2ADC1000BD71C /* PBXTargetDependency */, + ); + name = DemoFastScroll; + productName = DemoFastScroll; + productReference = BD79A82B20D2A869000BD71C /* DemoFastScroll.app */; + productType = "com.apple.product-type.application"; + }; + BD79A87620D2ADC1000BD71C /* FastScroll */ = { + isa = PBXNativeTarget; + buildConfigurationList = BD79A88020D2ADC1000BD71C /* Build configuration list for PBXNativeTarget "FastScroll" */; + buildPhases = ( + BD79A87220D2ADC1000BD71C /* Sources */, + BD79A87320D2ADC1000BD71C /* Frameworks */, + BD79A87420D2ADC1000BD71C /* Headers */, + BD79A87520D2ADC1000BD71C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FastScroll; + productName = FastScroll; + productReference = BD79A87720D2ADC1000BD71C /* FastScroll.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BD79A82320D2A869000BD71C /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0900; + LastUpgradeCheck = 0900; + ORGANIZATIONNAME = Frichti; + TargetAttributes = { + BD79A82A20D2A869000BD71C = { + CreatedOnToolsVersion = 9.0.1; + ProvisioningStyle = Automatic; + }; + BD79A87620D2ADC1000BD71C = { + CreatedOnToolsVersion = 9.0.1; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = BD79A82620D2A869000BD71C /* Build configuration list for PBXProject "DemoFastScroll" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = BD79A82220D2A869000BD71C; + productRefGroup = BD79A82C20D2A869000BD71C /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BD79A82A20D2A869000BD71C /* DemoFastScroll */, + BD79A87620D2ADC1000BD71C /* FastScroll */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + BD79A82920D2A869000BD71C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BD79A83920D2A869000BD71C /* LaunchScreen.storyboard in Resources */, + BD86B24F20D2CEBC005735EE /* SectionView.xib in Resources */, + BD79A88F20D2BE07000BD71C /* ItemCollectionViewCell.xib in Resources */, + BD79A83620D2A869000BD71C /* Assets.xcassets in Resources */, + BD79A83420D2A869000BD71C /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BD79A87520D2ADC1000BD71C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + BD79A82720D2A869000BD71C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BD86B2CB20D927CB005735EE /* DemoImageCollectionViewController.swift in Sources */, + BD86B2CF20D940AC005735EE /* DemoNoBubbleCollectionViewController.swift in Sources */, + BD86B26420D3B7FD005735EE /* FastScrollCollectionView.swift in Sources */, + BD79A88D20D2BD75000BD71C /* ItemCollectionViewCell.swift in Sources */, + BD79A83120D2A869000BD71C /* DemoCollectionViewController.swift in Sources */, + BD86B25120D2CF89005735EE /* SectionView.swift in Sources */, + BD86B2CD20D92B96005735EE /* DemoDefaultCollectionViewController.swift in Sources */, + BD79A88A20D2B7FC000BD71C /* Section.swift in Sources */, + BD79A88820D2B7EF000BD71C /* Item.swift in Sources */, + BD86B2D120D940EA005735EE /* DemoLargeBubbleCollectionViewController.swift in Sources */, + BD79A82F20D2A869000BD71C /* AppDelegate.swift in Sources */, + BD86B2C920D8F339005735EE /* Utils.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BD79A87220D2ADC1000BD71C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BD86B26520D3C1BA005735EE /* FastScrollCollectionView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + BD79A87D20D2ADC1000BD71C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BD79A87620D2ADC1000BD71C /* FastScroll */; + targetProxy = BD79A87C20D2ADC1000BD71C /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + BD79A83220D2A869000BD71C /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + BD79A83320D2A869000BD71C /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + BD79A83720D2A869000BD71C /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + BD79A83820D2A869000BD71C /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + BD79A83B20D2A869000BD71C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + BD79A83C20D2A869000BD71C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + BD79A83E20D2A869000BD71C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = DemoFastScroll/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.frichti.DemoFastScroll; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + BD79A83F20D2A869000BD71C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = DemoFastScroll/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.frichti.DemoFastScroll; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + BD79A88120D2ADC1000BD71C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = FastScroll/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.frichti.FastScroll; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + BD79A88220D2ADC1000BD71C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = FastScroll/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.frichti.FastScroll; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BD79A82620D2A869000BD71C /* Build configuration list for PBXProject "DemoFastScroll" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BD79A83B20D2A869000BD71C /* Debug */, + BD79A83C20D2A869000BD71C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BD79A83D20D2A869000BD71C /* Build configuration list for PBXNativeTarget "DemoFastScroll" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BD79A83E20D2A869000BD71C /* Debug */, + BD79A83F20D2A869000BD71C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BD79A88020D2ADC1000BD71C /* Build configuration list for PBXNativeTarget "FastScroll" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BD79A88120D2ADC1000BD71C /* Debug */, + BD79A88220D2ADC1000BD71C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BD79A82320D2A869000BD71C /* Project object */; +} diff --git a/DemoFastScroll/DemoFastScroll/AppDelegate.swift b/DemoFastScroll/DemoFastScroll/AppDelegate.swift new file mode 100644 index 0000000..8007c59 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/AppDelegate.swift @@ -0,0 +1,46 @@ +// +// AppDelegate.swift +// DemoFastScroll +// +// Created by Arsene Huot on 14/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // 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. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // 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. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // 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. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // 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. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/AppIcon.appiconset/Contents.json b/DemoFastScroll/DemoFastScroll/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d8db8d6 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/Contents.json b/DemoFastScroll/DemoFastScroll/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/contact.imageset/Contents.json b/DemoFastScroll/DemoFastScroll/Assets.xcassets/contact.imageset/Contents.json new file mode 100644 index 0000000..912483c --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Assets.xcassets/contact.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "contact.jpg", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/contact.imageset/contact.jpg b/DemoFastScroll/DemoFastScroll/Assets.xcassets/contact.imageset/contact.jpg new file mode 100644 index 0000000..ca7b84a Binary files /dev/null and b/DemoFastScroll/DemoFastScroll/Assets.xcassets/contact.imageset/contact.jpg differ diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/cursor.imageset/Contents.json b/DemoFastScroll/DemoFastScroll/Assets.xcassets/cursor.imageset/Contents.json new file mode 100644 index 0000000..79684df --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Assets.xcassets/cursor.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "cursor.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "cursor@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "cursor@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/cursor.imageset/cursor.png b/DemoFastScroll/DemoFastScroll/Assets.xcassets/cursor.imageset/cursor.png new file mode 100644 index 0000000..b2b0143 Binary files /dev/null and b/DemoFastScroll/DemoFastScroll/Assets.xcassets/cursor.imageset/cursor.png differ diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/cursor.imageset/cursor@2x.png b/DemoFastScroll/DemoFastScroll/Assets.xcassets/cursor.imageset/cursor@2x.png new file mode 100644 index 0000000..6b776e6 Binary files /dev/null and b/DemoFastScroll/DemoFastScroll/Assets.xcassets/cursor.imageset/cursor@2x.png differ diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/cursor.imageset/cursor@3x.png b/DemoFastScroll/DemoFastScroll/Assets.xcassets/cursor.imageset/cursor@3x.png new file mode 100644 index 0000000..8e6c28a Binary files /dev/null and b/DemoFastScroll/DemoFastScroll/Assets.xcassets/cursor.imageset/cursor@3x.png differ diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/good.imageset/Contents.json b/DemoFastScroll/DemoFastScroll/Assets.xcassets/good.imageset/Contents.json new file mode 100644 index 0000000..eb07a6a --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Assets.xcassets/good.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icons8-pouce-en-l'air-50.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/good.imageset/icons8-pouce-en-l'air-50.png b/DemoFastScroll/DemoFastScroll/Assets.xcassets/good.imageset/icons8-pouce-en-l'air-50.png new file mode 100644 index 0000000..9ddc0c9 Binary files /dev/null and b/DemoFastScroll/DemoFastScroll/Assets.xcassets/good.imageset/icons8-pouce-en-l'air-50.png differ diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/hide.imageset/Contents.json b/DemoFastScroll/DemoFastScroll/Assets.xcassets/hide.imageset/Contents.json new file mode 100644 index 0000000..382acbe --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Assets.xcassets/hide.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icons8-cacher-50.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/hide.imageset/icons8-cacher-50.png b/DemoFastScroll/DemoFastScroll/Assets.xcassets/hide.imageset/icons8-cacher-50.png new file mode 100644 index 0000000..fdb4a74 Binary files /dev/null and b/DemoFastScroll/DemoFastScroll/Assets.xcassets/hide.imageset/icons8-cacher-50.png differ diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/home.imageset/Contents.json b/DemoFastScroll/DemoFastScroll/Assets.xcassets/home.imageset/Contents.json new file mode 100644 index 0000000..f11c37e --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Assets.xcassets/home.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icons8-chalet-50.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/home.imageset/icons8-chalet-50.png b/DemoFastScroll/DemoFastScroll/Assets.xcassets/home.imageset/icons8-chalet-50.png new file mode 100644 index 0000000..58afaaf Binary files /dev/null and b/DemoFastScroll/DemoFastScroll/Assets.xcassets/home.imageset/icons8-chalet-50.png differ diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/image.imageset/Contents.json b/DemoFastScroll/DemoFastScroll/Assets.xcassets/image.imageset/Contents.json new file mode 100644 index 0000000..95d29f4 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Assets.xcassets/image.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icons8-très-grandes-icônes-50.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git "a/DemoFastScroll/DemoFastScroll/Assets.xcassets/image.imageset/icons8-tr\303\250s-grandes-ic\303\264nes-50.png" "b/DemoFastScroll/DemoFastScroll/Assets.xcassets/image.imageset/icons8-tr\303\250s-grandes-ic\303\264nes-50.png" new file mode 100644 index 0000000..ce6882a Binary files /dev/null and "b/DemoFastScroll/DemoFastScroll/Assets.xcassets/image.imageset/icons8-tr\303\250s-grandes-ic\303\264nes-50.png" differ diff --git a/DemoFastScroll/DemoFastScroll/Assets.xcassets/large.imageset/Contents.json b/DemoFastScroll/DemoFastScroll/Assets.xcassets/large.imageset/Contents.json new file mode 100644 index 0000000..82bcf16 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Assets.xcassets/large.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icons8-ajuster-à-la-largeur-50.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git "a/DemoFastScroll/DemoFastScroll/Assets.xcassets/large.imageset/icons8-ajuster-\303\240-la-largeur-50.png" "b/DemoFastScroll/DemoFastScroll/Assets.xcassets/large.imageset/icons8-ajuster-\303\240-la-largeur-50.png" new file mode 100644 index 0000000..820e35e Binary files /dev/null and "b/DemoFastScroll/DemoFastScroll/Assets.xcassets/large.imageset/icons8-ajuster-\303\240-la-largeur-50.png" differ diff --git a/DemoFastScroll/DemoFastScroll/Base.lproj/LaunchScreen.storyboard b/DemoFastScroll/DemoFastScroll/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..6796117 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DemoFastScroll/DemoFastScroll/Base.lproj/Main.storyboard b/DemoFastScroll/DemoFastScroll/Base.lproj/Main.storyboard new file mode 100644 index 0000000..96fee21 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Base.lproj/Main.storyboard @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DemoFastScroll/DemoFastScroll/Components/Cells/ItemCollectionViewCell.swift b/DemoFastScroll/DemoFastScroll/Components/Cells/ItemCollectionViewCell.swift new file mode 100644 index 0000000..9877129 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Components/Cells/ItemCollectionViewCell.swift @@ -0,0 +1,13 @@ +// +// ItemCollectionViewCell.swift +// DemoFastScroll +// +// Created by Arsene Huot on 14/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +import UIKit + +class ItemCollectionViewCell: UICollectionViewCell { + @IBOutlet weak var titleLabel: UILabel! +} diff --git a/DemoFastScroll/DemoFastScroll/Components/Cells/ItemCollectionViewCell.xib b/DemoFastScroll/DemoFastScroll/Components/Cells/ItemCollectionViewCell.xib new file mode 100644 index 0000000..982e5c6 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Components/Cells/ItemCollectionViewCell.xib @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DemoFastScroll/DemoFastScroll/Components/Views/SectionView.swift b/DemoFastScroll/DemoFastScroll/Components/Views/SectionView.swift new file mode 100644 index 0000000..2efe219 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Components/Views/SectionView.swift @@ -0,0 +1,13 @@ +// +// SectionView.swift +// DemoFastScroll +// +// Created by Arsene Huot on 14/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +import UIKit + +class SectionView: UICollectionReusableView { + @IBOutlet weak var title: UILabel! +} diff --git a/DemoFastScroll/DemoFastScroll/Components/Views/SectionView.xib b/DemoFastScroll/DemoFastScroll/Components/Views/SectionView.xib new file mode 100644 index 0000000..630c5a6 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Components/Views/SectionView.xib @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DemoFastScroll/DemoFastScroll/Info.plist b/DemoFastScroll/DemoFastScroll/Info.plist new file mode 100644 index 0000000..89d7858 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Info.plist @@ -0,0 +1,43 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/DemoFastScroll/DemoFastScroll/Model/Item.swift b/DemoFastScroll/DemoFastScroll/Model/Item.swift new file mode 100644 index 0000000..461dde9 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Model/Item.swift @@ -0,0 +1,15 @@ +// +// Item.swift +// DemoFastScroll +// +// Created by Arsene Huot on 14/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +class Item { + let title: String + + init(title: String) { + self.title = title + } +} diff --git a/DemoFastScroll/DemoFastScroll/Model/Section.swift b/DemoFastScroll/DemoFastScroll/Model/Section.swift new file mode 100644 index 0000000..6b6140a --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Model/Section.swift @@ -0,0 +1,17 @@ +// +// Section.swift +// DemoFastScroll +// +// Created by Arsene Huot on 14/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +class Section { + var sectionTitle: String + var items: [Item] + + init(sectionTitle: String, items: [Item]) { + self.sectionTitle = sectionTitle + self.items = items + } +} diff --git a/DemoFastScroll/DemoFastScroll/Utils/Utils.swift b/DemoFastScroll/DemoFastScroll/Utils/Utils.swift new file mode 100644 index 0000000..bfdcabd --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/Utils/Utils.swift @@ -0,0 +1,383 @@ +// +// Constants.swift +// DemoFastScroll +// +// Created by Arsene Huot on 19/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +import Foundation + +class Utils { + static func fakeData() -> [Section] { + var data: [Section] = [] + + data.append(Section(sectionTitle: "A", + items: [Item(title: "Arsène"), + Item(title: "Alexis"), + Item(title: "Auriane"), + Item(title: "Arsène"), + Item(title: "Alexis"), + Item(title: "Auriane"), + Item(title: "Arsène"), + Item(title: "Alexis"), + Item(title: "Auriane")] + ) + ) + + data.append(Section(sectionTitle: "B", + items: [Item(title: "Basile"), + Item(title: "Bill"), + Item(title: "Bob"), + Item(title: "Bruce"), + Item(title: "Basile"), + Item(title: "Bill"), + Item(title: "Bob"), + Item(title: "Bruce"), + Item(title: "Basile"), + Item(title: "Bill"), + Item(title: "Bob"), + Item(title: "Bruce")] + ) + ) + + data.append(Section(sectionTitle: "C", + items: [Item(title: "Camille"), + Item(title: "Charlie"), + Item(title: "Celine"), + Item(title: "Cisse"), + Item(title: "Culmine"), + Item(title: "Camille"), + Item(title: "Charlie"), + Item(title: "Celine"), + Item(title: "Cisse"), + Item(title: "Culmine")] + ) + ) + + data.append(Section(sectionTitle: "D", + items: [Item(title: "Dany"), + Item(title: "Daniel"), + Item(title: "Darwin"), + Item(title: "Dumi"), + Item(title: "Diego"), + Item(title: "Dany"), + Item(title: "Daniel"), + Item(title: "Darwin"), + Item(title: "Dumi"), + Item(title: "Diego")] + ) + ) + + data.append(Section(sectionTitle: "F", + items: [Item(title: "Farah"), + Item(title: "Florie"), + Item(title: "Flore")] + ) + ) + + data.append(Section(sectionTitle: "L", + items: [Item(title: "Lana"), + Item(title: "Laura"), + Item(title: "Laure"), + Item(title: "Laureline"), + Item(title: "Lucie"), + Item(title: "Lana"), + Item(title: "Laura"), + Item(title: "Laure"), + Item(title: "Laureline"), + Item(title: "Lucie"), + Item(title: "Lana"), + Item(title: "Laura"), + Item(title: "Laure"), + Item(title: "Laureline"), + Item(title: "Lucie")] + ) + ) + + data.append(Section(sectionTitle: "M", + items: [Item(title: "Marie"), + Item(title: "Michel"), + Item(title: "Momo"), + Item(title: "Monique"), + Item(title: "Micheline"), + Item(title: "Marie"), + Item(title: "Michel"), + Item(title: "Momo"), + Item(title: "Monique"), + Item(title: "Micheline"), + Item(title: "Marie"), + Item(title: "Michel"), + Item(title: "Momo"), + Item(title: "Monique"), + Item(title: "Micheline")] + ) + ) + + data.append(Section(sectionTitle: "N", + items: [Item(title: "Nicolas"), + Item(title: "Noémie"), + Item(title: "Nadine"), + Item(title: "Nabilla"), + Item(title: "Nicolas"), + Item(title: "Noémie"), + Item(title: "Nadine"), + Item(title: "Nabilla"), + Item(title: "Nicolas"), + Item(title: "Noémie"), + Item(title: "Nadine"), + Item(title: "Nabilla")] + ) + ) + + data.append(Section(sectionTitle: "P", + items: [Item(title: "Papa"), + Item(title: "Pauline"), + Item(title: "Paela"), + Item(title: "Patrice"), + Item(title: "Popo"), + Item(title: "Papa"), + Item(title: "Pauline"), + Item(title: "Paela"), + Item(title: "Patrice"), + Item(title: "Popo")] + ) + ) + + data.append(Section(sectionTitle: "R", + items: [Item(title: "Rusti"), + Item(title: "Rihanna"), + Item(title: "Romain"), + Item(title: "Rustine"), + Item(title: "Raoul"), + Item(title: "Papa"), + Item(title: "Pauline"), + Item(title: "Paela"), + Item(title: "Patrice"), + Item(title: "Popo"), + Item(title: "Papa"), + Item(title: "Pauline"), + Item(title: "Paela"), + Item(title: "Patrice"), + Item(title: "Popo")] + ) + ) + + data.append(Section(sectionTitle: "S", + items: [Item(title: "Saul"), + Item(title: "Sebastien"), + Item(title: "Slimane"), + Item(title: "Suraya"), + Item(title: "Saul"), + Item(title: "Sebastien"), + Item(title: "Slimane"), + Item(title: "Suraya"), + Item(title: "Saul"), + Item(title: "Sebastien"), + Item(title: "Slimane"), + Item(title: "Suraya")] + ) + ) + + data.append(Section(sectionTitle: "V", + items: [Item(title: "Vivian"), + Item(title: "Violette"), + Item(title: "Vermine"), + Item(title: "Vichy"), + Item(title: "Vivian"), + Item(title: "Violette"), + Item(title: "Vermine"), + Item(title: "Vichy"), + Item(title: "Vivian"), + Item(title: "Violette"), + Item(title: "Vermine"), + Item(title: "Vichy")] + ) + ) + + return data + } + + static func fakeLargeData() -> [Section] { + var data: [Section] = [] + + data.append(Section(sectionTitle: "Favorites", + items: [Item(title: "Arsène"), + Item(title: "Alexis"), + Item(title: "Auriane"), + Item(title: "Arsène"), + Item(title: "Alexis"), + Item(title: "Auriane"), + Item(title: "Arsène"), + Item(title: "Alexis"), + Item(title: "Auriane")] + ) + ) + + data.append(Section(sectionTitle: "Best", + items: [Item(title: "Basile"), + Item(title: "Bill"), + Item(title: "Bob"), + Item(title: "Bruce"), + Item(title: "Basile"), + Item(title: "Bill"), + Item(title: "Bob"), + Item(title: "Bruce"), + Item(title: "Basile"), + Item(title: "Bill"), + Item(title: "Bob"), + Item(title: "Bruce")] + ) + ) + + data.append(Section(sectionTitle: "Best friends ever", + items: [Item(title: "Camille"), + Item(title: "Charlie"), + Item(title: "Celine"), + Item(title: "Cisse"), + Item(title: "Culmine"), + Item(title: "Camille"), + Item(title: "Charlie"), + Item(title: "Celine"), + Item(title: "Cisse"), + Item(title: "Culmine")] + ) + ) + + data.append(Section(sectionTitle: "Crazy guys but the best. Guys if you like the lib, let us a star please :)", + items: [Item(title: "Dany"), + Item(title: "Daniel"), + Item(title: "Darwin"), + Item(title: "Dumi"), + Item(title: "Diego"), + Item(title: "Dany"), + Item(title: "Daniel"), + Item(title: "Darwin"), + Item(title: "Dumi"), + Item(title: "Diego")] + ) + ) + + data.append(Section(sectionTitle: "Love them", + items: [Item(title: "Lana"), + Item(title: "Laura"), + Item(title: "Laure"), + Item(title: "Laureline"), + Item(title: "Lucie"), + Item(title: "Lana"), + Item(title: "Laura"), + Item(title: "Laure"), + Item(title: "Laureline"), + Item(title: "Lucie"), + Item(title: "Lana"), + Item(title: "Laura"), + Item(title: "Laure"), + Item(title: "Laureline"), + Item(title: "Lucie")] + ) + ) + + data.append(Section(sectionTitle: "Family", + items: [Item(title: "Marie"), + Item(title: "Michel"), + Item(title: "Momo"), + Item(title: "Monique"), + Item(title: "Micheline"), + Item(title: "Marie"), + Item(title: "Michel"), + Item(title: "Momo"), + Item(title: "Monique"), + Item(title: "Micheline"), + Item(title: "Marie"), + Item(title: "Michel"), + Item(title: "Momo"), + Item(title: "Monique"), + Item(title: "Micheline")] + ) + ) + + data.append(Section(sectionTitle: "Family Bis", + items: [Item(title: "Nicolas"), + Item(title: "Noémie"), + Item(title: "Nadine"), + Item(title: "Nabilla"), + Item(title: "Nicolas"), + Item(title: "Noémie"), + Item(title: "Nadine"), + Item(title: "Nabilla"), + Item(title: "Nicolas"), + Item(title: "Noémie"), + Item(title: "Nadine"), + Item(title: "Nabilla")] + ) + ) + + data.append(Section(sectionTitle: "Others", + items: [Item(title: "Papa"), + Item(title: "Pauline"), + Item(title: "Paela"), + Item(title: "Patrice"), + Item(title: "Popo"), + Item(title: "Papa"), + Item(title: "Pauline"), + Item(title: "Paela"), + Item(title: "Patrice"), + Item(title: "Popo")] + ) + ) + + data.append(Section(sectionTitle: "Ex Love", + items: [Item(title: "Rusti"), + Item(title: "Rihanna"), + Item(title: "Romain"), + Item(title: "Rustine"), + Item(title: "Raoul"), + Item(title: "Papa"), + Item(title: "Pauline"), + Item(title: "Paela"), + Item(title: "Patrice"), + Item(title: "Popo"), + Item(title: "Papa"), + Item(title: "Pauline"), + Item(title: "Paela"), + Item(title: "Patrice"), + Item(title: "Popo")] + ) + ) + + data.append(Section(sectionTitle: "Don't know", + items: [Item(title: "Saul"), + Item(title: "Sebastien"), + Item(title: "Slimane"), + Item(title: "Suraya"), + Item(title: "Saul"), + Item(title: "Sebastien"), + Item(title: "Slimane"), + Item(title: "Suraya"), + Item(title: "Saul"), + Item(title: "Sebastien"), + Item(title: "Slimane"), + Item(title: "Suraya")] + ) + ) + + data.append(Section(sectionTitle: "?", + items: [Item(title: "Vivian"), + Item(title: "Violette"), + Item(title: "Vermine"), + Item(title: "Vichy"), + Item(title: "Vivian"), + Item(title: "Violette"), + Item(title: "Vermine"), + Item(title: "Vichy"), + Item(title: "Vivian"), + Item(title: "Violette"), + Item(title: "Vermine"), + Item(title: "Vichy")] + ) + ) + + return data + } +} + diff --git a/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoCollectionViewController.swift b/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoCollectionViewController.swift new file mode 100644 index 0000000..6ce0cfa --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoCollectionViewController.swift @@ -0,0 +1,148 @@ +// +// ViewController.swift +// DemoFastScroll +// +// Created by Arsene Huot on 14/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +import UIKit + +class DemoCollectionViewController: UIViewController { + @IBOutlet weak var collectionView: FastScrollCollectionView! + + fileprivate var data: [Section] = [] + + override func viewDidLoad() { + super.viewDidLoad() + registerCell() + data = Utils.fakeData() + configFastScroll() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + collectionView.reloadData() + } +} + +// MARK: Helpers + +extension DemoCollectionViewController { + fileprivate func configFastScroll() { + collectionView.delegate = self + collectionView.dataSource = self + + //bubble + collectionView.bubbleFocus = .dynamic + collectionView.bubbleTextSize = 14.0 + collectionView.bubbleMarginRight = 60.0 + collectionView.bubbleColor = UIColor(red: 38.0 / 255.0, green: 48.0 / 255.0, blue: 60.0 / 255.0, alpha: 1.0) + + //handle + collectionView.handleHeight = 40.0 + collectionView.handleWidth = 40.0 + collectionView.handleRadius = 20.0 + collectionView.handleMarginRight = -20 + collectionView.handleColor = UIColor(red: 38.0 / 255.0, green: 48.0 / 255.0, blue: 60.0 / 255.0, alpha: 1.0) + + //scrollbar + collectionView.scrollbarWidth = 0.0 + collectionView.scrollbarMarginTop = 20.0 + collectionView.scrollbarMarginBottom = 0.0 + collectionView.scrollbarMarginRight = 10.0 + + //callback action to display bubble name + collectionView.bubbleNameForIndexPath = { indexPath in + let visibleSection: Section = self.data[indexPath.section] + return visibleSection.sectionTitle + } + } + + fileprivate func registerCell() { + collectionView?.register(UINib(nibName: String(describing: ItemCollectionViewCell.self), bundle: nil), forCellWithReuseIdentifier: String(describing: ItemCollectionViewCell.self)) + collectionView?.register(UINib(nibName: String(describing: SectionView.self), bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: String(describing: SectionView.self)) + } +} + + +// MARK: UICollectionViewDelegate + +extension DemoCollectionViewController: UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + guard let cell = cell as? ItemCollectionViewCell else { return } + + let item = data[indexPath.section].items[indexPath.row] + cell.titleLabel.text = item.title + } +} + +// MARK: UICollectionViewDataSource + +extension DemoCollectionViewController: UICollectionViewDataSource { + func numberOfSections(in collectionView: UICollectionView) -> Int { + return data.count + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return data[section].items.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + return collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ItemCollectionViewCell.self), for: indexPath) + } + + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + let section = data[indexPath.section] + let sectionView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: String(describing: SectionView.self), for: indexPath) as! SectionView + + sectionView.title.text = section.sectionTitle + return sectionView + } +} + +// MARK: - UICollectionViewDelegateFlowLayout + +extension DemoCollectionViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + return CGSize(width: view.frame.width, height: 90) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { + return CGSize.zero + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: view.frame.width, height: 70) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 0.0 + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 0.0 + } +} + +// MARK: - UIScrollViewDelegate + +extension DemoCollectionViewController: UIScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + collectionView.scrollViewDidScroll(scrollView) + } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + collectionView.scrollViewWillBeginDragging(scrollView) + } + + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + collectionView.scrollViewDidEndDecelerating(scrollView) + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + collectionView.scrollViewDidEndDragging(scrollView, willDecelerate: decelerate) + } +} + diff --git a/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoDefaultCollectionViewController.swift b/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoDefaultCollectionViewController.swift new file mode 100644 index 0000000..8699aac --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoDefaultCollectionViewController.swift @@ -0,0 +1,130 @@ +// +// ViewController.swift +// DemoFastScroll +// +// Created by Arsene Huot on 14/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +import UIKit + +class DemoDefaultCollectionViewController: UIViewController { + @IBOutlet weak var collectionView: FastScrollCollectionView! + + fileprivate var data: [Section] = [] + + override func viewDidLoad() { + super.viewDidLoad() + registerCell() + data = Utils.fakeData() + configFastScroll() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + collectionView.reloadData() + } +} + +// MARK: Helpers + +extension DemoDefaultCollectionViewController { + fileprivate func configFastScroll() { + collectionView.delegate = self + collectionView.dataSource = self + + //callback action to display bubble name + collectionView.bubbleNameForIndexPath = { indexPath in + let visibleSection: Section = self.data[indexPath.section] + return visibleSection.sectionTitle + } + } + + fileprivate func registerCell() { + collectionView?.register(UINib(nibName: String(describing: ItemCollectionViewCell.self), bundle: nil), forCellWithReuseIdentifier: String(describing: ItemCollectionViewCell.self)) + collectionView?.register(UINib(nibName: String(describing: SectionView.self), bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: String(describing: SectionView.self)) + } +} + + +// MARK: UICollectionViewDelegate + +extension DemoDefaultCollectionViewController: UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + guard let cell = cell as? ItemCollectionViewCell else { return } + + let item = data[indexPath.section].items[indexPath.row] + cell.titleLabel.text = item.title + } +} + +// MARK: UICollectionViewDataSource + +extension DemoDefaultCollectionViewController: UICollectionViewDataSource { + func numberOfSections(in collectionView: UICollectionView) -> Int { + return data.count + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return data[section].items.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + return collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ItemCollectionViewCell.self), for: indexPath) + } + + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + let section = data[indexPath.section] + let sectionView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: String(describing: SectionView.self), for: indexPath) as! SectionView + + sectionView.title.text = section.sectionTitle + return sectionView + } +} + +// MARK: - UICollectionViewDelegateFlowLayout + +extension DemoDefaultCollectionViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + return CGSize(width: view.frame.width, height: 90) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { + return CGSize.zero + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: view.frame.width, height: 70) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 0.0 + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 0.0 + } +} + +// MARK: - UIScrollViewDelegate + +extension DemoDefaultCollectionViewController: UIScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + collectionView.scrollViewDidScroll(scrollView) + } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + collectionView.scrollViewWillBeginDragging(scrollView) + } + + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + collectionView.scrollViewDidEndDecelerating(scrollView) + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + collectionView.scrollViewDidEndDragging(scrollView, willDecelerate: decelerate) + } +} + + diff --git a/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoImageCollectionViewController.swift b/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoImageCollectionViewController.swift new file mode 100644 index 0000000..d07207d --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoImageCollectionViewController.swift @@ -0,0 +1,149 @@ +// +// ViewController.swift +// DemoFastScroll +// +// Created by Arsene Huot on 14/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +import UIKit + +class DemoImageCollectionViewController: UIViewController { + @IBOutlet weak var collectionView: FastScrollCollectionView! + + fileprivate var data: [Section] = [] + + override func viewDidLoad() { + super.viewDidLoad() + registerCell() + data = Utils.fakeData() + configFastScroll() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + collectionView.reloadData() + } +} + +// MARK: Helpers + +extension DemoImageCollectionViewController { + fileprivate func configFastScroll() { + collectionView.delegate = self + collectionView.dataSource = self + + //bubble + collectionView.bubbleTextSize = 14.0 + collectionView.bubbleMarginRight = 80.0 + collectionView.bubbleColor = UIColor(red: 38.0 / 255.0, green: 48.0 / 255.0, blue: 60.0 / 255.0, alpha: 1.0) + + //handle + collectionView.handleImage = UIImage.init(named: "cursor") + collectionView.handleHeight = 40.0 + collectionView.handleWidth = 44.0 + collectionView.handleRadius = 0.0 + collectionView.handleMarginRight = 0 + collectionView.handleColor = UIColor.clear + + //scrollbar + collectionView.scrollbarWidth = 0.0 + collectionView.scrollbarMarginTop = 20.0 + collectionView.scrollbarMarginBottom = 0.0 + collectionView.scrollbarMarginRight = 10.0 + + //callback action to display bubble name + collectionView.bubbleNameForIndexPath = { indexPath in + let visibleSection: Section = self.data[indexPath.section] + return visibleSection.sectionTitle + } + } + + fileprivate func registerCell() { + collectionView?.register(UINib(nibName: String(describing: ItemCollectionViewCell.self), bundle: nil), forCellWithReuseIdentifier: String(describing: ItemCollectionViewCell.self)) + collectionView?.register(UINib(nibName: String(describing: SectionView.self), bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: String(describing: SectionView.self)) + } +} + + +// MARK: UICollectionViewDelegate + +extension DemoImageCollectionViewController: UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + guard let cell = cell as? ItemCollectionViewCell else { return } + + let item = data[indexPath.section].items[indexPath.row] + cell.titleLabel.text = item.title + } +} + +// MARK: UICollectionViewDataSource + +extension DemoImageCollectionViewController: UICollectionViewDataSource { + func numberOfSections(in collectionView: UICollectionView) -> Int { + return data.count + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return data[section].items.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + return collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ItemCollectionViewCell.self), for: indexPath) + } + + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + let section = data[indexPath.section] + let sectionView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: String(describing: SectionView.self), for: indexPath) as! SectionView + + sectionView.title.text = section.sectionTitle + return sectionView + } +} + +// MARK: - UICollectionViewDelegateFlowLayout + +extension DemoImageCollectionViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + return CGSize(width: view.frame.width, height: 90) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { + return CGSize.zero + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: view.frame.width, height: 70) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 0.0 + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 0.0 + } +} + +// MARK: - UIScrollViewDelegate + +extension DemoImageCollectionViewController: UIScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + collectionView.scrollViewDidScroll(scrollView) + } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + collectionView.scrollViewWillBeginDragging(scrollView) + } + + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + collectionView.scrollViewDidEndDecelerating(scrollView) + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + collectionView.scrollViewDidEndDragging(scrollView, willDecelerate: decelerate) + } +} + + diff --git a/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoLargeBubbleCollectionViewController.swift b/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoLargeBubbleCollectionViewController.swift new file mode 100644 index 0000000..62310b0 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoLargeBubbleCollectionViewController.swift @@ -0,0 +1,151 @@ +// +// ViewController.swift +// DemoFastScroll +// +// Created by Arsene Huot on 14/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +import UIKit + +class DemoLargeBubbleCollectionViewController: UIViewController { + @IBOutlet weak var collectionView: FastScrollCollectionView! + + fileprivate var data: [Section] = [] + + override func viewDidLoad() { + super.viewDidLoad() + registerCell() + data = Utils.fakeLargeData() + configFastScroll() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + collectionView.reloadData() + } +} + +// MARK: Helpers + +extension DemoLargeBubbleCollectionViewController { + fileprivate func configFastScroll() { + collectionView.delegate = self + collectionView.dataSource = self + + //bubble + collectionView.bubbleTextSize = 14.0 + collectionView.bubbleMarginRight = 80.0 + collectionView.bubbleColor = UIColor(red: 38.0 / 255.0, green: 48.0 / 255.0, blue: 60.0 / 255.0, alpha: 1.0) + + //handle + collectionView.handleImage = UIImage.init(named: "cursor") + collectionView.handleHeight = 40.0 + collectionView.handleWidth = 44.0 + collectionView.handleRadius = 0.0 + collectionView.handleMarginRight = 0 + collectionView.handleColor = UIColor.clear + + //scrollbar + collectionView.scrollbarWidth = 0.0 + collectionView.scrollbarMarginTop = 20.0 + collectionView.scrollbarMarginBottom = 0.0 + collectionView.scrollbarMarginRight = 10.0 + + //callback action to display bubble name + collectionView.bubbleNameForIndexPath = { indexPath in + let visibleSection: Section = self.data[indexPath.section] + return visibleSection.sectionTitle + } + } + + fileprivate func registerCell() { + collectionView?.register(UINib(nibName: String(describing: ItemCollectionViewCell.self), bundle: nil), forCellWithReuseIdentifier: String(describing: ItemCollectionViewCell.self)) + collectionView?.register(UINib(nibName: String(describing: SectionView.self), bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: String(describing: SectionView.self)) + } +} + + +// MARK: UICollectionViewDelegate + +extension DemoLargeBubbleCollectionViewController: UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + guard let cell = cell as? ItemCollectionViewCell else { return } + + let item = data[indexPath.section].items[indexPath.row] + cell.titleLabel.text = item.title + } +} + +// MARK: UICollectionViewDataSource + +extension DemoLargeBubbleCollectionViewController: UICollectionViewDataSource { + func numberOfSections(in collectionView: UICollectionView) -> Int { + return data.count + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return data[section].items.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + return collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ItemCollectionViewCell.self), for: indexPath) + } + + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + let section = data[indexPath.section] + let sectionView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: String(describing: SectionView.self), for: indexPath) as! SectionView + + sectionView.title.text = section.sectionTitle + return sectionView + } +} + +// MARK: - UICollectionViewDelegateFlowLayout + +extension DemoLargeBubbleCollectionViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + return CGSize(width: view.frame.width, height: 90) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { + return CGSize.zero + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: view.frame.width, height: 70) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 0.0 + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 0.0 + } +} + +// MARK: - UIScrollViewDelegate + +extension DemoLargeBubbleCollectionViewController: UIScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + collectionView.scrollViewDidScroll(scrollView) + } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + collectionView.scrollViewWillBeginDragging(scrollView) + } + + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + collectionView.scrollViewDidEndDecelerating(scrollView) + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + collectionView.scrollViewDidEndDragging(scrollView, willDecelerate: decelerate) + } +} + + + + diff --git a/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoNoBubbleCollectionViewController.swift b/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoNoBubbleCollectionViewController.swift new file mode 100644 index 0000000..09d06c2 --- /dev/null +++ b/DemoFastScroll/DemoFastScroll/View Controllers/CollectionView/DemoNoBubbleCollectionViewController.swift @@ -0,0 +1,148 @@ +// +// ViewController.swift +// DemoFastScroll +// +// Created by Arsene Huot on 14/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +import UIKit + +class DemoNoBubbleCollectionViewController: UIViewController { + @IBOutlet weak var collectionView: FastScrollCollectionView! + + fileprivate var data: [Section] = [] + + override func viewDidLoad() { + super.viewDidLoad() + registerCell() + data = Utils.fakeData() + configFastScroll() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + collectionView.reloadData() + } +} + +// MARK: Helpers + +extension DemoNoBubbleCollectionViewController { + fileprivate func configFastScroll() { + collectionView.delegate = self + collectionView.dataSource = self + + //bubble + collectionView.deactivateBubble = true + + //handle + collectionView.handleImage = UIImage.init(named: "cursor") + collectionView.handleHeight = 40.0 + collectionView.handleWidth = 44.0 + collectionView.handleRadius = 0.0 + collectionView.handleMarginRight = 0 + collectionView.handleColor = UIColor.clear + + //scrollbar + collectionView.scrollbarWidth = 0.0 + collectionView.scrollbarMarginTop = 20.0 + collectionView.scrollbarMarginBottom = 0.0 + collectionView.scrollbarMarginRight = 10.0 + + //callback action to display bubble name + collectionView.bubbleNameForIndexPath = { indexPath in + let visibleSection: Section = self.data[indexPath.section] + return visibleSection.sectionTitle + } + } + + fileprivate func registerCell() { + collectionView?.register(UINib(nibName: String(describing: ItemCollectionViewCell.self), bundle: nil), forCellWithReuseIdentifier: String(describing: ItemCollectionViewCell.self)) + collectionView?.register(UINib(nibName: String(describing: SectionView.self), bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: String(describing: SectionView.self)) + } +} + + +// MARK: UICollectionViewDelegate + +extension DemoNoBubbleCollectionViewController: UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + guard let cell = cell as? ItemCollectionViewCell else { return } + + let item = data[indexPath.section].items[indexPath.row] + cell.titleLabel.text = item.title + } +} + +// MARK: UICollectionViewDataSource + +extension DemoNoBubbleCollectionViewController: UICollectionViewDataSource { + func numberOfSections(in collectionView: UICollectionView) -> Int { + return data.count + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return data[section].items.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + return collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ItemCollectionViewCell.self), for: indexPath) + } + + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + let section = data[indexPath.section] + let sectionView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: String(describing: SectionView.self), for: indexPath) as! SectionView + + sectionView.title.text = section.sectionTitle + return sectionView + } +} + +// MARK: - UICollectionViewDelegateFlowLayout + +extension DemoNoBubbleCollectionViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + return CGSize(width: view.frame.width, height: 90) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { + return CGSize.zero + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: view.frame.width, height: 70) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 0.0 + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 0.0 + } +} + +// MARK: - UIScrollViewDelegate + +extension DemoNoBubbleCollectionViewController: UIScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + collectionView.scrollViewDidScroll(scrollView) + } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + collectionView.scrollViewWillBeginDragging(scrollView) + } + + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + collectionView.scrollViewDidEndDecelerating(scrollView) + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + collectionView.scrollViewDidEndDragging(scrollView, willDecelerate: decelerate) + } +} + + + diff --git a/DemoFastScroll/FastScroll/FastScroll.h b/DemoFastScroll/FastScroll/FastScroll.h new file mode 100644 index 0000000..a9c739a --- /dev/null +++ b/DemoFastScroll/FastScroll/FastScroll.h @@ -0,0 +1,19 @@ +// +// FastScroll.h +// FastScroll +// +// Created by Arsene Huot on 14/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +#import + +//! Project version number for FastScroll. +FOUNDATION_EXPORT double FastScrollVersionNumber; + +//! Project version string for FastScroll. +FOUNDATION_EXPORT const unsigned char FastScrollVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/DemoFastScroll/FastScroll/Info.plist b/DemoFastScroll/FastScroll/Info.plist new file mode 100644 index 0000000..1007fd9 --- /dev/null +++ b/DemoFastScroll/FastScroll/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/FastScroll.podspec b/FastScroll.podspec new file mode 100644 index 0000000..3f942b3 --- /dev/null +++ b/FastScroll.podspec @@ -0,0 +1,18 @@ +Pod::Spec.new do |spec| + spec.name = "FastScroll" + spec.version = "1.0.0" + spec.summary = "Framework to create a fast scroller on your collection view." + spec.homepage = "https://github.com/frichti/FastScroll" + spec.license = { type: 'MIT', file: 'LICENSE' } + spec.authors = { "Arsène Huot" => 'arsene.huot@frichti.co' } + spec.social_media_url = "https://twitter.com/FrichtiTech" + + spec.ios.deployment_target = "9.0" + spec.platform = :ios, "9.0" + spec.requires_arc = true + spec.source = { git: "https://github.com/frichti/FastScroll.git", tag: "v#{spec.version}", submodules: true } + spec.source_files = "FastScroll/**/*.{h,swift}" + spec.swift_version = '4.0' + + spec.frameworks = "UIKit", "Foundation" +end \ No newline at end of file diff --git a/FastScroll.xcodeproj/project.pbxproj b/FastScroll.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6257fc5 --- /dev/null +++ b/FastScroll.xcodeproj/project.pbxproj @@ -0,0 +1,459 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + BD86B25E20D3B773005735EE /* FastScrollCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD86B25D20D3B773005735EE /* FastScrollCollectionView.swift */; }; + BDC01AC320D29A39007CD19A /* FastScroll.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BDC01AB920D29A39007CD19A /* FastScroll.framework */; }; + BDC01AC820D29A39007CD19A /* FastScrollTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDC01AC720D29A39007CD19A /* FastScrollTests.swift */; }; + BDC01ACA20D29A39007CD19A /* FastScroll.h in Headers */ = {isa = PBXBuildFile; fileRef = BDC01ABC20D29A39007CD19A /* FastScroll.h */; settings = {ATTRIBUTES = (Public, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + BDC01AC420D29A39007CD19A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BDC01AB020D29A39007CD19A /* Project object */; + proxyType = 1; + remoteGlobalIDString = BDC01AB820D29A39007CD19A; + remoteInfo = FastScroll; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + BD86B25D20D3B773005735EE /* FastScrollCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FastScrollCollectionView.swift; sourceTree = ""; }; + BDC01AB920D29A39007CD19A /* FastScroll.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FastScroll.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BDC01ABC20D29A39007CD19A /* FastScroll.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FastScroll.h; sourceTree = ""; }; + BDC01ABD20D29A39007CD19A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BDC01AC220D29A39007CD19A /* FastScrollTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FastScrollTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + BDC01AC720D29A39007CD19A /* FastScrollTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FastScrollTests.swift; sourceTree = ""; }; + BDC01AC920D29A39007CD19A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + BDC01AB520D29A39007CD19A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BDC01ABF20D29A39007CD19A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BDC01AC320D29A39007CD19A /* FastScroll.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + BD79A81F20D2A056000BD71C /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + BDC01AAF20D29A39007CD19A = { + isa = PBXGroup; + children = ( + BDC01ABB20D29A39007CD19A /* FastScroll */, + BDC01AC620D29A39007CD19A /* FastScrollTests */, + BDC01ABA20D29A39007CD19A /* Products */, + BD79A81F20D2A056000BD71C /* Frameworks */, + ); + sourceTree = ""; + }; + BDC01ABA20D29A39007CD19A /* Products */ = { + isa = PBXGroup; + children = ( + BDC01AB920D29A39007CD19A /* FastScroll.framework */, + BDC01AC220D29A39007CD19A /* FastScrollTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + BDC01ABB20D29A39007CD19A /* FastScroll */ = { + isa = PBXGroup; + children = ( + BDC01ABC20D29A39007CD19A /* FastScroll.h */, + BDC01ABD20D29A39007CD19A /* Info.plist */, + BD86B25D20D3B773005735EE /* FastScrollCollectionView.swift */, + ); + path = FastScroll; + sourceTree = ""; + }; + BDC01AC620D29A39007CD19A /* FastScrollTests */ = { + isa = PBXGroup; + children = ( + BDC01AC720D29A39007CD19A /* FastScrollTests.swift */, + BDC01AC920D29A39007CD19A /* Info.plist */, + ); + path = FastScrollTests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + BDC01AB620D29A39007CD19A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + BDC01ACA20D29A39007CD19A /* FastScroll.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + BDC01AB820D29A39007CD19A /* FastScroll */ = { + isa = PBXNativeTarget; + buildConfigurationList = BDC01ACD20D29A39007CD19A /* Build configuration list for PBXNativeTarget "FastScroll" */; + buildPhases = ( + BDC01AB420D29A39007CD19A /* Sources */, + BDC01AB520D29A39007CD19A /* Frameworks */, + BDC01AB620D29A39007CD19A /* Headers */, + BDC01AB720D29A39007CD19A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FastScroll; + productName = FastScroll; + productReference = BDC01AB920D29A39007CD19A /* FastScroll.framework */; + productType = "com.apple.product-type.framework"; + }; + BDC01AC120D29A39007CD19A /* FastScrollTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = BDC01AD020D29A39007CD19A /* Build configuration list for PBXNativeTarget "FastScrollTests" */; + buildPhases = ( + BDC01ABE20D29A39007CD19A /* Sources */, + BDC01ABF20D29A39007CD19A /* Frameworks */, + BDC01AC020D29A39007CD19A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + BDC01AC520D29A39007CD19A /* PBXTargetDependency */, + ); + name = FastScrollTests; + productName = FastScrollTests; + productReference = BDC01AC220D29A39007CD19A /* FastScrollTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BDC01AB020D29A39007CD19A /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0900; + LastUpgradeCheck = 0900; + ORGANIZATIONNAME = Frichti; + TargetAttributes = { + BDC01AB820D29A39007CD19A = { + CreatedOnToolsVersion = 9.0.1; + LastSwiftMigration = 0900; + ProvisioningStyle = Manual; + }; + BDC01AC120D29A39007CD19A = { + CreatedOnToolsVersion = 9.0.1; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = BDC01AB320D29A39007CD19A /* Build configuration list for PBXProject "FastScroll" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = BDC01AAF20D29A39007CD19A; + productRefGroup = BDC01ABA20D29A39007CD19A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BDC01AB820D29A39007CD19A /* FastScroll */, + BDC01AC120D29A39007CD19A /* FastScrollTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + BDC01AB720D29A39007CD19A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BDC01AC020D29A39007CD19A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + BDC01AB420D29A39007CD19A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BD86B25E20D3B773005735EE /* FastScrollCollectionView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BDC01ABE20D29A39007CD19A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BDC01AC820D29A39007CD19A /* FastScrollTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + BDC01AC520D29A39007CD19A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BDC01AB820D29A39007CD19A /* FastScroll */; + targetProxy = BDC01AC420D29A39007CD19A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + BDC01ACB20D29A39007CD19A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + BDC01ACC20D29A39007CD19A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + BDC01ACE20D29A39007CD19A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = FastScroll/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.frichti.FastScroll; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + BDC01ACF20D29A39007CD19A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = FastScroll/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.frichti.FastScroll; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + BDC01AD120D29A39007CD19A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 3T4JPY5YD4; + INFOPLIST_FILE = FastScrollTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.frichti.FastScrollTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + BDC01AD220D29A39007CD19A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 3T4JPY5YD4; + INFOPLIST_FILE = FastScrollTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.frichti.FastScrollTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BDC01AB320D29A39007CD19A /* Build configuration list for PBXProject "FastScroll" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BDC01ACB20D29A39007CD19A /* Debug */, + BDC01ACC20D29A39007CD19A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BDC01ACD20D29A39007CD19A /* Build configuration list for PBXNativeTarget "FastScroll" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BDC01ACE20D29A39007CD19A /* Debug */, + BDC01ACF20D29A39007CD19A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BDC01AD020D29A39007CD19A /* Build configuration list for PBXNativeTarget "FastScrollTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BDC01AD120D29A39007CD19A /* Debug */, + BDC01AD220D29A39007CD19A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BDC01AB020D29A39007CD19A /* Project object */; +} diff --git a/FastScroll.xcodeproj/xcshareddata/xcschemes/FastScroll.xcscheme b/FastScroll.xcodeproj/xcshareddata/xcschemes/FastScroll.xcscheme new file mode 100644 index 0000000..b5b2686 --- /dev/null +++ b/FastScroll.xcodeproj/xcshareddata/xcschemes/FastScroll.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FastScroll/FastScroll.h b/FastScroll/FastScroll.h new file mode 100644 index 0000000..a9c739a --- /dev/null +++ b/FastScroll/FastScroll.h @@ -0,0 +1,19 @@ +// +// FastScroll.h +// FastScroll +// +// Created by Arsene Huot on 14/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +#import + +//! Project version number for FastScroll. +FOUNDATION_EXPORT double FastScrollVersionNumber; + +//! Project version string for FastScroll. +FOUNDATION_EXPORT const unsigned char FastScrollVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/FastScroll/FastScrollCollectionView.swift b/FastScroll/FastScrollCollectionView.swift new file mode 100644 index 0000000..a5c8107 --- /dev/null +++ b/FastScroll/FastScrollCollectionView.swift @@ -0,0 +1,344 @@ +// +// FastScrollCollectionView.swift +// FastScroll +// +// Created by Arsene Huot on 15/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +import Foundation +import UIKit + +open class FastScrollCollectionView: UICollectionView { + public enum BubbleFocus { + case first + case last + case dynamic + } + + // Bubble to display your information during scroll + public var deactivateBubble: Bool = false + public var bubble: UITextView? + public var bubbleFont: UIFont = UIFont.systemFont(ofSize: 12.0) + public var bubbleTextSize: CGFloat = 12.0 + public var bubbleTextColor: UIColor = UIColor.white + public var bubbleRadius: CGFloat = 20.0 + public var bubblePadding: CGFloat = 12.0 + public var bubbleMarginRight: CGFloat = 30.0 + public var bubbleColor: UIColor = UIColor.darkGray + public var bubbleShadowColor: UIColor = UIColor.darkGray + public var bubbleShadowOpacity: Float = 0.7 + public var bubbleShadowRadius: CGFloat = 3.0 + public var bubbleShadowOffset: CGSize = CGSize(width: 0.0, height: 5.0) + public var bubbleNumberOfLines: Int = 1 + public var bubbleFocus: BubbleFocus = .first + + // Handler to scroll + public var handle: UIView? + public var handleImage: UIImage? + public var handleWidth: CGFloat = 30.0 + public var handleHeight: CGFloat = 30.0 + public var handleRadius: CGFloat = 15.0 + public var handleMarginRight: CGFloat = 6.0 + public var handleShadowColor: UIColor = UIColor.darkGray + public var handleShadowOpacity: Float = 0.7 + public var handleShadowOffset: CGSize = CGSize(width: 0.0, height: 5.0) + public var handleShadowRadius: CGFloat = 3.0 + public var handleColor: UIColor = UIColor.darkGray + public var handleTimeToDisappear: CGFloat = 1.5 + public var handleDisappearAnimationDuration: CGFloat = 0.2 + public var handleTouched: Bool = false + + // Gesture center on handler + public var gestureHandleView: UIView? + public var gestureWidth: CGFloat = 50.0 + public var gestureHeight: CGFloat = 50.0 + + // Scrollbar + public var scrollbar: UIView? + public var scrollbarWidth: CGFloat = 2.0 + public var scrollbarColor: UIColor = UIColor(red: 220.0 / 255.0, green: 220.0 / 255.0, blue: 220.0 / 255.0, alpha: 1.0) + public var scrollbarRadius: CGFloat = 1.0 + public var scrollbarMarginTop: CGFloat = 40.0 + public var scrollbarMarginBottom: CGFloat = 20.0 + public var scrollbarMarginRight: CGFloat = 20.0 + + // Timer to dismiss handle + fileprivate var handleTimer: Timer? + + // Action callback + public var bubbleNameForIndexPath: (IndexPath) -> String = { _ in return ""} + + // MARK: LifeCycle + + override open func draw(_ rect: CGRect) { + super.draw(rect) + setup() + setupCollectionView() + } + + // MARK: Setups + + fileprivate func setupCollectionView() { + showsVerticalScrollIndicator = false + } + + public func setup() { + setupScrollbar() + setupHandle() + setupBubble() + } + + fileprivate func setupHandle() { + if handle == nil { + handle = UIView(frame: CGRect(x: self.frame.width - handleWidth - handleMarginRight, y: scrollbarMarginTop, width: handleWidth, height: handleHeight)) + self.superview?.addSubview(handle!) + + gestureHandleView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: gestureWidth, height: gestureHeight)) + gestureHandleView!.center = handle!.center + + self.superview?.addSubview(handle!) + self.superview?.addSubview(gestureHandleView!) + } + + //config layer + handle!.backgroundColor = handleColor + handle!.layer.cornerRadius = handleRadius + handle!.layer.shadowColor = handleShadowColor.cgColor + handle!.layer.shadowOffset = handleShadowOffset + handle!.layer.shadowRadius = handleShadowRadius + handle!.layer.shadowOpacity = handleShadowOpacity + + //set imageView + if let handleImage = handleImage { + let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: handleWidth, height: handleHeight)) + imageView.image = handleImage + handle!.addSubview(imageView) + } + + //set gesture + let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:))) + gestureHandleView!.addGestureRecognizer(panGesture) + + //hide + handle!.alpha = 0.0 + + //position + positionHandle(scrollbarMarginTop) + } + + fileprivate func setupBubble() { + if bubble == nil { + bubble = UITextView() + self.superview?.addSubview(bubble!) + } + + bubble!.font = bubbleFont + bubble!.font = UIFont(name: bubbleFont.fontName, size: bubbleTextSize) + bubble!.text = "Test" + bubble!.textColor = bubbleTextColor + bubble!.textAlignment = NSTextAlignment.center + bubble!.textContainerInset = UIEdgeInsetsMake(bubblePadding, bubblePadding, bubblePadding, bubblePadding) + bubble!.contentMode = UIViewContentMode.scaleAspectFit + bubble!.sizeToFit() + + bubble!.backgroundColor = bubbleColor + bubble!.layer.cornerRadius = bubbleRadius + bubble!.layer.shadowColor = bubbleShadowColor.cgColor + bubble!.layer.shadowOffset = bubbleShadowOffset + bubble!.layer.shadowRadius = bubbleRadius + bubble!.layer.shadowOpacity = bubbleShadowOpacity + bubble!.layer.shadowRadius = bubbleShadowRadius + bubble!.layer.masksToBounds = false + + bubble!.isHidden = true + + updateBubblePosition() + } + + fileprivate func setupScrollbar() { + guard let superview = self.superview else { + return + } + + if scrollbar == nil { + scrollbar = UIView(frame: CGRect(x: self.frame.width - scrollbarWidth - scrollbarMarginRight, y: scrollbarMarginTop, width: scrollbarWidth, height: superview.bounds.height - scrollbarMarginBottom - scrollbarMarginTop)) + self.superview?.addSubview(scrollbar!) + } + + scrollbar!.backgroundColor = scrollbarColor + scrollbar!.layer.cornerRadius = scrollbarRadius + scrollbar!.alpha = 0.0 + } + + // MARK: Helpers + @objc func hideHandle() { + guard let handle = handle, let scrollbar = scrollbar else { + return + } + + UIView.animate(withDuration: TimeInterval(handleDisappearAnimationDuration), animations: { + handle.alpha = 0.0 + scrollbar.alpha = 0.0 + }) + } + + fileprivate func updateBubblePosition() { + guard let scrollbar = scrollbar, let bubble = bubble, let handle = handle else { + return + } + + bubble.frame.origin.x = scrollbar.frame.origin.x - bubble.frame.size.width - bubbleMarginRight + bubble.center.y = handle.center.y + } + + fileprivate func positionHandle(_ y: CGFloat) { + guard let handle = handle, let scrollbar = scrollbar, let gestureHandleView = gestureHandleView else { + return + } + + handle.frame.origin.y = y >= scrollbarMarginTop ? + (y > scrollbarMarginTop + scrollbar.frame.height - handle.frame.height) ? scrollbarMarginTop + scrollbar.frame.height - handle.frame.height : y + : + scrollbarMarginTop + + gestureHandleView.center = handle.center + } + + fileprivate func scrollCollectionFromHandle() { + guard let handle = handle, let scrollbar = scrollbar else { + return + } + + let collectionContentHeight = self.contentSize.height - self.bounds.height + let scrollBarHeight = scrollbar.frame.height + + let scrollY = (handle.frame.origin.y - scrollbarMarginTop) * (collectionContentHeight / (scrollBarHeight - handle.frame.size.height)) + + self.setContentOffset(CGPoint(x: 0.0, y: scrollY), animated: false) + } + + @objc func handlePanGesture(_ panGesture: UIPanGestureRecognizer) { + guard let superview = superview, let bubble = bubble, let handle = handle, let handleTimer = handleTimer, let scrollbar = scrollbar else { + return + } + + // get translation + let translation = panGesture.translation(in: superview) + panGesture.setTranslation(CGPoint.zero, in: superview) + + // manage start stop pan + if panGesture.state == UIGestureRecognizerState.began { + bubble.isHidden = deactivateBubble ? true : false + handleTouched = true + + //invalid hide timer + handleTimer.invalidate() + handle.alpha = 1.0 + scrollbar.alpha = 1.0 + } + + if panGesture.state == UIGestureRecognizerState.ended { + bubble.isHidden = true + handleTouched = false + self.handleTimer = Timer.scheduledTimer(timeInterval: TimeInterval(handleTimeToDisappear), target: self, selector: #selector(hideHandle), userInfo: nil, repeats: false) + } + + if panGesture.state == UIGestureRecognizerState.changed { + //invalid hide timer + handleTimer.invalidate() + handle.alpha = 1.0 + scrollbar.alpha = 1.0 + } + + // views positions + positionHandle(handle.frame.origin.y + translation.y) + updateBubblePosition() + scrollCollectionFromHandle() + + // manage bubble info + manageBubbleInfo() + } + + fileprivate func manageBubbleInfo() { + guard let bubble = bubble else { + return + } + + let visibleCells = self.visibleCells + + var currentCellIndex: Int + + switch bubbleFocus { + case .first: + currentCellIndex = 0 + + case .last: + currentCellIndex = visibleCells.count - 1 + + case .dynamic: + //Calcul scroll percentage + let scrollY = contentOffset.y + let collectionContentHeight = self.contentSize.height > self.bounds.height ? self.contentSize.height - self.bounds.height : self.bounds.height + let scrollPercentage = scrollY / collectionContentHeight + currentCellIndex = Int(floor(CGFloat(visibleCells.count) * scrollPercentage)) + } + + if currentCellIndex < visibleCells.count { + if let indexPath = indexPath(for: visibleCells[currentCellIndex] as UICollectionViewCell) { + bubble.text = bubbleNameForIndexPath(indexPath) + let newSize = bubble.sizeThatFits(CGSize(width: self.bounds.width - (self.bounds.width - (bubble.frame.origin.x + bubble.frame.size.width)), height: bubble.frame.size.height)) + let oldSize = bubble.frame.size + bubble.frame = CGRect(x: bubble.frame.origin.x + (oldSize.width - newSize.width), y: bubble.frame.origin.y, width: newSize.width, height: newSize.height) + } + } + } +} + +// MARK: Scroll Management + +extension FastScrollCollectionView { + public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + guard let handle = handle, let scrollbar = scrollbar else { + return + } + + handle.alpha = 1.0 + scrollbar.alpha = 1.0 + } + + public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + if !decelerate { + self.handleTimer = Timer.scheduledTimer(timeInterval: TimeInterval(handleTimeToDisappear), target: self, selector: #selector(hideHandle), userInfo: nil, repeats: false) + } + } + + public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + self.handleTimer = Timer.scheduledTimer(timeInterval: TimeInterval(handleTimeToDisappear), target: self, selector: #selector(hideHandle), userInfo: nil, repeats: false) + } + + public func scrollViewDidScroll(_ scrollView: UIScrollView) { + guard let handle = handle, let scrollbar = scrollbar else { + return + } + + //invalid timer + if let handleTimer = handleTimer { + handleTimer.invalidate() + } + + //scroll position + let scrollY = scrollView.contentOffset.y + let collectionContentHeight = self.contentSize.height > self.bounds.height ? self.contentSize.height - self.bounds.height : self.bounds.height + let scrollBarHeight = scrollbar.frame.height + + + let handlePosition = (scrollY / collectionContentHeight) * (scrollBarHeight - handle.frame.size.height) + scrollbarMarginTop + if (handleTouched == false) { + positionHandle(handlePosition) + } + + updateBubblePosition() + } +} + diff --git a/FastScroll/Info.plist b/FastScroll/Info.plist new file mode 100644 index 0000000..4c0d218 --- /dev/null +++ b/FastScroll/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/FastScrollTests/FastScrollTests.swift b/FastScrollTests/FastScrollTests.swift new file mode 100644 index 0000000..a19ce28 --- /dev/null +++ b/FastScrollTests/FastScrollTests.swift @@ -0,0 +1,36 @@ +// +// FastScrollTests.swift +// FastScrollTests +// +// Created by Arsene Huot on 14/06/2018. +// Copyright © 2018 Frichti. All rights reserved. +// + +import XCTest +@testable import FastScroll + +class FastScrollTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/FastScrollTests/Info.plist b/FastScrollTests/Info.plist new file mode 100644 index 0000000..6c40a6c --- /dev/null +++ b/FastScrollTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1f8127c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Frichti.co + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.