├── DialLayout.gif ├── DialScrollLayout.podspec ├── DialScrollLayout.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── apple.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── apple.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── DialScrollLayout ├── Assets.xcassets │ ├── 1.imageset │ │ ├── 1.jpg │ │ └── Contents.json │ ├── 2.imageset │ │ ├── 2.jpg │ │ └── Contents.json │ ├── 3.imageset │ │ ├── 3.jpg │ │ └── Contents.json │ ├── 4.imageset │ │ ├── 4.jpg │ │ └── Contents.json │ ├── 5.imageset │ │ ├── 5.jpg │ │ └── Contents.json │ ├── 6.imageset │ │ ├── 6.jpg │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── DialScrollLayout.swift └── Info.plist ├── Example ├── AppDelegate.swift ├── ImageCollectionCell.swift └── ViewController.swift └── README.md /DialLayout.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunmehta77/DialScrollLayout/31154915a9bc7090f63d7b8f7aaa41336ac9a520/DialLayout.gif -------------------------------------------------------------------------------- /DialScrollLayout.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'DialScrollLayout' 3 | s.version = '0.1.0' 4 | s.summary = 'DialScrollLayout is collection view with scrolling cells' 5 | 6 | s.description = <<-DESC 7 | DialScrollLayout is collection view with scrolling cells with centering at corner of scrollview like old phone dial. 8 | DESC 9 | 10 | s.homepage = 'https://github.com/varunmehta77/DialScrollLayout.git' 11 | s.license = { :type => 'MIT', :file => 'LICENSE' } 12 | s.author = { 'Varun Mehta' => 'varunmehta077@gmail.com' } 13 | s.source = { :git => 'https://github.com/varunmehta77/DialScrollLayout.git', :tag => '0.1.0' } 14 | s.pod_target_xcconfig = { 'SWIFT_VERSION' => '3.2' } 15 | s.ios.deployment_target = '10.0' 16 | s.source_files = 'DialScrollLayout/DialScrollLayout.swift' 17 | 18 | end 19 | 20 | -------------------------------------------------------------------------------- /DialScrollLayout.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1800A60C212ADE99007199B2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1800A60B212ADE99007199B2 /* AppDelegate.swift */; }; 11 | 1800A613212ADE9A007199B2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1800A612212ADE9A007199B2 /* Assets.xcassets */; }; 12 | 1800A616212ADE9A007199B2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1800A614212ADE9A007199B2 /* LaunchScreen.storyboard */; }; 13 | 1800A61E212ADEE3007199B2 /* DialScrollLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1800A61D212ADEE3007199B2 /* DialScrollLayout.swift */; }; 14 | 1800A621212AFAC1007199B2 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1800A620212AFAC1007199B2 /* ViewController.swift */; }; 15 | 1800A623212AFAEB007199B2 /* ImageCollectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1800A622212AFAEB007199B2 /* ImageCollectionCell.swift */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 1800A608212ADE99007199B2 /* DialScrollLayout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DialScrollLayout.app; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | 1800A60B212ADE99007199B2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 21 | 1800A612212ADE9A007199B2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 22 | 1800A615212ADE9A007199B2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 23 | 1800A617212ADE9A007199B2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 24 | 1800A61D212ADEE3007199B2 /* DialScrollLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DialScrollLayout.swift; sourceTree = ""; }; 25 | 1800A620212AFAC1007199B2 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 26 | 1800A622212AFAEB007199B2 /* ImageCollectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCollectionCell.swift; sourceTree = ""; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | 1800A605212ADE99007199B2 /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | ); 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXFrameworksBuildPhase section */ 38 | 39 | /* Begin PBXGroup section */ 40 | 1800A5FF212ADE99007199B2 = { 41 | isa = PBXGroup; 42 | children = ( 43 | 1800A61F212AFAA7007199B2 /* Example */, 44 | 1800A60A212ADE99007199B2 /* DialScrollLayout */, 45 | 1800A609212ADE99007199B2 /* Products */, 46 | ); 47 | sourceTree = ""; 48 | }; 49 | 1800A609212ADE99007199B2 /* Products */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | 1800A608212ADE99007199B2 /* DialScrollLayout.app */, 53 | ); 54 | name = Products; 55 | sourceTree = ""; 56 | }; 57 | 1800A60A212ADE99007199B2 /* DialScrollLayout */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | 1800A61D212ADEE3007199B2 /* DialScrollLayout.swift */, 61 | 1800A612212ADE9A007199B2 /* Assets.xcassets */, 62 | 1800A614212ADE9A007199B2 /* LaunchScreen.storyboard */, 63 | 1800A617212ADE9A007199B2 /* Info.plist */, 64 | ); 65 | path = DialScrollLayout; 66 | sourceTree = ""; 67 | }; 68 | 1800A61F212AFAA7007199B2 /* Example */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 1800A60B212ADE99007199B2 /* AppDelegate.swift */, 72 | 1800A620212AFAC1007199B2 /* ViewController.swift */, 73 | 1800A622212AFAEB007199B2 /* ImageCollectionCell.swift */, 74 | ); 75 | path = Example; 76 | sourceTree = ""; 77 | }; 78 | /* End PBXGroup section */ 79 | 80 | /* Begin PBXNativeTarget section */ 81 | 1800A607212ADE99007199B2 /* DialScrollLayout */ = { 82 | isa = PBXNativeTarget; 83 | buildConfigurationList = 1800A61A212ADE9A007199B2 /* Build configuration list for PBXNativeTarget "DialScrollLayout" */; 84 | buildPhases = ( 85 | 1800A604212ADE99007199B2 /* Sources */, 86 | 1800A605212ADE99007199B2 /* Frameworks */, 87 | 1800A606212ADE99007199B2 /* Resources */, 88 | ); 89 | buildRules = ( 90 | ); 91 | dependencies = ( 92 | ); 93 | name = DialScrollLayout; 94 | productName = DialScrollLayout; 95 | productReference = 1800A608212ADE99007199B2 /* DialScrollLayout.app */; 96 | productType = "com.apple.product-type.application"; 97 | }; 98 | /* End PBXNativeTarget section */ 99 | 100 | /* Begin PBXProject section */ 101 | 1800A600212ADE99007199B2 /* Project object */ = { 102 | isa = PBXProject; 103 | attributes = { 104 | LastSwiftUpdateCheck = 0940; 105 | LastUpgradeCheck = 0940; 106 | ORGANIZATIONNAME = Apple; 107 | TargetAttributes = { 108 | 1800A607212ADE99007199B2 = { 109 | CreatedOnToolsVersion = 9.4.1; 110 | }; 111 | }; 112 | }; 113 | buildConfigurationList = 1800A603212ADE99007199B2 /* Build configuration list for PBXProject "DialScrollLayout" */; 114 | compatibilityVersion = "Xcode 9.3"; 115 | developmentRegion = en; 116 | hasScannedForEncodings = 0; 117 | knownRegions = ( 118 | en, 119 | Base, 120 | ); 121 | mainGroup = 1800A5FF212ADE99007199B2; 122 | productRefGroup = 1800A609212ADE99007199B2 /* Products */; 123 | projectDirPath = ""; 124 | projectRoot = ""; 125 | targets = ( 126 | 1800A607212ADE99007199B2 /* DialScrollLayout */, 127 | ); 128 | }; 129 | /* End PBXProject section */ 130 | 131 | /* Begin PBXResourcesBuildPhase section */ 132 | 1800A606212ADE99007199B2 /* Resources */ = { 133 | isa = PBXResourcesBuildPhase; 134 | buildActionMask = 2147483647; 135 | files = ( 136 | 1800A616212ADE9A007199B2 /* LaunchScreen.storyboard in Resources */, 137 | 1800A613212ADE9A007199B2 /* Assets.xcassets in Resources */, 138 | ); 139 | runOnlyForDeploymentPostprocessing = 0; 140 | }; 141 | /* End PBXResourcesBuildPhase section */ 142 | 143 | /* Begin PBXSourcesBuildPhase section */ 144 | 1800A604212ADE99007199B2 /* Sources */ = { 145 | isa = PBXSourcesBuildPhase; 146 | buildActionMask = 2147483647; 147 | files = ( 148 | 1800A61E212ADEE3007199B2 /* DialScrollLayout.swift in Sources */, 149 | 1800A60C212ADE99007199B2 /* AppDelegate.swift in Sources */, 150 | 1800A621212AFAC1007199B2 /* ViewController.swift in Sources */, 151 | 1800A623212AFAEB007199B2 /* ImageCollectionCell.swift in Sources */, 152 | ); 153 | runOnlyForDeploymentPostprocessing = 0; 154 | }; 155 | /* End PBXSourcesBuildPhase section */ 156 | 157 | /* Begin PBXVariantGroup section */ 158 | 1800A614212ADE9A007199B2 /* LaunchScreen.storyboard */ = { 159 | isa = PBXVariantGroup; 160 | children = ( 161 | 1800A615212ADE9A007199B2 /* Base */, 162 | ); 163 | name = LaunchScreen.storyboard; 164 | sourceTree = ""; 165 | }; 166 | /* End PBXVariantGroup section */ 167 | 168 | /* Begin XCBuildConfiguration section */ 169 | 1800A618212ADE9A007199B2 /* Debug */ = { 170 | isa = XCBuildConfiguration; 171 | buildSettings = { 172 | ALWAYS_SEARCH_USER_PATHS = NO; 173 | CLANG_ANALYZER_NONNULL = YES; 174 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 175 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 176 | CLANG_CXX_LIBRARY = "libc++"; 177 | CLANG_ENABLE_MODULES = YES; 178 | CLANG_ENABLE_OBJC_ARC = YES; 179 | CLANG_ENABLE_OBJC_WEAK = YES; 180 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 181 | CLANG_WARN_BOOL_CONVERSION = YES; 182 | CLANG_WARN_COMMA = YES; 183 | CLANG_WARN_CONSTANT_CONVERSION = YES; 184 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 185 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 186 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 187 | CLANG_WARN_EMPTY_BODY = YES; 188 | CLANG_WARN_ENUM_CONVERSION = YES; 189 | CLANG_WARN_INFINITE_RECURSION = YES; 190 | CLANG_WARN_INT_CONVERSION = YES; 191 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 192 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 193 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 194 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 195 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 196 | CLANG_WARN_STRICT_PROTOTYPES = YES; 197 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 198 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 199 | CLANG_WARN_UNREACHABLE_CODE = YES; 200 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 201 | CODE_SIGN_IDENTITY = "iPhone Developer"; 202 | COPY_PHASE_STRIP = NO; 203 | DEBUG_INFORMATION_FORMAT = dwarf; 204 | ENABLE_STRICT_OBJC_MSGSEND = YES; 205 | ENABLE_TESTABILITY = YES; 206 | GCC_C_LANGUAGE_STANDARD = gnu11; 207 | GCC_DYNAMIC_NO_PIC = NO; 208 | GCC_NO_COMMON_BLOCKS = YES; 209 | GCC_OPTIMIZATION_LEVEL = 0; 210 | GCC_PREPROCESSOR_DEFINITIONS = ( 211 | "DEBUG=1", 212 | "$(inherited)", 213 | ); 214 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 215 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 216 | GCC_WARN_UNDECLARED_SELECTOR = YES; 217 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 218 | GCC_WARN_UNUSED_FUNCTION = YES; 219 | GCC_WARN_UNUSED_VARIABLE = YES; 220 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 221 | MTL_ENABLE_DEBUG_INFO = YES; 222 | ONLY_ACTIVE_ARCH = YES; 223 | SDKROOT = iphoneos; 224 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 225 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 226 | }; 227 | name = Debug; 228 | }; 229 | 1800A619212ADE9A007199B2 /* Release */ = { 230 | isa = XCBuildConfiguration; 231 | buildSettings = { 232 | ALWAYS_SEARCH_USER_PATHS = NO; 233 | CLANG_ANALYZER_NONNULL = YES; 234 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 235 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 236 | CLANG_CXX_LIBRARY = "libc++"; 237 | CLANG_ENABLE_MODULES = YES; 238 | CLANG_ENABLE_OBJC_ARC = YES; 239 | CLANG_ENABLE_OBJC_WEAK = YES; 240 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 241 | CLANG_WARN_BOOL_CONVERSION = YES; 242 | CLANG_WARN_COMMA = YES; 243 | CLANG_WARN_CONSTANT_CONVERSION = YES; 244 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 245 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 246 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 247 | CLANG_WARN_EMPTY_BODY = YES; 248 | CLANG_WARN_ENUM_CONVERSION = YES; 249 | CLANG_WARN_INFINITE_RECURSION = YES; 250 | CLANG_WARN_INT_CONVERSION = YES; 251 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 252 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 253 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 255 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 256 | CLANG_WARN_STRICT_PROTOTYPES = YES; 257 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 258 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 259 | CLANG_WARN_UNREACHABLE_CODE = YES; 260 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 261 | CODE_SIGN_IDENTITY = "iPhone Developer"; 262 | COPY_PHASE_STRIP = NO; 263 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 264 | ENABLE_NS_ASSERTIONS = NO; 265 | ENABLE_STRICT_OBJC_MSGSEND = YES; 266 | GCC_C_LANGUAGE_STANDARD = gnu11; 267 | GCC_NO_COMMON_BLOCKS = YES; 268 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 269 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 270 | GCC_WARN_UNDECLARED_SELECTOR = YES; 271 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 272 | GCC_WARN_UNUSED_FUNCTION = YES; 273 | GCC_WARN_UNUSED_VARIABLE = YES; 274 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 275 | MTL_ENABLE_DEBUG_INFO = NO; 276 | SDKROOT = iphoneos; 277 | SWIFT_COMPILATION_MODE = wholemodule; 278 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 279 | VALIDATE_PRODUCT = YES; 280 | }; 281 | name = Release; 282 | }; 283 | 1800A61B212ADE9A007199B2 /* Debug */ = { 284 | isa = XCBuildConfiguration; 285 | buildSettings = { 286 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 287 | CODE_SIGN_STYLE = Automatic; 288 | DEVELOPMENT_TEAM = HNVWD8VFHC; 289 | INFOPLIST_FILE = DialScrollLayout/Info.plist; 290 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 291 | LD_RUNPATH_SEARCH_PATHS = ( 292 | "$(inherited)", 293 | "@executable_path/Frameworks", 294 | ); 295 | PRODUCT_BUNDLE_IDENTIFIER = com.ios.DialScrollLayout; 296 | PRODUCT_NAME = "$(TARGET_NAME)"; 297 | SWIFT_VERSION = 4.0; 298 | TARGETED_DEVICE_FAMILY = "1,2"; 299 | }; 300 | name = Debug; 301 | }; 302 | 1800A61C212ADE9A007199B2 /* Release */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 306 | CODE_SIGN_STYLE = Automatic; 307 | DEVELOPMENT_TEAM = HNVWD8VFHC; 308 | INFOPLIST_FILE = DialScrollLayout/Info.plist; 309 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 310 | LD_RUNPATH_SEARCH_PATHS = ( 311 | "$(inherited)", 312 | "@executable_path/Frameworks", 313 | ); 314 | PRODUCT_BUNDLE_IDENTIFIER = com.ios.DialScrollLayout; 315 | PRODUCT_NAME = "$(TARGET_NAME)"; 316 | SWIFT_VERSION = 4.0; 317 | TARGETED_DEVICE_FAMILY = "1,2"; 318 | }; 319 | name = Release; 320 | }; 321 | /* End XCBuildConfiguration section */ 322 | 323 | /* Begin XCConfigurationList section */ 324 | 1800A603212ADE99007199B2 /* Build configuration list for PBXProject "DialScrollLayout" */ = { 325 | isa = XCConfigurationList; 326 | buildConfigurations = ( 327 | 1800A618212ADE9A007199B2 /* Debug */, 328 | 1800A619212ADE9A007199B2 /* Release */, 329 | ); 330 | defaultConfigurationIsVisible = 0; 331 | defaultConfigurationName = Release; 332 | }; 333 | 1800A61A212ADE9A007199B2 /* Build configuration list for PBXNativeTarget "DialScrollLayout" */ = { 334 | isa = XCConfigurationList; 335 | buildConfigurations = ( 336 | 1800A61B212ADE9A007199B2 /* Debug */, 337 | 1800A61C212ADE9A007199B2 /* Release */, 338 | ); 339 | defaultConfigurationIsVisible = 0; 340 | defaultConfigurationName = Release; 341 | }; 342 | /* End XCConfigurationList section */ 343 | }; 344 | rootObject = 1800A600212ADE99007199B2 /* Project object */; 345 | } 346 | -------------------------------------------------------------------------------- /DialScrollLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DialScrollLayout.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DialScrollLayout.xcodeproj/project.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunmehta77/DialScrollLayout/31154915a9bc7090f63d7b8f7aaa41336ac9a520/DialScrollLayout.xcodeproj/project.xcworkspace/xcuserdata/apple.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /DialScrollLayout.xcodeproj/xcuserdata/apple.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | DialScrollLayout.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /DialScrollLayout/Assets.xcassets/1.imageset/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunmehta77/DialScrollLayout/31154915a9bc7090f63d7b8f7aaa41336ac9a520/DialScrollLayout/Assets.xcassets/1.imageset/1.jpg -------------------------------------------------------------------------------- /DialScrollLayout/Assets.xcassets/1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "1.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /DialScrollLayout/Assets.xcassets/2.imageset/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunmehta77/DialScrollLayout/31154915a9bc7090f63d7b8f7aaa41336ac9a520/DialScrollLayout/Assets.xcassets/2.imageset/2.jpg -------------------------------------------------------------------------------- /DialScrollLayout/Assets.xcassets/2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "2.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /DialScrollLayout/Assets.xcassets/3.imageset/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunmehta77/DialScrollLayout/31154915a9bc7090f63d7b8f7aaa41336ac9a520/DialScrollLayout/Assets.xcassets/3.imageset/3.jpg -------------------------------------------------------------------------------- /DialScrollLayout/Assets.xcassets/3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "3.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /DialScrollLayout/Assets.xcassets/4.imageset/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunmehta77/DialScrollLayout/31154915a9bc7090f63d7b8f7aaa41336ac9a520/DialScrollLayout/Assets.xcassets/4.imageset/4.jpg -------------------------------------------------------------------------------- /DialScrollLayout/Assets.xcassets/4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "4.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /DialScrollLayout/Assets.xcassets/5.imageset/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunmehta77/DialScrollLayout/31154915a9bc7090f63d7b8f7aaa41336ac9a520/DialScrollLayout/Assets.xcassets/5.imageset/5.jpg -------------------------------------------------------------------------------- /DialScrollLayout/Assets.xcassets/5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "5.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /DialScrollLayout/Assets.xcassets/6.imageset/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunmehta77/DialScrollLayout/31154915a9bc7090f63d7b8f7aaa41336ac9a520/DialScrollLayout/Assets.xcassets/6.imageset/6.jpg -------------------------------------------------------------------------------- /DialScrollLayout/Assets.xcassets/6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "6.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /DialScrollLayout/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /DialScrollLayout/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /DialScrollLayout/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /DialScrollLayout/DialScrollLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DialScrollLayout.swift 3 | // DialScrollLayout 4 | // 5 | // Created by Apple on 20/08/18. 6 | // Copyright © 2018 Apple. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public enum ScrollAlignmentType { 13 | case Center 14 | case Left 15 | } 16 | 17 | public class DialScrollLayout: UICollectionViewFlowLayout { 18 | 19 | var wheelType: ScrollAlignmentType = .Center 20 | var cellCount: Int! = 0 21 | var offset: CGFloat = 0 22 | var xOffset: CGFloat = 0 23 | var center: CGPoint = CGPoint.zero 24 | var dialRadius: CGFloat = 0 25 | var itemHeight: CGFloat = 0 26 | var cellSize: CGSize = CGSize.zero 27 | var angularSpacing: CGFloat = 0 28 | var currentIndexPath:IndexPath! 29 | 30 | public var changeAlphaWhileScrolling: Bool = true 31 | public var shouldTransformSize: Bool = true 32 | public var shouldSnap: Bool = false 33 | public var isLayoutRightSide: Bool = false 34 | 35 | var lastVelocity: CGPoint = CGPoint.zero 36 | 37 | public init(radius: CGFloat, angularSpacing: CGFloat, cellSize:CGSize, alignment:ScrollAlignmentType, itemHeight:CGFloat, xOffset:CGFloat) { 38 | super.init() 39 | 40 | self.wheelType = alignment 41 | self.angularSpacing = angularSpacing 42 | self.dialRadius = radius 43 | self.xOffset = xOffset 44 | self.cellSize = cellSize 45 | self.itemHeight = itemHeight 46 | self.itemSize = cellSize 47 | 48 | self.minimumInteritemSpacing = 0 49 | self.minimumLineSpacing = 0 50 | self.itemHeight = itemHeight 51 | self.angularSpacing = angularSpacing 52 | self.sectionInset = UIEdgeInsets.zero 53 | self.scrollDirection = .vertical 54 | self.offset = 0.0 55 | } 56 | 57 | required public init?(coder aDecoder: NSCoder) { 58 | fatalError("init(coder:) has not been implemented") 59 | } 60 | 61 | override public func prepare() { 62 | super.prepare() 63 | 64 | if self.collectionView!.numberOfSections > 0 { 65 | self.cellCount = self.collectionView?.numberOfItems(inSection: 0) 66 | } else { 67 | self.cellCount = 0 68 | } 69 | self.offset = -self.collectionView!.contentOffset.y / self.itemHeight 70 | } 71 | 72 | override public func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { 73 | return true 74 | } 75 | 76 | 77 | func getRectForItem(_ itemIndex: Int) -> CGRect { 78 | let newIndex = CGFloat(itemIndex) + self.offset 79 | 80 | let deltaX = self.cellSize.width/2 81 | 82 | let temp = Float(self.angularSpacing) 83 | 84 | let dds = Float(self.dialRadius + (deltaX*1)) 85 | 86 | var rX = cosf(temp * Float(newIndex) * Float(Double.pi/180)) * dds 87 | 88 | let rY = sinf(temp * Float(newIndex) * Float(Double.pi/180)) * dds 89 | var oX = -self.dialRadius + self.xOffset - (0.5 * self.cellSize.width); 90 | let oY = self.collectionView!.bounds.size.height/2 + self.collectionView!.contentOffset.y - (0.5 * self.cellSize.height) 91 | 92 | if isLayoutRightSide { 93 | oX = self.collectionView!.frame.size.width + self.dialRadius - self.xOffset - (0.5 * self.cellSize.width) 94 | rX *= -1 95 | } 96 | 97 | let itemFrame = CGRect(x: oX + CGFloat(rX), y: oY + CGFloat(rY), width: self.cellSize.width, height: self.cellSize.height) 98 | 99 | return itemFrame 100 | } 101 | 102 | 103 | override public func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 104 | var theLayoutAttributes = Array() 105 | 106 | let maxVisiblesHalf:Int = 180 / Int(self.angularSpacing) 107 | 108 | for i in 0 ..< self.cellCount { 109 | let itemFrame = self.getRectForItem(i) 110 | 111 | if rect.intersects(itemFrame) && i > (-1 * Int(self.offset) - maxVisiblesHalf) && i < (-1 * Int(self.offset) + maxVisiblesHalf) { 112 | let indexPath = IndexPath(item: i, section: 0) 113 | let theAttributes = self.layoutAttributesForItem(at: indexPath) 114 | theLayoutAttributes.append(theAttributes!) 115 | } 116 | } 117 | 118 | return theLayoutAttributes; 119 | } 120 | 121 | override public func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { 122 | if shouldSnap { 123 | let index = Int(floor(proposedContentOffset.y / self.itemHeight)) 124 | let off = (Int(proposedContentOffset.y) % Int(self.itemHeight)) 125 | 126 | let height = Int(self.itemHeight) 127 | 128 | var targetY = index * height 129 | if( off > Int((self.itemHeight * 0.5)) && index <= self.cellCount) { 130 | targetY = (index+1) * height 131 | } 132 | 133 | return CGPoint(x: proposedContentOffset.x, y: CGFloat(targetY)) 134 | } else { 135 | return proposedContentOffset 136 | } 137 | } 138 | 139 | 140 | override public func targetIndexPath(forInteractivelyMovingItem previousIndexPath: IndexPath, withPosition position: CGPoint) -> IndexPath { 141 | return IndexPath(item: 0, section: 0) 142 | } 143 | 144 | override public var collectionViewContentSize : CGSize { 145 | return CGSize(width: self.collectionView!.bounds.size.width, height: CGFloat(self.cellCount-1) * self.itemHeight + self.collectionView!.bounds.size.height) 146 | } 147 | 148 | override public func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { 149 | let newIndex = CGFloat(indexPath.item) + self.offset 150 | 151 | let attribute = UICollectionViewLayoutAttributes(forCellWith: indexPath) 152 | attribute.size = self.cellSize 153 | 154 | var scaleFactor: CGFloat! 155 | var translationT: CGAffineTransform! 156 | 157 | let rotationValue = self.angularSpacing * newIndex * CGFloat(Double.pi/180) 158 | var rotationT = CGAffineTransform(rotationAngle: rotationValue) 159 | 160 | if isLayoutRightSide { 161 | rotationT = CGAffineTransform(rotationAngle: -rotationValue) 162 | } 163 | 164 | if( self.wheelType == .Left){ 165 | scaleFactor = fmax(0.6, 1 - fabs( CGFloat(newIndex) * 0.25)) 166 | let newFrame = self.getRectForItem(indexPath.item) 167 | attribute.frame = CGRect(x: newFrame.origin.x , y: newFrame.origin.y, width: newFrame.size.width, height: newFrame.size.height) 168 | 169 | translationT = CGAffineTransform(translationX: 0 , y: 0) 170 | } else { 171 | scaleFactor = fmax(0.4, 1 - fabs( CGFloat(newIndex) * 0.50)) 172 | 173 | if isLayoutRightSide { 174 | attribute.center = CGPoint( x: self.collectionView!.frame.size.width + self.dialRadius - self.xOffset , y: self.collectionView!.bounds.size.height/2 + self.collectionView!.contentOffset.y) 175 | 176 | translationT = CGAffineTransform( translationX: -1 * (self.dialRadius + ((1 - scaleFactor) * -30)) , y: 0) 177 | } else { 178 | attribute.center = CGPoint(x: -self.dialRadius + self.xOffset , y: self.collectionView!.bounds.size.height/2 + self.collectionView!.contentOffset.y); 179 | translationT = CGAffineTransform(translationX: self.dialRadius + ((1 - scaleFactor) * -30) , y: 0); 180 | } 181 | } 182 | 183 | let scaleT:CGAffineTransform = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor) 184 | if changeAlphaWhileScrolling { 185 | attribute.alpha = scaleFactor 186 | } 187 | attribute.isHidden = false 188 | 189 | if shouldTransformSize { 190 | attribute.transform = scaleT.concatenating(translationT.concatenating(rotationT)) 191 | } 192 | 193 | return attribute 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /DialScrollLayout/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DialScrollLayout 4 | // 5 | // Created by Apple on 20/08/18. 6 | // Copyright © 2018 Apple. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | self.window = UIWindow(frame: UIScreen.main.bounds) 19 | self.window?.rootViewController = ViewController() 20 | self.window?.makeKeyAndVisible() 21 | 22 | return true 23 | } 24 | 25 | func applicationWillResignActive(_ application: UIApplication) { 26 | // 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. 27 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 28 | } 29 | 30 | func applicationDidEnterBackground(_ application: UIApplication) { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | func applicationWillEnterForeground(_ application: UIApplication) { 36 | // 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. 37 | } 38 | 39 | func applicationDidBecomeActive(_ application: UIApplication) { 40 | // 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. 41 | } 42 | 43 | func applicationWillTerminate(_ application: UIApplication) { 44 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 45 | } 46 | 47 | 48 | } 49 | 50 | -------------------------------------------------------------------------------- /Example/ImageCollectionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageCollectionCell.swift 3 | // DialScrollLayout 4 | // 5 | // Created by Apple on 20/08/18. 6 | // Copyright © 2018 Apple. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ImageCollectionCell: UICollectionViewCell { 12 | 13 | static let identifier = "cell" 14 | 15 | let imgView = UIImageView() 16 | 17 | override init(frame: CGRect) { 18 | super.init(frame: frame) 19 | setupImageView() 20 | } 21 | 22 | required init?(coder aDecoder: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | 26 | override func prepareForReuse() { 27 | super.prepareForReuse() 28 | imgView.image = nil 29 | } 30 | } 31 | 32 | extension ImageCollectionCell { 33 | 34 | ///setup image view 35 | fileprivate func setupImageView() { 36 | contentView.addSubview(imgView) 37 | imgView.translatesAutoresizingMaskIntoConstraints = false 38 | imgView.backgroundColor = UIColor.lightGray 39 | imgView.contentMode = .scaleAspectFit 40 | NSLayoutConstraint.activate([ 41 | imgView.leadingAnchor.constraint( 42 | equalTo: contentView.leadingAnchor 43 | ), 44 | imgView.trailingAnchor.constraint( 45 | equalTo: contentView.trailingAnchor 46 | ), 47 | imgView.topAnchor.constraint( 48 | equalTo: contentView.topAnchor 49 | ), 50 | imgView.bottomAnchor.constraint( 51 | equalTo: contentView.bottomAnchor 52 | ) 53 | ]) 54 | contentView.setNeedsLayout() 55 | contentView.layoutIfNeeded() 56 | imgView.clipsToBounds = true 57 | imgView.layer.cornerRadius = imgView.bounds.height/2 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExController.swift 3 | // DialScrollLayout 4 | // 5 | // Created by Apple on 20/08/18. 6 | // Copyright © 2018 Apple. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | fileprivate let cellHeight: CGFloat = 70 14 | fileprivate static let dialViewWidth: CGFloat = (UIScreen.main.bounds.width * 0.25) 15 | fileprivate static let dialViewHeight: CGFloat = (UIScreen.main.bounds.width * 0.25 * 3) 16 | 17 | fileprivate var segmentControl = UISegmentedControl() 18 | fileprivate var collectionView: UICollectionView! 19 | fileprivate var layout: DialScrollLayout! 20 | 21 | fileprivate var allImages = ["1", "2", "3", "4", "5", "6", "1", "2", "3", "4", "5", "6"] 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | // Do any additional setup after loading the view, typically from a nib. 26 | 27 | view.backgroundColor = UIColor.white 28 | setupSegmentControl() 29 | setupCollectionView() 30 | } 31 | 32 | override func didReceiveMemoryWarning() { 33 | super.didReceiveMemoryWarning() 34 | // Dispose of any resources that can be recreated. 35 | } 36 | } 37 | 38 | extension ViewController { 39 | 40 | ///calculating radius according to display 41 | fileprivate func getRadius() -> CGFloat { 42 | return (ViewController.dialViewWidth/2) + 43 | (pow(ViewController.dialViewHeight, 2)/(8*ViewController.dialViewWidth)); 44 | 45 | } 46 | 47 | @objc fileprivate func segmentControlChanged() { 48 | layout.isLayoutRightSide = segmentControl.selectedSegmentIndex == 1 49 | collectionView.reloadData() 50 | } 51 | } 52 | 53 | ///Setup views 54 | extension ViewController { 55 | ///setup segment control 56 | fileprivate func setupSegmentControl() { 57 | view.addSubview(segmentControl) 58 | segmentControl.translatesAutoresizingMaskIntoConstraints = false 59 | segmentControl.selectedSegmentIndex = 0 60 | segmentControl.insertSegment(withTitle: "Left", at: 0, animated: true) 61 | segmentControl.insertSegment(withTitle: "Right", at: 1, animated: true) 62 | segmentControl.selectedSegmentIndex = 0 63 | segmentControl.addTarget( 64 | self, 65 | action: #selector(segmentControlChanged), 66 | for: .valueChanged 67 | ) 68 | NSLayoutConstraint.activate([ 69 | segmentControl.centerXAnchor.constraint( 70 | equalTo: view.centerXAnchor 71 | ), 72 | segmentControl.topAnchor.constraint( 73 | equalTo: view.topAnchor, 74 | constant: 40 75 | ) 76 | ]) 77 | } 78 | 79 | ///setup for collection view 80 | fileprivate func setupCollectionView() { 81 | layout = DialScrollLayout.init( 82 | radius: getRadius() - (cellHeight/2), 83 | angularSpacing: 30, 84 | cellSize: CGSize.init( 85 | width: cellHeight, 86 | height: cellHeight 87 | ), 88 | alignment: .Left, 89 | itemHeight: cellHeight, 90 | xOffset: ViewController.dialViewWidth - cellHeight/2 91 | ) 92 | collectionView = UICollectionView.init( 93 | frame: CGRect.zero, 94 | collectionViewLayout: layout 95 | ) 96 | collectionView.translatesAutoresizingMaskIntoConstraints = false 97 | view.addSubview(collectionView) 98 | collectionView.backgroundColor = UIColor.clear 99 | collectionView.showsVerticalScrollIndicator = false 100 | collectionView.showsHorizontalScrollIndicator = false 101 | NSLayoutConstraint.activate([ 102 | collectionView.leadingAnchor.constraint( 103 | equalTo: view.leadingAnchor 104 | ), 105 | collectionView.trailingAnchor.constraint( 106 | equalTo: view.trailingAnchor 107 | ), 108 | collectionView.topAnchor.constraint( 109 | equalTo: segmentControl.bottomAnchor, 110 | constant: 24 111 | ), 112 | collectionView.bottomAnchor.constraint( 113 | equalTo: view.bottomAnchor 114 | ) 115 | ]) 116 | collectionView.register( 117 | ImageCollectionCell.self, 118 | forCellWithReuseIdentifier: ImageCollectionCell.identifier 119 | ) 120 | collectionView.clipsToBounds = true 121 | collectionView.dataSource = self 122 | collectionView.delegate = self 123 | } 124 | } 125 | 126 | extension ViewController: UICollectionViewDataSource { 127 | 128 | func numberOfSections(in collectionView: UICollectionView) -> Int { 129 | return 1 130 | } 131 | 132 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 133 | return allImages.count 134 | } 135 | 136 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 137 | let cell = collectionView.dequeueReusableCell( 138 | withReuseIdentifier: ImageCollectionCell.identifier, 139 | for: indexPath 140 | ) as! ImageCollectionCell 141 | cell.imgView.contentMode = .scaleToFill 142 | cell.imgView.image = UIImage.init(named: allImages[indexPath.row]) 143 | 144 | return cell 145 | } 146 | } 147 | 148 | extension ViewController: UICollectionViewDelegate { 149 | 150 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 151 | collectionView.deselectItem(at: indexPath, animated: true) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DialScrollLayout 2 | DialScrollLayout is collection view with scrolling cells with centering at corner of scrollview like old phone dial 3 | 4 | 5 | ### CocoaPods 6 | 7 | [CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command: 8 | 9 | ```bash 10 | $ gem install cocoapods 11 | ``` 12 | 13 | To integrate DialScrollLayout into your Xcode project using CocoaPods, specify it in your `Podfile`: 14 | 15 | ```ruby 16 | source 'https://github.com/CocoaPods/Specs.git' 17 | platform :ios, '10.0' 18 | use_frameworks! 19 | 20 | target '' do 21 | pod 'DialScrollLayout', '~> 0.1.0' 22 | end 23 | ``` 24 | 25 | Then, run the following command: 26 | 27 | ```bash 28 | $ pod install 29 | ``` 30 | 31 | 32 | 33 | 34 | ![alt tag](https://github.com/varunmehta77/DialScrollLayout/blob/master/DialLayout.gif) 35 | 36 | --------------------------------------------------------------------------------