├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── Package.swift ├── PinFlexLayoutDemo ├── PinFlexLayoutDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── PinFlexLayoutDemo.xcworkspace │ └── contents.xcworkspacedata ├── PinFlexLayoutDemo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift ├── Podfile └── Podfile.lock ├── Podfile ├── Podfile.lock ├── README.md ├── Resources ├── sample.gif └── shot.png ├── StackScrollView-Demo ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── ButtonStackCell.swift ├── DatePickerStackCell.swift ├── HeaderStackCell.swift ├── Info.plist ├── LabelStackCell.swift ├── MarginStackCell.swift ├── NibStackCell.swift ├── NibStackCell.xib ├── SeparatorStackCell.swift ├── StackCellBase.swift ├── SwitchStackCell.swift ├── TapStackCell.swift ├── TextFieldStackCell.swift ├── TextViewStackCell.swift └── ViewController.swift ├── StackScrollView.podspec ├── StackScrollView.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── StackScrollView.xcscheme ├── StackScrollView.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist └── StackScrollView ├── Info.plist ├── NibLoader.swift ├── StackCell.swift ├── StackCellType.swift ├── StackScrollView.h └── StackScrollView.swift /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [muukii] 2 | patreon: muukii 3 | ko_fi: muukii 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Carthage 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 29 | # 30 | # Note: if you ignore the Pods directory, make sure to uncomment 31 | # `pod install` in .travis.yml 32 | # 33 | Pods/ 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 muukii 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "StackScrollView", 6 | platforms: [ 7 | .iOS(.v11), 8 | ], 9 | products: [ 10 | .library(name: "StackScrollView", targets: ["StackScrollView"]), 11 | ], 12 | dependencies: [ 13 | ], 14 | targets: [ 15 | .target(name: "StackScrollView", dependencies: [], path: "StackScrollView"), 16 | ], 17 | swiftLanguageVersions: [.v5] 18 | ) 19 | -------------------------------------------------------------------------------- /PinFlexLayoutDemo/PinFlexLayoutDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4B97CDFE1FB8C0A800F7DEAE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B97CDFD1FB8C0A800F7DEAE /* AppDelegate.swift */; }; 11 | 4B97CE001FB8C0A800F7DEAE /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B97CDFF1FB8C0A800F7DEAE /* ViewController.swift */; }; 12 | 4B97CE031FB8C0A800F7DEAE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4B97CE011FB8C0A800F7DEAE /* Main.storyboard */; }; 13 | 4B97CE051FB8C0A800F7DEAE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4B97CE041FB8C0A800F7DEAE /* Assets.xcassets */; }; 14 | 4B97CE081FB8C0A800F7DEAE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4B97CE061FB8C0A800F7DEAE /* LaunchScreen.storyboard */; }; 15 | E5FD058D51B2B38C8AEA7E49 /* Pods_PinFlexLayoutDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4CC3F783CF7CE8407838F75 /* Pods_PinFlexLayoutDemo.framework */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 0B691F269169F80216C4753D /* Pods-PinFlexLayoutDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinFlexLayoutDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PinFlexLayoutDemo/Pods-PinFlexLayoutDemo.debug.xcconfig"; sourceTree = ""; }; 20 | 1E9AD18F8F72E5AB6581C856 /* Pods-PinFlexLayoutDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PinFlexLayoutDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-PinFlexLayoutDemo/Pods-PinFlexLayoutDemo.release.xcconfig"; sourceTree = ""; }; 21 | 4B97CDFA1FB8C0A800F7DEAE /* PinFlexLayoutDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PinFlexLayoutDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 4B97CDFD1FB8C0A800F7DEAE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 23 | 4B97CDFF1FB8C0A800F7DEAE /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 24 | 4B97CE021FB8C0A800F7DEAE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 25 | 4B97CE041FB8C0A800F7DEAE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 26 | 4B97CE071FB8C0A800F7DEAE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 27 | 4B97CE091FB8C0A800F7DEAE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 28 | B4CC3F783CF7CE8407838F75 /* Pods_PinFlexLayoutDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PinFlexLayoutDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 4B97CDF71FB8C0A800F7DEAE /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | E5FD058D51B2B38C8AEA7E49 /* Pods_PinFlexLayoutDemo.framework in Frameworks */, 37 | ); 38 | runOnlyForDeploymentPostprocessing = 0; 39 | }; 40 | /* End PBXFrameworksBuildPhase section */ 41 | 42 | /* Begin PBXGroup section */ 43 | 4B97CDF11FB8C0A800F7DEAE = { 44 | isa = PBXGroup; 45 | children = ( 46 | 4B97CDFC1FB8C0A800F7DEAE /* PinFlexLayoutDemo */, 47 | 4B97CDFB1FB8C0A800F7DEAE /* Products */, 48 | 707D91799B9292539928BE9D /* Pods */, 49 | 5D24B0919A4C2459EAE2DE5B /* Frameworks */, 50 | ); 51 | sourceTree = ""; 52 | }; 53 | 4B97CDFB1FB8C0A800F7DEAE /* Products */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | 4B97CDFA1FB8C0A800F7DEAE /* PinFlexLayoutDemo.app */, 57 | ); 58 | name = Products; 59 | sourceTree = ""; 60 | }; 61 | 4B97CDFC1FB8C0A800F7DEAE /* PinFlexLayoutDemo */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | 4B97CDFD1FB8C0A800F7DEAE /* AppDelegate.swift */, 65 | 4B97CDFF1FB8C0A800F7DEAE /* ViewController.swift */, 66 | 4B97CE011FB8C0A800F7DEAE /* Main.storyboard */, 67 | 4B97CE041FB8C0A800F7DEAE /* Assets.xcassets */, 68 | 4B97CE061FB8C0A800F7DEAE /* LaunchScreen.storyboard */, 69 | 4B97CE091FB8C0A800F7DEAE /* Info.plist */, 70 | ); 71 | path = PinFlexLayoutDemo; 72 | sourceTree = ""; 73 | }; 74 | 5D24B0919A4C2459EAE2DE5B /* Frameworks */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | B4CC3F783CF7CE8407838F75 /* Pods_PinFlexLayoutDemo.framework */, 78 | ); 79 | name = Frameworks; 80 | sourceTree = ""; 81 | }; 82 | 707D91799B9292539928BE9D /* Pods */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 0B691F269169F80216C4753D /* Pods-PinFlexLayoutDemo.debug.xcconfig */, 86 | 1E9AD18F8F72E5AB6581C856 /* Pods-PinFlexLayoutDemo.release.xcconfig */, 87 | ); 88 | name = Pods; 89 | sourceTree = ""; 90 | }; 91 | /* End PBXGroup section */ 92 | 93 | /* Begin PBXNativeTarget section */ 94 | 4B97CDF91FB8C0A800F7DEAE /* PinFlexLayoutDemo */ = { 95 | isa = PBXNativeTarget; 96 | buildConfigurationList = 4B97CE0C1FB8C0A800F7DEAE /* Build configuration list for PBXNativeTarget "PinFlexLayoutDemo" */; 97 | buildPhases = ( 98 | AB9129D63F4F3B2C05E6773A /* [CP] Check Pods Manifest.lock */, 99 | 4B97CDF61FB8C0A800F7DEAE /* Sources */, 100 | 4B97CDF71FB8C0A800F7DEAE /* Frameworks */, 101 | 4B97CDF81FB8C0A800F7DEAE /* Resources */, 102 | DEFB6A8BB15E312EAD1C359C /* [CP] Embed Pods Frameworks */, 103 | FDEC35DE2DF751D1DD9FDE80 /* [CP] Copy Pods Resources */, 104 | ); 105 | buildRules = ( 106 | ); 107 | dependencies = ( 108 | ); 109 | name = PinFlexLayoutDemo; 110 | productName = PinFlexLayoutDemo; 111 | productReference = 4B97CDFA1FB8C0A800F7DEAE /* PinFlexLayoutDemo.app */; 112 | productType = "com.apple.product-type.application"; 113 | }; 114 | /* End PBXNativeTarget section */ 115 | 116 | /* Begin PBXProject section */ 117 | 4B97CDF21FB8C0A800F7DEAE /* Project object */ = { 118 | isa = PBXProject; 119 | attributes = { 120 | LastSwiftUpdateCheck = 0910; 121 | LastUpgradeCheck = 0910; 122 | ORGANIZATIONNAME = muukii; 123 | TargetAttributes = { 124 | 4B97CDF91FB8C0A800F7DEAE = { 125 | CreatedOnToolsVersion = 9.1; 126 | ProvisioningStyle = Automatic; 127 | }; 128 | }; 129 | }; 130 | buildConfigurationList = 4B97CDF51FB8C0A800F7DEAE /* Build configuration list for PBXProject "PinFlexLayoutDemo" */; 131 | compatibilityVersion = "Xcode 8.0"; 132 | developmentRegion = en; 133 | hasScannedForEncodings = 0; 134 | knownRegions = ( 135 | en, 136 | Base, 137 | ); 138 | mainGroup = 4B97CDF11FB8C0A800F7DEAE; 139 | productRefGroup = 4B97CDFB1FB8C0A800F7DEAE /* Products */; 140 | projectDirPath = ""; 141 | projectRoot = ""; 142 | targets = ( 143 | 4B97CDF91FB8C0A800F7DEAE /* PinFlexLayoutDemo */, 144 | ); 145 | }; 146 | /* End PBXProject section */ 147 | 148 | /* Begin PBXResourcesBuildPhase section */ 149 | 4B97CDF81FB8C0A800F7DEAE /* Resources */ = { 150 | isa = PBXResourcesBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | 4B97CE081FB8C0A800F7DEAE /* LaunchScreen.storyboard in Resources */, 154 | 4B97CE051FB8C0A800F7DEAE /* Assets.xcassets in Resources */, 155 | 4B97CE031FB8C0A800F7DEAE /* Main.storyboard in Resources */, 156 | ); 157 | runOnlyForDeploymentPostprocessing = 0; 158 | }; 159 | /* End PBXResourcesBuildPhase section */ 160 | 161 | /* Begin PBXShellScriptBuildPhase section */ 162 | AB9129D63F4F3B2C05E6773A /* [CP] Check Pods Manifest.lock */ = { 163 | isa = PBXShellScriptBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | ); 167 | inputPaths = ( 168 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 169 | "${PODS_ROOT}/Manifest.lock", 170 | ); 171 | name = "[CP] Check Pods Manifest.lock"; 172 | outputPaths = ( 173 | "$(DERIVED_FILE_DIR)/Pods-PinFlexLayoutDemo-checkManifestLockResult.txt", 174 | ); 175 | runOnlyForDeploymentPostprocessing = 0; 176 | shellPath = /bin/sh; 177 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 178 | showEnvVarsInLog = 0; 179 | }; 180 | DEFB6A8BB15E312EAD1C359C /* [CP] Embed Pods Frameworks */ = { 181 | isa = PBXShellScriptBuildPhase; 182 | buildActionMask = 2147483647; 183 | files = ( 184 | ); 185 | inputPaths = ( 186 | "${SRCROOT}/Pods/Target Support Files/Pods-PinFlexLayoutDemo/Pods-PinFlexLayoutDemo-frameworks.sh", 187 | "${BUILT_PRODUCTS_DIR}/FlexLayout/FlexLayout.framework", 188 | "${BUILT_PRODUCTS_DIR}/PinLayout/PinLayout.framework", 189 | "${PODS_ROOT}/Reveal-SDK/RevealServer-11/iOS/RevealServer.framework", 190 | "${BUILT_PRODUCTS_DIR}/StackScrollView/StackScrollView.framework", 191 | "${BUILT_PRODUCTS_DIR}/Yoga/yoga.framework", 192 | "${BUILT_PRODUCTS_DIR}/YogaKit/YogaKit.framework", 193 | ); 194 | name = "[CP] Embed Pods Frameworks"; 195 | outputPaths = ( 196 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FlexLayout.framework", 197 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PinLayout.framework", 198 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RevealServer.framework", 199 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/StackScrollView.framework", 200 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/yoga.framework", 201 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YogaKit.framework", 202 | ); 203 | runOnlyForDeploymentPostprocessing = 0; 204 | shellPath = /bin/sh; 205 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PinFlexLayoutDemo/Pods-PinFlexLayoutDemo-frameworks.sh\"\n"; 206 | showEnvVarsInLog = 0; 207 | }; 208 | FDEC35DE2DF751D1DD9FDE80 /* [CP] Copy Pods Resources */ = { 209 | isa = PBXShellScriptBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | ); 213 | inputPaths = ( 214 | ); 215 | name = "[CP] Copy Pods Resources"; 216 | outputPaths = ( 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | shellPath = /bin/sh; 220 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PinFlexLayoutDemo/Pods-PinFlexLayoutDemo-resources.sh\"\n"; 221 | showEnvVarsInLog = 0; 222 | }; 223 | /* End PBXShellScriptBuildPhase section */ 224 | 225 | /* Begin PBXSourcesBuildPhase section */ 226 | 4B97CDF61FB8C0A800F7DEAE /* Sources */ = { 227 | isa = PBXSourcesBuildPhase; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | 4B97CE001FB8C0A800F7DEAE /* ViewController.swift in Sources */, 231 | 4B97CDFE1FB8C0A800F7DEAE /* AppDelegate.swift in Sources */, 232 | ); 233 | runOnlyForDeploymentPostprocessing = 0; 234 | }; 235 | /* End PBXSourcesBuildPhase section */ 236 | 237 | /* Begin PBXVariantGroup section */ 238 | 4B97CE011FB8C0A800F7DEAE /* Main.storyboard */ = { 239 | isa = PBXVariantGroup; 240 | children = ( 241 | 4B97CE021FB8C0A800F7DEAE /* Base */, 242 | ); 243 | name = Main.storyboard; 244 | sourceTree = ""; 245 | }; 246 | 4B97CE061FB8C0A800F7DEAE /* LaunchScreen.storyboard */ = { 247 | isa = PBXVariantGroup; 248 | children = ( 249 | 4B97CE071FB8C0A800F7DEAE /* Base */, 250 | ); 251 | name = LaunchScreen.storyboard; 252 | sourceTree = ""; 253 | }; 254 | /* End PBXVariantGroup section */ 255 | 256 | /* Begin XCBuildConfiguration section */ 257 | 4B97CE0A1FB8C0A800F7DEAE /* Debug */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | ALWAYS_SEARCH_USER_PATHS = NO; 261 | CLANG_ANALYZER_NONNULL = YES; 262 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 263 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 264 | CLANG_CXX_LIBRARY = "libc++"; 265 | CLANG_ENABLE_MODULES = YES; 266 | CLANG_ENABLE_OBJC_ARC = YES; 267 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 268 | CLANG_WARN_BOOL_CONVERSION = YES; 269 | CLANG_WARN_COMMA = YES; 270 | CLANG_WARN_CONSTANT_CONVERSION = YES; 271 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 272 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 273 | CLANG_WARN_EMPTY_BODY = YES; 274 | CLANG_WARN_ENUM_CONVERSION = YES; 275 | CLANG_WARN_INFINITE_RECURSION = YES; 276 | CLANG_WARN_INT_CONVERSION = YES; 277 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 278 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 279 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 280 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 281 | CLANG_WARN_STRICT_PROTOTYPES = YES; 282 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 283 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 284 | CLANG_WARN_UNREACHABLE_CODE = YES; 285 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 286 | CODE_SIGN_IDENTITY = "iPhone Developer"; 287 | COPY_PHASE_STRIP = NO; 288 | DEBUG_INFORMATION_FORMAT = dwarf; 289 | ENABLE_STRICT_OBJC_MSGSEND = YES; 290 | ENABLE_TESTABILITY = YES; 291 | GCC_C_LANGUAGE_STANDARD = gnu11; 292 | GCC_DYNAMIC_NO_PIC = NO; 293 | GCC_NO_COMMON_BLOCKS = YES; 294 | GCC_OPTIMIZATION_LEVEL = 0; 295 | GCC_PREPROCESSOR_DEFINITIONS = ( 296 | "DEBUG=1", 297 | "$(inherited)", 298 | ); 299 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 300 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 301 | GCC_WARN_UNDECLARED_SELECTOR = YES; 302 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 303 | GCC_WARN_UNUSED_FUNCTION = YES; 304 | GCC_WARN_UNUSED_VARIABLE = YES; 305 | IPHONEOS_DEPLOYMENT_TARGET = 11.1; 306 | MTL_ENABLE_DEBUG_INFO = YES; 307 | ONLY_ACTIVE_ARCH = YES; 308 | SDKROOT = iphoneos; 309 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 310 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 311 | }; 312 | name = Debug; 313 | }; 314 | 4B97CE0B1FB8C0A800F7DEAE /* Release */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | ALWAYS_SEARCH_USER_PATHS = NO; 318 | CLANG_ANALYZER_NONNULL = YES; 319 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 321 | CLANG_CXX_LIBRARY = "libc++"; 322 | CLANG_ENABLE_MODULES = YES; 323 | CLANG_ENABLE_OBJC_ARC = YES; 324 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 325 | CLANG_WARN_BOOL_CONVERSION = YES; 326 | CLANG_WARN_COMMA = YES; 327 | CLANG_WARN_CONSTANT_CONVERSION = YES; 328 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 329 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 330 | CLANG_WARN_EMPTY_BODY = YES; 331 | CLANG_WARN_ENUM_CONVERSION = YES; 332 | CLANG_WARN_INFINITE_RECURSION = YES; 333 | CLANG_WARN_INT_CONVERSION = YES; 334 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 335 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 336 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 337 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 338 | CLANG_WARN_STRICT_PROTOTYPES = YES; 339 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 340 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 341 | CLANG_WARN_UNREACHABLE_CODE = YES; 342 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 343 | CODE_SIGN_IDENTITY = "iPhone Developer"; 344 | COPY_PHASE_STRIP = NO; 345 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 346 | ENABLE_NS_ASSERTIONS = NO; 347 | ENABLE_STRICT_OBJC_MSGSEND = YES; 348 | GCC_C_LANGUAGE_STANDARD = gnu11; 349 | GCC_NO_COMMON_BLOCKS = YES; 350 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 351 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 352 | GCC_WARN_UNDECLARED_SELECTOR = YES; 353 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 354 | GCC_WARN_UNUSED_FUNCTION = YES; 355 | GCC_WARN_UNUSED_VARIABLE = YES; 356 | IPHONEOS_DEPLOYMENT_TARGET = 11.1; 357 | MTL_ENABLE_DEBUG_INFO = NO; 358 | SDKROOT = iphoneos; 359 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 360 | VALIDATE_PRODUCT = YES; 361 | }; 362 | name = Release; 363 | }; 364 | 4B97CE0D1FB8C0A800F7DEAE /* Debug */ = { 365 | isa = XCBuildConfiguration; 366 | baseConfigurationReference = 0B691F269169F80216C4753D /* Pods-PinFlexLayoutDemo.debug.xcconfig */; 367 | buildSettings = { 368 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 369 | CODE_SIGN_STYLE = Automatic; 370 | INFOPLIST_FILE = PinFlexLayoutDemo/Info.plist; 371 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 372 | PRODUCT_BUNDLE_IDENTIFIER = me.muukii.PinFlexLayoutDemo; 373 | PRODUCT_NAME = "$(TARGET_NAME)"; 374 | SWIFT_VERSION = 4.0; 375 | TARGETED_DEVICE_FAMILY = "1,2"; 376 | }; 377 | name = Debug; 378 | }; 379 | 4B97CE0E1FB8C0A800F7DEAE /* Release */ = { 380 | isa = XCBuildConfiguration; 381 | baseConfigurationReference = 1E9AD18F8F72E5AB6581C856 /* Pods-PinFlexLayoutDemo.release.xcconfig */; 382 | buildSettings = { 383 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 384 | CODE_SIGN_STYLE = Automatic; 385 | INFOPLIST_FILE = PinFlexLayoutDemo/Info.plist; 386 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 387 | PRODUCT_BUNDLE_IDENTIFIER = me.muukii.PinFlexLayoutDemo; 388 | PRODUCT_NAME = "$(TARGET_NAME)"; 389 | SWIFT_VERSION = 4.0; 390 | TARGETED_DEVICE_FAMILY = "1,2"; 391 | }; 392 | name = Release; 393 | }; 394 | /* End XCBuildConfiguration section */ 395 | 396 | /* Begin XCConfigurationList section */ 397 | 4B97CDF51FB8C0A800F7DEAE /* Build configuration list for PBXProject "PinFlexLayoutDemo" */ = { 398 | isa = XCConfigurationList; 399 | buildConfigurations = ( 400 | 4B97CE0A1FB8C0A800F7DEAE /* Debug */, 401 | 4B97CE0B1FB8C0A800F7DEAE /* Release */, 402 | ); 403 | defaultConfigurationIsVisible = 0; 404 | defaultConfigurationName = Release; 405 | }; 406 | 4B97CE0C1FB8C0A800F7DEAE /* Build configuration list for PBXNativeTarget "PinFlexLayoutDemo" */ = { 407 | isa = XCConfigurationList; 408 | buildConfigurations = ( 409 | 4B97CE0D1FB8C0A800F7DEAE /* Debug */, 410 | 4B97CE0E1FB8C0A800F7DEAE /* Release */, 411 | ); 412 | defaultConfigurationIsVisible = 0; 413 | defaultConfigurationName = Release; 414 | }; 415 | /* End XCConfigurationList section */ 416 | }; 417 | rootObject = 4B97CDF21FB8C0A800F7DEAE /* Project object */; 418 | } 419 | -------------------------------------------------------------------------------- /PinFlexLayoutDemo/PinFlexLayoutDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PinFlexLayoutDemo/PinFlexLayoutDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /PinFlexLayoutDemo/PinFlexLayoutDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // PinFlexLayoutDemo 4 | // 5 | // Created by muukii on 11/13/17. 6 | // Copyright © 2017 muukii. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /PinFlexLayoutDemo/PinFlexLayoutDemo/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 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /PinFlexLayoutDemo/PinFlexLayoutDemo/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 | -------------------------------------------------------------------------------- /PinFlexLayoutDemo/PinFlexLayoutDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /PinFlexLayoutDemo/PinFlexLayoutDemo/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 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /PinFlexLayoutDemo/PinFlexLayoutDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // PinFlexLayoutDemo 4 | // 5 | // Created by muukii on 11/13/17. 6 | // Copyright © 2017 muukii. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import StackScrollView 12 | import PinLayout 13 | import FlexLayout 14 | 15 | class ViewController: UIViewController { 16 | 17 | private let stackScrollView = StackScrollView() 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | var views: [UIView] = [] 23 | 24 | let marginColor = UIColor(white: 0.98, alpha: 1) 25 | 26 | views.append(MarginStackCell(height: 96, backgroundColor: marginColor)) 27 | views.append(FlexLabelStackCell(title: "Hello")) 28 | views.append(PinLabelStackCell(title: "Fooo")) 29 | views.append(DatePickerCell.init()) 30 | views.append(DatePickerCell.init()) 31 | 32 | 33 | stackScrollView.append(views: views) 34 | 35 | stackScrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 36 | stackScrollView.frame = view.bounds 37 | view.addSubview(stackScrollView) 38 | } 39 | } 40 | 41 | class StackCellBase: UIControl, StackCellType { 42 | 43 | init() { 44 | super.init(frame: .zero) 45 | } 46 | 47 | override init(frame: CGRect) { 48 | super.init(frame: frame) 49 | } 50 | 51 | @available(*, unavailable) 52 | required init?(coder aDecoder: NSCoder) { 53 | fatalError("init(coder:) has not been implemented") 54 | } 55 | 56 | } 57 | 58 | final class MarginStackCell: StackCellBase { 59 | 60 | let height: CGFloat 61 | 62 | init(height: CGFloat, backgroundColor: UIColor) { 63 | self.height = height 64 | super.init() 65 | self.backgroundColor = backgroundColor 66 | } 67 | 68 | override var intrinsicContentSize: CGSize { 69 | return CGSize(width: UIViewNoIntrinsicMetric, height: height) 70 | } 71 | } 72 | 73 | // WIP: 74 | final class PinLabelStackCell: StackCellBase, ManualLayoutStackCellType { 75 | 76 | private let label = UILabel() 77 | 78 | init(title: String) { 79 | super.init() 80 | 81 | addSubview(label) 82 | label.font = UIFont.preferredFont(forTextStyle: .body) 83 | label.text = title 84 | } 85 | 86 | override func layoutSubviews() { 87 | super.layoutSubviews() 88 | 89 | layout() 90 | } 91 | 92 | private func layout() { 93 | 94 | label.pin.all().fitSize() 95 | 96 | } 97 | 98 | func size(maxWidth: CGFloat?, maxHeight: CGFloat?) -> CGSize { 99 | 100 | if let maxWidth = maxWidth { 101 | self.pin.width(maxWidth) 102 | } 103 | 104 | layout() 105 | 106 | let size = self.bounds.size 107 | return size 108 | return CGSize(width: size.height, height: 16) 109 | } 110 | } 111 | 112 | final class FlexLabelStackCell: StackCellBase, ManualLayoutStackCellType { 113 | 114 | private let label = UILabel() 115 | 116 | init(title: String) { 117 | super.init() 118 | 119 | self 120 | .flex 121 | .direction(.row) 122 | .define { flex in 123 | flex.addItem(label) 124 | } 125 | 126 | label.font = UIFont.preferredFont(forTextStyle: .body) 127 | label.text = title 128 | } 129 | 130 | override func layoutSubviews() { 131 | super.layoutSubviews() 132 | 133 | layout() 134 | } 135 | 136 | private func layout() { 137 | self.flex.layout(mode: .adjustHeight) 138 | } 139 | 140 | func size(maxWidth: CGFloat?, maxHeight: CGFloat?) -> CGSize { 141 | 142 | if let maxWidth = maxWidth { 143 | self.flex.width(maxWidth) 144 | } 145 | 146 | layout() 147 | 148 | let size = self.bounds.size 149 | return size 150 | } 151 | } 152 | 153 | final class DatePickerCell : StackCellBase, ManualLayoutStackCellType { 154 | 155 | private let datePicker: UIDatePicker = .init() 156 | private let label: UILabel = .init() 157 | private let separator: UIView = .init() 158 | 159 | private var isOn: Bool = false 160 | 161 | override init() { 162 | super.init() 163 | 164 | self.addTarget(self, action: #selector(tap), for: .touchUpInside) 165 | 166 | label.flex.height(40) 167 | separator.flex.height(1 / UIScreen.main.scale) 168 | backgroundColor = UIColor(white: 0.9, alpha: 1) 169 | } 170 | 171 | @objc func tap() { 172 | isOn = !isOn 173 | updateLayout(animated: true) 174 | } 175 | 176 | override func didMoveToSuperview() { 177 | super.didMoveToSuperview() 178 | 179 | superview?.backgroundColor = .black 180 | } 181 | 182 | override func layoutSubviews() { 183 | super.layoutSubviews() 184 | 185 | layout() 186 | } 187 | 188 | private func layout() { 189 | 190 | self.flex.define { flex in 191 | flex.addItem(label) 192 | 193 | flex.addItem(separator) 194 | flex.addItem(datePicker) 195 | 196 | separator.flex.isIncludedInLayout(isOn) 197 | datePicker.flex.isIncludedInLayout(isOn) 198 | 199 | separator.alpha = isOn ? 1 : 0 200 | datePicker.alpha = isOn ? 1 : 0 201 | } 202 | 203 | self.flex.layout(mode: .adjustHeight) 204 | } 205 | 206 | func size(maxWidth: CGFloat?, maxHeight: CGFloat?) -> CGSize { 207 | 208 | if let maxWidth = maxWidth { 209 | self.flex.width(maxWidth) 210 | } 211 | 212 | layout() 213 | 214 | let size = self.bounds.size 215 | return size 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /PinFlexLayoutDemo/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'PinFlexLayoutDemo' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | pod 'StackScrollView', :path => '../' 9 | pod 'PinLayout' 10 | pod 'FlexLayout' 11 | pod 'Reveal-SDK' 12 | # Pods for PinFlexLayoutDemo 13 | 14 | end 15 | -------------------------------------------------------------------------------- /PinFlexLayoutDemo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FlexLayout (1.1.5): 3 | - YogaKit 4 | - PinLayout (1.4.2) 5 | - Reveal-SDK (11) 6 | - StackScrollView (1.1.0) 7 | - Yoga (1.6.0) 8 | - YogaKit (1.6.0): 9 | - Yoga (~> 1.6) 10 | 11 | DEPENDENCIES: 12 | - FlexLayout 13 | - PinLayout 14 | - Reveal-SDK 15 | - StackScrollView (from `../`) 16 | 17 | EXTERNAL SOURCES: 18 | StackScrollView: 19 | :path: ../ 20 | 21 | SPEC CHECKSUMS: 22 | FlexLayout: c3e436554a53336b1285962803a631651fe74db3 23 | PinLayout: f903b9b737fd6c051f046ba7de1ad849edbb5d21 24 | Reveal-SDK: 7fa13d04bb55db61ff2342a990cf731d5159361d 25 | StackScrollView: 42eb0693ef0bb70bd9eca837b7df04c62c1553ab 26 | Yoga: 81670877477311136b1b3f69a6307ce62e1c89cf 27 | YogaKit: 8fe0ddd21673226c0b6077fc3ad5c8fa6cda722e 28 | 29 | PODFILE CHECKSUM: c27183902f5d813581da68fa9eb6ed8f11f53f5b 30 | 31 | COCOAPODS: 1.3.1 32 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'StackScrollView-Demo' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | pod "Reveal-SDK" 8 | pod "EasyPeasy" 9 | #pod "Then" 10 | 11 | # Pods for StackScrollView-Demo 12 | 13 | end 14 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - EasyPeasy (1.10.0) 3 | - Reveal-SDK (32) 4 | 5 | DEPENDENCIES: 6 | - EasyPeasy 7 | - Reveal-SDK 8 | 9 | SPEC REPOS: 10 | trunk: 11 | - EasyPeasy 12 | - Reveal-SDK 13 | 14 | SPEC CHECKSUMS: 15 | EasyPeasy: b9b1bae024d21a7dfa9bad26d6826d88348cc6f1 16 | Reveal-SDK: 559f22118e742b3e7d03cd50350efe21a0d0b3ba 17 | 18 | PODFILE CHECKSUM: 801a5623355ad33f423fde4c805d2477d85eadb4 19 | 20 | COCOAPODS: 1.11.2 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StackScrollView 2 | 3 | [![CI Status](http://img.shields.io/travis/muukii/StackScrollView.svg?style=flat)](https://travis-ci.org/muukii/StackScrollView) 4 | [![Version](https://img.shields.io/cocoapods/v/StackScrollView.svg?style=flat)](http://cocoapods.org/pods/StackScrollView) 5 | [![License](https://img.shields.io/cocoapods/l/StackScrollView.svg?style=flat)](http://cocoapods.org/pods/StackScrollView) 6 | [![Platform](https://img.shields.io/cocoapods/p/StackScrollView.svg?style=flat)](http://cocoapods.org/pods/StackScrollView) 7 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 8 | 9 | 10 | 11 | ⚠️ This sample is using demo-components. 12 | StackScrollView does not have default-components. 13 | StackScrollView is like UIStackView. 14 | So, we need to create the components we need. 15 | 16 | ## What is this? 17 | 18 | StackScrollView builds form UI easily. 19 | 20 | StackScrollView includes UICollectionView. 21 | UICollectionView calculates size of view by AutoLayout, then that display. 22 | (Use `systemLayoutSizeFitting`) 23 | 24 | - We call `StackCell` instead of `Cell` on StackScrollView. 25 | - We no longer need to consider reusing Cells. 26 | - `StackCell` requires constraint based layout. 27 | 28 | ## Usage 29 | 30 | ### Basic usage 31 | 32 | ```swift 33 | let stack = StackScrollView() 34 | 35 | stack.append(view: ...) 36 | 37 | stack.remove(view: ..., animated: true) 38 | ``` 39 | 40 | ### APIs 41 | 42 | #### StackScrollView 43 | 44 | ```swift 45 | func append(view: UIView) 46 | func remove(view: UIView, animated: Bool) 47 | func scroll(to view: UIView, at position: UICollectionViewScrollPosition, animated: Bool) 48 | ``` 49 | 50 | #### StackCellType 51 | 52 | StackScrollView does not required StackCellType. 53 | if `StackCell` has `StackCellType`, be easy that control StackCell. 54 | 55 | ```swift 56 | func scrollToSelf(animated: Bool) 57 | func scrollToSelf(at position: UICollectionViewScrollPosition, animated: Bool) 58 | func updateLayout(animated: Bool) 59 | func remove() 60 | ``` 61 | 62 | *Demo has included this APIs usage.* 63 | 64 | ### Create CustomCell from Code 65 | 66 | *We have to set constraints completely.* 67 | 68 | ```swift 69 | final class LabelStackCell: UIView { 70 | 71 | private let label = UILabel() 72 | 73 | init(title: String) { 74 | super.init(frame: .zero) 75 | 76 | addSubview(label) 77 | label.translatesAutoresizingMaskIntoConstraints = false 78 | 79 | label.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 8).isActive = true 80 | label.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: 8).isActive = true 81 | label.rightAnchor.constraint(equalTo: rightAnchor, constant: 8).isActive = true 82 | label.leftAnchor.constraint(equalTo: leftAnchor, constant: 8).isActive = true 83 | label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true 84 | heightAnchor.constraint(greaterThanOrEqualToConstant: 40).isActive = true 85 | 86 | label.font = UIFont.preferredFont(forTextStyle: .body) 87 | label.text = title 88 | } 89 | } 90 | ``` 91 | 92 | ```swift 93 | let stack = StackScrollView() 94 | stack.append(view: LabelStackCell(title: "Label")) 95 | ``` 96 | 97 | ### Create CustomCell from XIB 98 | 99 | We can use UIView from XIB. 100 | 101 | This framework has `NibLoader`. 102 | It might be useful for you. 103 | 104 | ### Create everything 105 | 106 | You can create any Cell. 107 | Please, check `StackScrollView-Demo` 108 | 109 | ### ManualLayout 110 | 111 | You can create Cell with ManualLayout. 112 | 113 | If you use ManualLayout, the Cell have to use `ManualLayoutStackCellType`. 114 | Then, return self-size based on maximum size in `size(maxWidth:maxHeight)` 115 | 116 | ## Author 117 | 118 | muukii, muukii.app@gmail.com 119 | 120 | ## License 121 | 122 | StackScrollView is available under the MIT license. See the LICENSE file for more info. 123 | -------------------------------------------------------------------------------- /Resources/sample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/StackScrollView/c7d2950cdaee7f4aa0dfd1a1364d352f841c9954/Resources/sample.gif -------------------------------------------------------------------------------- /Resources/shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluidGroup/StackScrollView/c7d2950cdaee7f4aa0dfd1a1364d352f841c9954/Resources/shot.png -------------------------------------------------------------------------------- /StackScrollView-Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // StackScrollView-Demo 4 | // 5 | // Created by muukii on 9/18/16. 6 | // Copyright © 2016 muukii. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /StackScrollView-Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /StackScrollView-Demo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /StackScrollView-Demo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /StackScrollView-Demo/ButtonStackCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ButtonStackCell.swift 3 | // StackScrollView 4 | // 5 | // Created by muukii on 5/2/17. 6 | // Copyright © 2017 muukii. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import EasyPeasy 12 | 13 | final class ButtonStackCell: StackCellBase { 14 | 15 | var tapped: () -> Void = {} 16 | 17 | private let button = UIButton(type: .system) 18 | 19 | init(buttonTitle: String) { 20 | super.init() 21 | 22 | backgroundColor = .white 23 | 24 | button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) 25 | 26 | addSubview(button) 27 | 28 | button <- [ 29 | Center(), 30 | Top(12), 31 | Bottom(12), 32 | ] 33 | 34 | button.setTitle(buttonTitle, for: .normal) 35 | } 36 | 37 | @objc private func buttonTapped() { 38 | tapped() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /StackScrollView-Demo/DatePickerStackCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DatePickerStackCell.swift 3 | // StackScrollView 4 | // 5 | // Created by muukii on 5/2/17. 6 | // Copyright © 2017 muukii. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import EasyPeasy 12 | 13 | import StackScrollView 14 | 15 | final class DatePickerStackCell: TapStackCell { 16 | 17 | let pickerView = UIDatePicker() 18 | let titleLabel = UILabel() 19 | let valueLabel = UILabel() 20 | var editing: Bool = false 21 | 22 | private let borderView = UIView() 23 | private let pickerContainerView = UIView() 24 | private let bodyContainerView = UIView() 25 | 26 | override init() { 27 | 28 | super.init() 29 | 30 | backgroundColor = UIColor.white 31 | 32 | pickerContainerView.clipsToBounds = true 33 | 34 | pickerView.setContentHuggingPriority(.init(100), for: .horizontal) 35 | 36 | pickerContainerView.addSubview(pickerView) 37 | 38 | addSubview(bodyContainerView) 39 | addSubview(borderView) 40 | addSubview(pickerContainerView) 41 | 42 | pickerView <- [ 43 | Top().with(.medium), 44 | Right(), 45 | Left(), 46 | Bottom().with(.medium), 47 | CenterY(), 48 | ] 49 | 50 | bodyContainerView.addSubview(titleLabel) 51 | bodyContainerView.addSubview(valueLabel) 52 | 53 | titleLabel <- [ 54 | Top(>=12), 55 | Bottom(<=12), 56 | Left(16), 57 | CenterY(), 58 | ] 59 | 60 | valueLabel <- [ 61 | Top(>=12), 62 | Bottom(<=12), 63 | Left(>=24).to(titleLabel, .right), 64 | CenterY(), 65 | Right(16), 66 | ] 67 | 68 | bodyContainerView <- [ 69 | Top(), 70 | Right(), 71 | Left(), 72 | Bottom().to(borderView, .top), 73 | ] 74 | 75 | borderView <- [ 76 | Left(16), 77 | Right(16), 78 | Height(1 / UIScreen.main.scale), 79 | Bottom().to(pickerContainerView, .top), 80 | ] 81 | 82 | pickerContainerView <- [ 83 | Right(), 84 | Left(), 85 | Bottom(), 86 | Height(0), 87 | ] 88 | 89 | bodyContainerView.isUserInteractionEnabled = false 90 | 91 | valueLabel.textAlignment = .right 92 | valueLabel.numberOfLines = 0 93 | 94 | borderView.backgroundColor = UIColor(white: 0.95, alpha: 1) 95 | borderView.alpha = 0 96 | } 97 | 98 | override func tap() { 99 | 100 | if editing { 101 | _close() 102 | } else { 103 | _open() 104 | } 105 | 106 | } 107 | 108 | func _close() { 109 | 110 | guard editing == true else { return } 111 | 112 | editing = false 113 | 114 | UIView.animate( 115 | withDuration: 0.5, 116 | delay: 0, 117 | usingSpringWithDamping: 1, 118 | initialSpringVelocity: 0, 119 | options: [ 120 | .beginFromCurrentState, 121 | .allowUserInteraction 122 | ], 123 | animations: { 124 | 125 | self.pickerContainerView <- [ 126 | Height(0), 127 | ] 128 | self.borderView.alpha = 0 129 | self.pickerView.alpha = 0 130 | 131 | self.pickerContainerView.invalidateIntrinsicContentSize() 132 | self.pickerContainerView.layoutIfNeeded() 133 | self.updateLayout(animated: true) 134 | self.scrollToSelf(animated: true) 135 | 136 | }) { (finish) in 137 | 138 | } 139 | 140 | } 141 | 142 | func _open() { 143 | 144 | guard editing == false else { return } 145 | 146 | editing = true 147 | 148 | UIView.animate( 149 | withDuration: 0.5, 150 | delay: 0, 151 | usingSpringWithDamping: 1, 152 | initialSpringVelocity: 0, 153 | options: [ 154 | .beginFromCurrentState, 155 | .allowUserInteraction 156 | ], 157 | animations: { 158 | 159 | NSLayoutConstraint.deactivate( 160 | self.pickerContainerView <- [ 161 | Height(), 162 | ] 163 | ) 164 | self.borderView.alpha = 1 165 | self.pickerView.alpha = 1 166 | 167 | self.pickerContainerView.invalidateIntrinsicContentSize() 168 | self.pickerContainerView.layoutIfNeeded() 169 | self.updateLayout(animated: true) 170 | self.scrollToSelf(animated: true) 171 | 172 | }) { (finish) in 173 | 174 | } 175 | 176 | } 177 | 178 | func set(title: String) { 179 | titleLabel.text = title 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /StackScrollView-Demo/HeaderStackCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeaderStackCell.swift 3 | // StackScrollView 4 | // 5 | // Created by muukii on 5/2/17. 6 | // Copyright © 2017 muukii. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import EasyPeasy 12 | 13 | import StackScrollView 14 | 15 | final class HeaderStackCell: StackCellBase { 16 | 17 | let label = UILabel() 18 | 19 | init(title: String, backgroundColor: UIColor) { 20 | 21 | super.init() 22 | self.backgroundColor = backgroundColor 23 | label.font = UIFont.preferredFont(forTextStyle: .caption1) 24 | addSubview(label) 25 | 26 | label <- [ 27 | Top(4), 28 | Left(8), 29 | Right(16), 30 | Bottom(4), 31 | ] 32 | 33 | label.text = title 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /StackScrollView-Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /StackScrollView-Demo/LabelStackCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LabelStackCell.swift 3 | // StackScrollView 4 | // 5 | // Created by muukii on 5/2/17. 6 | // Copyright © 2017 muukii. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import EasyPeasy 12 | 13 | import StackScrollView 14 | 15 | final class LabelStackCell: StackCellBase { 16 | 17 | private let label = UILabel() 18 | 19 | init(title: String) { 20 | super.init() 21 | 22 | addSubview(label) 23 | 24 | label <- [ 25 | Top(>=8), 26 | Left(8), 27 | Right(8), 28 | Bottom(<=8), 29 | CenterY(), 30 | ] 31 | 32 | self <- [ 33 | Height(>=40), 34 | ] 35 | 36 | label.font = UIFont.preferredFont(forTextStyle: .body) 37 | label.text = title 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /StackScrollView-Demo/MarginStackCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MarginStackCell.swift 3 | // StackScrollView 4 | // 5 | // Created by muukii on 5/2/17. 6 | // Copyright © 2017 muukii. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import StackScrollView 12 | 13 | final class MarginStackCell: StackCellBase { 14 | 15 | let height: CGFloat 16 | 17 | init(height: CGFloat, backgroundColor: UIColor) { 18 | self.height = height 19 | super.init() 20 | self.backgroundColor = backgroundColor 21 | } 22 | 23 | override var intrinsicContentSize: CGSize { 24 | return CGSize(width: UIView.noIntrinsicMetric, height: height) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /StackScrollView-Demo/NibStackCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NibStackCell.swift 3 | // StackScrollView 4 | // 5 | // Created by muukii on 5/17/17. 6 | // Copyright © 2017 muukii. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class NibStackCell: UIView { 12 | 13 | @IBOutlet weak var slider: UISlider! 14 | } 15 | -------------------------------------------------------------------------------- /StackScrollView-Demo/NibStackCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /StackScrollView-Demo/SeparatorStackCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SeparatorStackCell.swift 3 | // StackScrollView 4 | // 5 | // Created by muukii on 5/2/17. 6 | // Copyright © 2017 muukii. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import EasyPeasy 12 | 13 | final class SeparatorStackCell: StackCellBase { 14 | 15 | private let borderView = UIView() 16 | 17 | public override var intrinsicContentSize: CGSize { 18 | return CGSize(width: UIView.noIntrinsicMetric, height: 1 / UIScreen.main.scale) 19 | } 20 | 21 | public init( 22 | leftMargin: CGFloat = 0, 23 | rightMargin: CGFloat = 0, 24 | backgroundColor: UIColor = UIColor.white, 25 | separatorColor: UIColor = UIColor(white: 0, alpha: 0.2)) { 26 | 27 | super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 1 / UIScreen.main.scale)) 28 | 29 | self.backgroundColor = backgroundColor 30 | borderView.backgroundColor = separatorColor 31 | addSubview(borderView) 32 | 33 | borderView <- [ 34 | Top(), 35 | Left(leftMargin), 36 | Right(rightMargin), 37 | Height(1 / UIScreen.main.scale), 38 | Bottom() 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /StackScrollView-Demo/StackCellBase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StackCellBase.swift 3 | // StackScrollView 4 | // 5 | // Created by muukii on 5/2/17. 6 | // Copyright © 2017 muukii. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import StackScrollView 12 | 13 | class StackCellBase: UIControl, StackCellType { 14 | 15 | init() { 16 | super.init(frame: .zero) 17 | } 18 | 19 | override init(frame: CGRect) { 20 | super.init(frame: frame) 21 | } 22 | 23 | @available(*, unavailable) 24 | required init?(coder aDecoder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /StackScrollView-Demo/SwitchStackCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwitchStackCell.swift 3 | // StackScrollView 4 | // 5 | // Created by muukii on 5/2/17. 6 | // Copyright © 2017 muukii. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import EasyPeasy 12 | 13 | final class SwitchStackCell: StackCellBase { 14 | 15 | var valueChanged: (Bool) -> Void = { _ in } 16 | 17 | let titleLabel = UILabel() 18 | private let switchView = UISwitch() 19 | 20 | init(title: String) { 21 | super.init() 22 | 23 | backgroundColor = .white 24 | 25 | addSubview(titleLabel) 26 | addSubview(switchView) 27 | 28 | titleLabel <- [ 29 | Top(>=12), 30 | Bottom(<=12), 31 | Left(16), 32 | CenterY(), 33 | ] 34 | 35 | switchView <- [ 36 | Left(12).to(titleLabel, .right), 37 | Top(>=0), 38 | Right(16), 39 | CenterY(), 40 | Bottom(<=0), 41 | ] 42 | 43 | switchView.addTarget(self, action: #selector(switchValueChanged), for: .valueChanged) 44 | 45 | titleLabel.font = UIFont.preferredFont(forTextStyle: .body) 46 | 47 | titleLabel.text = title 48 | } 49 | 50 | @objc private func switchValueChanged() { 51 | 52 | valueChanged(switchView.isOn) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /StackScrollView-Demo/TapStackCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TapStackCell.swift 3 | // StackScrollView 4 | // 5 | // Created by muukii on 5/2/17. 6 | // Copyright © 2017 muukii. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class TapStackCell: StackCellBase { 12 | 13 | var highlightedBackgroundColor: UIColor = .init(white: 0.95, alpha: 1) 14 | var normalBackgroundColor: UIColor = .white 15 | 16 | override init() { 17 | super.init() 18 | 19 | backgroundColor = .white 20 | addTarget(self, action: #selector(tap), for: .touchUpInside) 21 | } 22 | 23 | @objc func tap() { 24 | 25 | } 26 | 27 | override var isHighlighted: Bool { 28 | didSet { 29 | 30 | guard oldValue != isHighlighted else { return } 31 | 32 | UIView.animate(withDuration: 0.2, delay: 0, options: [.beginFromCurrentState, .allowUserInteraction], animations: { 33 | 34 | if self.isHighlighted { 35 | 36 | self.backgroundColor = self.highlightedBackgroundColor 37 | } else { 38 | 39 | self.backgroundColor = self.normalBackgroundColor 40 | } 41 | }, completion: nil) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /StackScrollView-Demo/TextFieldStackCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TextFieldStackCell.swift 3 | // StackScrollView 4 | // 5 | // Created by muukii on 5/2/17. 6 | // Copyright © 2017 muukii. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import EasyPeasy 12 | 13 | import StackScrollView 14 | 15 | final class TextFieldStackCell: StackCellBase { 16 | 17 | private let textField = UITextField() 18 | 19 | override init() { 20 | super.init() 21 | 22 | textField.font = UIFont.preferredFont(forTextStyle: .body) 23 | 24 | addSubview(textField) 25 | textField <- Edges(UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)) 26 | 27 | self <- Height(>=40) 28 | } 29 | 30 | func set(value: String) { 31 | textField.text = value 32 | } 33 | 34 | func set(placeholder: String) { 35 | textField.placeholder = placeholder 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /StackScrollView-Demo/TextViewStackCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TextViewStackCell.swift 3 | // StackScrollView 4 | // 5 | // Created by muukii on 5/2/17. 6 | // Copyright © 2017 muukii. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import EasyPeasy 12 | 13 | import StackScrollView 14 | 15 | final class TextViewStackCell: StackCellBase, UITextViewDelegate { 16 | 17 | private let textView = UITextView() 18 | 19 | override init() { 20 | super.init() 21 | 22 | addSubview(textView) 23 | textView.easy.layout(Edges(UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8))) 24 | 25 | self.easy.layout(Height(>=40)) 26 | 27 | textView.font = UIFont.preferredFont(forTextStyle: .body) 28 | textView.isScrollEnabled = false 29 | textView.delegate = self 30 | } 31 | 32 | func set(value: String) { 33 | 34 | textView.text = value 35 | } 36 | 37 | func textViewDidChange(_ textView: UITextView) { 38 | updateLayout(animated: true) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /StackScrollView-Demo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // StackScrollView 4 | // 5 | // Created by muukii on 08/29/2016. 6 | // Copyright (c) 2016 muukii. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import StackScrollView 12 | import EasyPeasy 13 | 14 | class ViewController: UIViewController { 15 | 16 | private let stackScrollView = StackScrollView() 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | var views: [UIView] = [] 22 | 23 | let marginColor = UIColor(white: 0.98, alpha: 1) 24 | 25 | views.append(MarginStackCell(height: 96, backgroundColor: marginColor)) 26 | 27 | views.append(HeaderStackCell(title: "LabelStackCell", backgroundColor: marginColor)) 28 | 29 | views.append(LabelStackCell(title: "Label")) 30 | 31 | views.append(MarginStackCell(height: 40, backgroundColor: marginColor)) 32 | 33 | views.append(HeaderStackCell(title: "TextFieldStackCell", backgroundColor: marginColor)) 34 | 35 | views.append(fullSeparator()) 36 | 37 | views.append({ 38 | let v = TextFieldStackCell() 39 | v.set(placeholder: "Title") 40 | return v 41 | }()) 42 | 43 | views.append(semiSeparator()) 44 | 45 | views.append({ 46 | let v = TextFieldStackCell() 47 | v.set(placeholder: "Detail") 48 | return v 49 | }()) 50 | 51 | views.append(fullSeparator()) 52 | 53 | views.append(MarginStackCell(height: 40, backgroundColor: marginColor)) 54 | 55 | views.append(HeaderStackCell(title: "DatePickerStackCell", backgroundColor: marginColor)) 56 | 57 | views.append(fullSeparator()) 58 | 59 | views.append({ 60 | let v = DatePickerStackCell() 61 | v.set(title: "Date") 62 | return v 63 | }()) 64 | 65 | views.append(fullSeparator()) 66 | 67 | views.append(MarginStackCell(height: 40, backgroundColor: marginColor)) 68 | 69 | views.append(HeaderStackCell(title: "TextViewStackCell", backgroundColor: marginColor)) 70 | 71 | views.append(fullSeparator()) 72 | 73 | views.append(TextViewStackCell()) 74 | 75 | views.append(fullSeparator()) 76 | 77 | views.append(MarginStackCell(height: 40, backgroundColor: marginColor)) 78 | 79 | views.append(HeaderStackCell(title: "SwitchStackCell", backgroundColor: marginColor)) 80 | 81 | (0..<3).forEach { i in 82 | 83 | let s = fullSeparator() 84 | views.append(s) 85 | views.append(SwitchStackCell(title: "Switch \(i)")) 86 | } 87 | 88 | views.append(fullSeparator()) 89 | 90 | views.append(MarginStackCell(height: 40, backgroundColor: marginColor)) 91 | 92 | views.append(HeaderStackCell(title: "ButtonStackCell", backgroundColor: marginColor)) 93 | 94 | let makeRemovableButton: () -> [UIView] = { 95 | 96 | let s = self.fullSeparator() 97 | 98 | var views: [UIView] = [] 99 | views.append(s) 100 | 101 | views.append({ 102 | let v = ButtonStackCell(buttonTitle: "Remove") 103 | v.tapped = { [unowned v] in 104 | v.remove() 105 | s.remove() 106 | } 107 | return v 108 | }()) 109 | return views 110 | } 111 | 112 | views.append(contentsOf: { () -> [UIView] in 113 | let s = fullSeparator() 114 | let v = ButtonStackCell(buttonTitle: "Insert Before") 115 | v.tapped = { [unowned stackScrollView, unowned s] in 116 | let views = (0 ... .random(in: 1 ... 2)).flatMap { _ in makeRemovableButton() } 117 | stackScrollView.insert(views: views, before: s, animated: true) 118 | } 119 | return [s, v] 120 | }()) 121 | 122 | views.append(contentsOf: { () -> [UIView] in 123 | let s = fullSeparator() 124 | let v = ButtonStackCell(buttonTitle: "Insert After") 125 | v.tapped = { [unowned stackScrollView, unowned v] in 126 | let views = (0 ... .random(in: 1 ... 2)).flatMap { _ in makeRemovableButton() } 127 | stackScrollView.insert(views: views, after: v, animated: true) 128 | } 129 | return [s, v] 130 | }()) 131 | 132 | views.append(fullSeparator()) 133 | 134 | views.append(MarginStackCell(height: 40, backgroundColor: marginColor)) 135 | 136 | views.append(HeaderStackCell(title: "MarginStackCell", backgroundColor: marginColor)) 137 | 138 | views.append(MarginStackCell(height: 40, backgroundColor: marginColor)) 139 | 140 | views.append(HeaderStackCell(title: "SeparatorStackCell", backgroundColor: marginColor)) 141 | 142 | views.append(fullSeparator()) 143 | 144 | views.append(MarginStackCell(height: 40, backgroundColor: marginColor)) 145 | 146 | do { 147 | // Load from XIB 148 | 149 | let cell = NibLoader().load() 150 | 151 | views.append(fullSeparator()) 152 | views.append(cell) 153 | views.append(fullSeparator()) 154 | 155 | } 156 | 157 | views.append(MarginStackCell(height: 40, backgroundColor: marginColor)) 158 | 159 | stackScrollView.append(views: views) 160 | 161 | stackScrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 162 | stackScrollView.frame = view.bounds 163 | view.addSubview(stackScrollView) 164 | } 165 | 166 | private func fullSeparator() -> SeparatorStackCell { 167 | return SeparatorStackCell(leftMargin: 0, rightMargin: 0, backgroundColor: .clear, separatorColor: UIColor(white: 0.90, alpha: 1)) 168 | } 169 | 170 | private func semiSeparator() -> SeparatorStackCell { 171 | return SeparatorStackCell(leftMargin: 8, rightMargin: 8, backgroundColor: .clear, separatorColor: UIColor(white: 0.90, alpha: 1)) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /StackScrollView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'StackScrollView' 3 | s.version = '1.6.3' 4 | s.summary = 'Scalable form builder with UICollectionView' 5 | s.homepage = 'https://github.com/muukii/StackScrollView' 6 | s.license = { :type => 'MIT', :file => 'LICENSE' } 7 | s.author = { 'muukii' => 'muukii.app@gmail.com' } 8 | s.source = { :git => 'https://github.com/muukii/StackScrollView.git', :tag => s.version.to_s } 9 | s.social_media_url = 'https://twitter.com/muukii0803' 10 | 11 | s.ios.deployment_target = '9.0' 12 | s.source_files = 'StackScrollView/**/*.swift' 13 | s.swift_versions = ["5.3", "5.4", "5.5"] 14 | end 15 | -------------------------------------------------------------------------------- /StackScrollView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4B1B51BA1ECB85C70034A3D9 /* NibLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1B51B91ECB85C70034A3D9 /* NibLoader.swift */; }; 11 | 4B1B51BC1ECB88230034A3D9 /* NibStackCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B1B51BB1ECB88220034A3D9 /* NibStackCell.xib */; }; 12 | 4B1B51BE1ECB882D0034A3D9 /* NibStackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1B51BD1ECB882D0034A3D9 /* NibStackCell.swift */; }; 13 | 4B6C2D9E1D8DA3FB003D3A46 /* StackScrollView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6C2D9C1D8DA3FB003D3A46 /* StackScrollView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 14 | 4B6C2DA61D8DA42A003D3A46 /* StackScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6C2DA41D8DA42A003D3A46 /* StackScrollView.swift */; }; 15 | 4B6C2DA71D8DA42A003D3A46 /* StackCellType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6C2DA51D8DA42A003D3A46 /* StackCellType.swift */; }; 16 | 4B6C2DB21D8DA68E003D3A46 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6C2DB11D8DA68E003D3A46 /* AppDelegate.swift */; }; 17 | 4B6C2DB41D8DA68E003D3A46 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6C2DB31D8DA68E003D3A46 /* ViewController.swift */; }; 18 | 4B6C2DB71D8DA68E003D3A46 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4B6C2DB51D8DA68E003D3A46 /* Main.storyboard */; }; 19 | 4B6C2DB91D8DA68E003D3A46 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4B6C2DB81D8DA68E003D3A46 /* Assets.xcassets */; }; 20 | 4B6C2DBC1D8DA68E003D3A46 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4B6C2DBA1D8DA68E003D3A46 /* LaunchScreen.storyboard */; }; 21 | 4B83ACCD1EB7C00700F92569 /* DatePickerStackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83ACCC1EB7C00700F92569 /* DatePickerStackCell.swift */; }; 22 | 4B83ACCF1EB7C00E00F92569 /* LabelStackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83ACCE1EB7C00E00F92569 /* LabelStackCell.swift */; }; 23 | 4B83ACD11EB7C01600F92569 /* SeparatorStackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83ACD01EB7C01600F92569 /* SeparatorStackCell.swift */; }; 24 | 4B83ACD31EB7C02000F92569 /* TextViewStackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83ACD21EB7C02000F92569 /* TextViewStackCell.swift */; }; 25 | 4B83ACD51EB7C02C00F92569 /* SwitchStackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83ACD41EB7C02C00F92569 /* SwitchStackCell.swift */; }; 26 | 4B83ACD71EB7C0C500F92569 /* ButtonStackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83ACD61EB7C0C500F92569 /* ButtonStackCell.swift */; }; 27 | 4B83ACD91EB7C0CB00F92569 /* MarginStackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83ACD81EB7C0CB00F92569 /* MarginStackCell.swift */; }; 28 | 4B83ACDB1EB7C20700F92569 /* StackCellBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83ACDA1EB7C20700F92569 /* StackCellBase.swift */; }; 29 | 4B83ACDD1EB8578400F92569 /* HeaderStackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83ACDC1EB8578400F92569 /* HeaderStackCell.swift */; }; 30 | 4B83ACDF1EB8580F00F92569 /* TextFieldStackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83ACDE1EB8580F00F92569 /* TextFieldStackCell.swift */; }; 31 | 4B83ACE11EB85EB200F92569 /* TapStackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83ACE01EB85EB200F92569 /* TapStackCell.swift */; }; 32 | 4B83ACE31EB8756800F92569 /* StackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83ACE21EB8756800F92569 /* StackCell.swift */; }; 33 | 4BE8B6ED1D8E5A9700A4DC33 /* StackScrollView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B6C2D991D8DA3FB003D3A46 /* StackScrollView.framework */; }; 34 | 4BE8B6EE1D8E5A9700A4DC33 /* StackScrollView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4B6C2D991D8DA3FB003D3A46 /* StackScrollView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 35 | 9E10463014A8095D5EC331C8 /* Pods_StackScrollView_Demo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDB7BDA1DD524C3580ED5E34 /* Pods_StackScrollView_Demo.framework */; }; 36 | /* End PBXBuildFile section */ 37 | 38 | /* Begin PBXContainerItemProxy section */ 39 | 4BE8B6EF1D8E5A9700A4DC33 /* PBXContainerItemProxy */ = { 40 | isa = PBXContainerItemProxy; 41 | containerPortal = 4B6C2D901D8DA3FB003D3A46 /* Project object */; 42 | proxyType = 1; 43 | remoteGlobalIDString = 4B6C2D981D8DA3FB003D3A46; 44 | remoteInfo = StackScrollView; 45 | }; 46 | /* End PBXContainerItemProxy section */ 47 | 48 | /* Begin PBXCopyFilesBuildPhase section */ 49 | 4BE8B6F11D8E5A9700A4DC33 /* Embed Frameworks */ = { 50 | isa = PBXCopyFilesBuildPhase; 51 | buildActionMask = 2147483647; 52 | dstPath = ""; 53 | dstSubfolderSpec = 10; 54 | files = ( 55 | 4BE8B6EE1D8E5A9700A4DC33 /* StackScrollView.framework in Embed Frameworks */, 56 | ); 57 | name = "Embed Frameworks"; 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | /* End PBXCopyFilesBuildPhase section */ 61 | 62 | /* Begin PBXFileReference section */ 63 | 107B190065E8096D8802EE36 /* Pods-StackScrollView-Demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StackScrollView-Demo.release.xcconfig"; path = "Pods/Target Support Files/Pods-StackScrollView-Demo/Pods-StackScrollView-Demo.release.xcconfig"; sourceTree = ""; }; 64 | 4B1B51B91ECB85C70034A3D9 /* NibLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NibLoader.swift; sourceTree = ""; }; 65 | 4B1B51BB1ECB88220034A3D9 /* NibStackCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NibStackCell.xib; sourceTree = ""; }; 66 | 4B1B51BD1ECB882D0034A3D9 /* NibStackCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NibStackCell.swift; sourceTree = ""; }; 67 | 4B6C2D991D8DA3FB003D3A46 /* StackScrollView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = StackScrollView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | 4B6C2D9C1D8DA3FB003D3A46 /* StackScrollView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StackScrollView.h; sourceTree = ""; }; 69 | 4B6C2D9D1D8DA3FB003D3A46 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 70 | 4B6C2DA41D8DA42A003D3A46 /* StackScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackScrollView.swift; sourceTree = ""; }; 71 | 4B6C2DA51D8DA42A003D3A46 /* StackCellType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackCellType.swift; sourceTree = ""; }; 72 | 4B6C2DA91D8DA439003D3A46 /* EasyPeasy.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = EasyPeasy.framework; path = Carthage/Build/iOS/EasyPeasy.framework; sourceTree = ""; }; 73 | 4B6C2DAF1D8DA68E003D3A46 /* StackScrollView-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "StackScrollView-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 74 | 4B6C2DB11D8DA68E003D3A46 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 75 | 4B6C2DB31D8DA68E003D3A46 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 76 | 4B6C2DB61D8DA68E003D3A46 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 77 | 4B6C2DB81D8DA68E003D3A46 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 78 | 4B6C2DBB1D8DA68E003D3A46 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 79 | 4B6C2DBD1D8DA68E003D3A46 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 80 | 4B83ACCC1EB7C00700F92569 /* DatePickerStackCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatePickerStackCell.swift; sourceTree = ""; }; 81 | 4B83ACCE1EB7C00E00F92569 /* LabelStackCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelStackCell.swift; sourceTree = ""; }; 82 | 4B83ACD01EB7C01600F92569 /* SeparatorStackCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SeparatorStackCell.swift; sourceTree = ""; }; 83 | 4B83ACD21EB7C02000F92569 /* TextViewStackCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextViewStackCell.swift; sourceTree = ""; }; 84 | 4B83ACD41EB7C02C00F92569 /* SwitchStackCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwitchStackCell.swift; sourceTree = ""; }; 85 | 4B83ACD61EB7C0C500F92569 /* ButtonStackCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ButtonStackCell.swift; sourceTree = ""; }; 86 | 4B83ACD81EB7C0CB00F92569 /* MarginStackCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarginStackCell.swift; sourceTree = ""; }; 87 | 4B83ACDA1EB7C20700F92569 /* StackCellBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackCellBase.swift; sourceTree = ""; }; 88 | 4B83ACDC1EB8578400F92569 /* HeaderStackCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderStackCell.swift; sourceTree = ""; }; 89 | 4B83ACDE1EB8580F00F92569 /* TextFieldStackCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldStackCell.swift; sourceTree = ""; }; 90 | 4B83ACE01EB85EB200F92569 /* TapStackCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TapStackCell.swift; sourceTree = ""; }; 91 | 4B83ACE21EB8756800F92569 /* StackCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StackCell.swift; sourceTree = ""; }; 92 | 4BE8B6F51D8E5AF200A4DC33 /* Then.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Then.framework; path = Carthage/Build/iOS/Then.framework; sourceTree = ""; }; 93 | 5BBF590042534B380EB634CE /* Pods-StackScrollView-Demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StackScrollView-Demo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-StackScrollView-Demo/Pods-StackScrollView-Demo.debug.xcconfig"; sourceTree = ""; }; 94 | EDB7BDA1DD524C3580ED5E34 /* Pods_StackScrollView_Demo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_StackScrollView_Demo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 95 | /* End PBXFileReference section */ 96 | 97 | /* Begin PBXFrameworksBuildPhase section */ 98 | 4B6C2D951D8DA3FB003D3A46 /* Frameworks */ = { 99 | isa = PBXFrameworksBuildPhase; 100 | buildActionMask = 2147483647; 101 | files = ( 102 | ); 103 | runOnlyForDeploymentPostprocessing = 0; 104 | }; 105 | 4B6C2DAC1D8DA68E003D3A46 /* Frameworks */ = { 106 | isa = PBXFrameworksBuildPhase; 107 | buildActionMask = 2147483647; 108 | files = ( 109 | 4BE8B6ED1D8E5A9700A4DC33 /* StackScrollView.framework in Frameworks */, 110 | 9E10463014A8095D5EC331C8 /* Pods_StackScrollView_Demo.framework in Frameworks */, 111 | ); 112 | runOnlyForDeploymentPostprocessing = 0; 113 | }; 114 | /* End PBXFrameworksBuildPhase section */ 115 | 116 | /* Begin PBXGroup section */ 117 | 4B6C2D8F1D8DA3FB003D3A46 = { 118 | isa = PBXGroup; 119 | children = ( 120 | 4B6C2D9B1D8DA3FB003D3A46 /* StackScrollView */, 121 | 4B6C2DB01D8DA68E003D3A46 /* StackScrollView-Demo */, 122 | 4B6C2D9A1D8DA3FB003D3A46 /* Products */, 123 | 4B6C2DA81D8DA439003D3A46 /* Frameworks */, 124 | B5611D3702B81D46FD45EAFC /* Pods */, 125 | ); 126 | indentWidth = 2; 127 | sourceTree = ""; 128 | tabWidth = 2; 129 | }; 130 | 4B6C2D9A1D8DA3FB003D3A46 /* Products */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 4B6C2D991D8DA3FB003D3A46 /* StackScrollView.framework */, 134 | 4B6C2DAF1D8DA68E003D3A46 /* StackScrollView-Demo.app */, 135 | ); 136 | name = Products; 137 | sourceTree = ""; 138 | }; 139 | 4B6C2D9B1D8DA3FB003D3A46 /* StackScrollView */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 4B6C2DA41D8DA42A003D3A46 /* StackScrollView.swift */, 143 | 4B6C2DA51D8DA42A003D3A46 /* StackCellType.swift */, 144 | 4B83ACE21EB8756800F92569 /* StackCell.swift */, 145 | 4B1B51B91ECB85C70034A3D9 /* NibLoader.swift */, 146 | 4B6C2D9C1D8DA3FB003D3A46 /* StackScrollView.h */, 147 | 4B6C2D9D1D8DA3FB003D3A46 /* Info.plist */, 148 | ); 149 | path = StackScrollView; 150 | sourceTree = ""; 151 | }; 152 | 4B6C2DA81D8DA439003D3A46 /* Frameworks */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 4BE8B6F51D8E5AF200A4DC33 /* Then.framework */, 156 | 4B6C2DA91D8DA439003D3A46 /* EasyPeasy.framework */, 157 | EDB7BDA1DD524C3580ED5E34 /* Pods_StackScrollView_Demo.framework */, 158 | ); 159 | name = Frameworks; 160 | sourceTree = ""; 161 | }; 162 | 4B6C2DB01D8DA68E003D3A46 /* StackScrollView-Demo */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 4B6C2DB11D8DA68E003D3A46 /* AppDelegate.swift */, 166 | 4B6C2DB31D8DA68E003D3A46 /* ViewController.swift */, 167 | 4B6C2DB51D8DA68E003D3A46 /* Main.storyboard */, 168 | 4B6C2DB81D8DA68E003D3A46 /* Assets.xcassets */, 169 | 4B6C2DBA1D8DA68E003D3A46 /* LaunchScreen.storyboard */, 170 | 4B6C2DBD1D8DA68E003D3A46 /* Info.plist */, 171 | 4B83ACDA1EB7C20700F92569 /* StackCellBase.swift */, 172 | 4B83ACE01EB85EB200F92569 /* TapStackCell.swift */, 173 | 4B83ACCC1EB7C00700F92569 /* DatePickerStackCell.swift */, 174 | 4B83ACCE1EB7C00E00F92569 /* LabelStackCell.swift */, 175 | 4B83ACD01EB7C01600F92569 /* SeparatorStackCell.swift */, 176 | 4B83ACD21EB7C02000F92569 /* TextViewStackCell.swift */, 177 | 4B83ACDE1EB8580F00F92569 /* TextFieldStackCell.swift */, 178 | 4B83ACD41EB7C02C00F92569 /* SwitchStackCell.swift */, 179 | 4B83ACD61EB7C0C500F92569 /* ButtonStackCell.swift */, 180 | 4B83ACD81EB7C0CB00F92569 /* MarginStackCell.swift */, 181 | 4B83ACDC1EB8578400F92569 /* HeaderStackCell.swift */, 182 | 4B1B51BD1ECB882D0034A3D9 /* NibStackCell.swift */, 183 | 4B1B51BB1ECB88220034A3D9 /* NibStackCell.xib */, 184 | ); 185 | path = "StackScrollView-Demo"; 186 | sourceTree = ""; 187 | }; 188 | B5611D3702B81D46FD45EAFC /* Pods */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | 5BBF590042534B380EB634CE /* Pods-StackScrollView-Demo.debug.xcconfig */, 192 | 107B190065E8096D8802EE36 /* Pods-StackScrollView-Demo.release.xcconfig */, 193 | ); 194 | name = Pods; 195 | sourceTree = ""; 196 | }; 197 | /* End PBXGroup section */ 198 | 199 | /* Begin PBXHeadersBuildPhase section */ 200 | 4B6C2D961D8DA3FB003D3A46 /* Headers */ = { 201 | isa = PBXHeadersBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | 4B6C2D9E1D8DA3FB003D3A46 /* StackScrollView.h in Headers */, 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | }; 208 | /* End PBXHeadersBuildPhase section */ 209 | 210 | /* Begin PBXNativeTarget section */ 211 | 4B6C2D981D8DA3FB003D3A46 /* StackScrollView */ = { 212 | isa = PBXNativeTarget; 213 | buildConfigurationList = 4B6C2DA11D8DA3FB003D3A46 /* Build configuration list for PBXNativeTarget "StackScrollView" */; 214 | buildPhases = ( 215 | 4B6C2D941D8DA3FB003D3A46 /* Sources */, 216 | 4B6C2D951D8DA3FB003D3A46 /* Frameworks */, 217 | 4B6C2D961D8DA3FB003D3A46 /* Headers */, 218 | 4B6C2D971D8DA3FB003D3A46 /* Resources */, 219 | ); 220 | buildRules = ( 221 | ); 222 | dependencies = ( 223 | ); 224 | name = StackScrollView; 225 | productName = StackScrollView; 226 | productReference = 4B6C2D991D8DA3FB003D3A46 /* StackScrollView.framework */; 227 | productType = "com.apple.product-type.framework"; 228 | }; 229 | 4B6C2DAE1D8DA68E003D3A46 /* StackScrollView-Demo */ = { 230 | isa = PBXNativeTarget; 231 | buildConfigurationList = 4B6C2DC01D8DA68E003D3A46 /* Build configuration list for PBXNativeTarget "StackScrollView-Demo" */; 232 | buildPhases = ( 233 | 85921EDC7C6ECDDC05B65CD4 /* [CP] Check Pods Manifest.lock */, 234 | 4B6C2DAB1D8DA68E003D3A46 /* Sources */, 235 | 4B6C2DAC1D8DA68E003D3A46 /* Frameworks */, 236 | 4B6C2DAD1D8DA68E003D3A46 /* Resources */, 237 | 4BE8B6F11D8E5A9700A4DC33 /* Embed Frameworks */, 238 | 24154DE9833A821AC3AEA6F2 /* [CP] Embed Pods Frameworks */, 239 | ); 240 | buildRules = ( 241 | ); 242 | dependencies = ( 243 | 4BE8B6F01D8E5A9700A4DC33 /* PBXTargetDependency */, 244 | ); 245 | name = "StackScrollView-Demo"; 246 | productName = "StackScrollView-Demo"; 247 | productReference = 4B6C2DAF1D8DA68E003D3A46 /* StackScrollView-Demo.app */; 248 | productType = "com.apple.product-type.application"; 249 | }; 250 | /* End PBXNativeTarget section */ 251 | 252 | /* Begin PBXProject section */ 253 | 4B6C2D901D8DA3FB003D3A46 /* Project object */ = { 254 | isa = PBXProject; 255 | attributes = { 256 | LastSwiftUpdateCheck = 0800; 257 | LastUpgradeCheck = 0800; 258 | ORGANIZATIONNAME = muukii; 259 | TargetAttributes = { 260 | 4B6C2D981D8DA3FB003D3A46 = { 261 | CreatedOnToolsVersion = 8.0; 262 | DevelopmentTeam = KU2QEJ9K3Z; 263 | LastSwiftMigration = 0800; 264 | ProvisioningStyle = Automatic; 265 | }; 266 | 4B6C2DAE1D8DA68E003D3A46 = { 267 | CreatedOnToolsVersion = 8.0; 268 | DevelopmentTeam = KU2QEJ9K3Z; 269 | LastSwiftMigration = 0800; 270 | ProvisioningStyle = Automatic; 271 | }; 272 | }; 273 | }; 274 | buildConfigurationList = 4B6C2D931D8DA3FB003D3A46 /* Build configuration list for PBXProject "StackScrollView" */; 275 | compatibilityVersion = "Xcode 3.2"; 276 | developmentRegion = English; 277 | hasScannedForEncodings = 0; 278 | knownRegions = ( 279 | English, 280 | en, 281 | Base, 282 | ); 283 | mainGroup = 4B6C2D8F1D8DA3FB003D3A46; 284 | productRefGroup = 4B6C2D9A1D8DA3FB003D3A46 /* Products */; 285 | projectDirPath = ""; 286 | projectRoot = ""; 287 | targets = ( 288 | 4B6C2D981D8DA3FB003D3A46 /* StackScrollView */, 289 | 4B6C2DAE1D8DA68E003D3A46 /* StackScrollView-Demo */, 290 | ); 291 | }; 292 | /* End PBXProject section */ 293 | 294 | /* Begin PBXResourcesBuildPhase section */ 295 | 4B6C2D971D8DA3FB003D3A46 /* Resources */ = { 296 | isa = PBXResourcesBuildPhase; 297 | buildActionMask = 2147483647; 298 | files = ( 299 | ); 300 | runOnlyForDeploymentPostprocessing = 0; 301 | }; 302 | 4B6C2DAD1D8DA68E003D3A46 /* Resources */ = { 303 | isa = PBXResourcesBuildPhase; 304 | buildActionMask = 2147483647; 305 | files = ( 306 | 4B1B51BC1ECB88230034A3D9 /* NibStackCell.xib in Resources */, 307 | 4B6C2DBC1D8DA68E003D3A46 /* LaunchScreen.storyboard in Resources */, 308 | 4B6C2DB91D8DA68E003D3A46 /* Assets.xcassets in Resources */, 309 | 4B6C2DB71D8DA68E003D3A46 /* Main.storyboard in Resources */, 310 | ); 311 | runOnlyForDeploymentPostprocessing = 0; 312 | }; 313 | /* End PBXResourcesBuildPhase section */ 314 | 315 | /* Begin PBXShellScriptBuildPhase section */ 316 | 24154DE9833A821AC3AEA6F2 /* [CP] Embed Pods Frameworks */ = { 317 | isa = PBXShellScriptBuildPhase; 318 | buildActionMask = 2147483647; 319 | files = ( 320 | ); 321 | inputPaths = ( 322 | "${PODS_ROOT}/Target Support Files/Pods-StackScrollView-Demo/Pods-StackScrollView-Demo-frameworks.sh", 323 | "${BUILT_PRODUCTS_DIR}/EasyPeasy/EasyPeasy.framework", 324 | "${PODS_XCFRAMEWORKS_BUILD_DIR}/Reveal-SDK/RevealServer.framework/RevealServer", 325 | ); 326 | name = "[CP] Embed Pods Frameworks"; 327 | outputPaths = ( 328 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/EasyPeasy.framework", 329 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RevealServer.framework", 330 | ); 331 | runOnlyForDeploymentPostprocessing = 0; 332 | shellPath = /bin/sh; 333 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-StackScrollView-Demo/Pods-StackScrollView-Demo-frameworks.sh\"\n"; 334 | showEnvVarsInLog = 0; 335 | }; 336 | 85921EDC7C6ECDDC05B65CD4 /* [CP] Check Pods Manifest.lock */ = { 337 | isa = PBXShellScriptBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | ); 341 | inputPaths = ( 342 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 343 | "${PODS_ROOT}/Manifest.lock", 344 | ); 345 | name = "[CP] Check Pods Manifest.lock"; 346 | outputPaths = ( 347 | "$(DERIVED_FILE_DIR)/Pods-StackScrollView-Demo-checkManifestLockResult.txt", 348 | ); 349 | runOnlyForDeploymentPostprocessing = 0; 350 | shellPath = /bin/sh; 351 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 352 | showEnvVarsInLog = 0; 353 | }; 354 | /* End PBXShellScriptBuildPhase section */ 355 | 356 | /* Begin PBXSourcesBuildPhase section */ 357 | 4B6C2D941D8DA3FB003D3A46 /* Sources */ = { 358 | isa = PBXSourcesBuildPhase; 359 | buildActionMask = 2147483647; 360 | files = ( 361 | 4B83ACE31EB8756800F92569 /* StackCell.swift in Sources */, 362 | 4B6C2DA71D8DA42A003D3A46 /* StackCellType.swift in Sources */, 363 | 4B1B51BA1ECB85C70034A3D9 /* NibLoader.swift in Sources */, 364 | 4B6C2DA61D8DA42A003D3A46 /* StackScrollView.swift in Sources */, 365 | ); 366 | runOnlyForDeploymentPostprocessing = 0; 367 | }; 368 | 4B6C2DAB1D8DA68E003D3A46 /* Sources */ = { 369 | isa = PBXSourcesBuildPhase; 370 | buildActionMask = 2147483647; 371 | files = ( 372 | 4B83ACD31EB7C02000F92569 /* TextViewStackCell.swift in Sources */, 373 | 4B83ACD11EB7C01600F92569 /* SeparatorStackCell.swift in Sources */, 374 | 4B83ACD71EB7C0C500F92569 /* ButtonStackCell.swift in Sources */, 375 | 4B83ACDB1EB7C20700F92569 /* StackCellBase.swift in Sources */, 376 | 4B83ACD51EB7C02C00F92569 /* SwitchStackCell.swift in Sources */, 377 | 4B6C2DB41D8DA68E003D3A46 /* ViewController.swift in Sources */, 378 | 4B83ACDF1EB8580F00F92569 /* TextFieldStackCell.swift in Sources */, 379 | 4B83ACCF1EB7C00E00F92569 /* LabelStackCell.swift in Sources */, 380 | 4B6C2DB21D8DA68E003D3A46 /* AppDelegate.swift in Sources */, 381 | 4B83ACD91EB7C0CB00F92569 /* MarginStackCell.swift in Sources */, 382 | 4B83ACCD1EB7C00700F92569 /* DatePickerStackCell.swift in Sources */, 383 | 4B83ACE11EB85EB200F92569 /* TapStackCell.swift in Sources */, 384 | 4B1B51BE1ECB882D0034A3D9 /* NibStackCell.swift in Sources */, 385 | 4B83ACDD1EB8578400F92569 /* HeaderStackCell.swift in Sources */, 386 | ); 387 | runOnlyForDeploymentPostprocessing = 0; 388 | }; 389 | /* End PBXSourcesBuildPhase section */ 390 | 391 | /* Begin PBXTargetDependency section */ 392 | 4BE8B6F01D8E5A9700A4DC33 /* PBXTargetDependency */ = { 393 | isa = PBXTargetDependency; 394 | target = 4B6C2D981D8DA3FB003D3A46 /* StackScrollView */; 395 | targetProxy = 4BE8B6EF1D8E5A9700A4DC33 /* PBXContainerItemProxy */; 396 | }; 397 | /* End PBXTargetDependency section */ 398 | 399 | /* Begin PBXVariantGroup section */ 400 | 4B6C2DB51D8DA68E003D3A46 /* Main.storyboard */ = { 401 | isa = PBXVariantGroup; 402 | children = ( 403 | 4B6C2DB61D8DA68E003D3A46 /* Base */, 404 | ); 405 | name = Main.storyboard; 406 | sourceTree = ""; 407 | }; 408 | 4B6C2DBA1D8DA68E003D3A46 /* LaunchScreen.storyboard */ = { 409 | isa = PBXVariantGroup; 410 | children = ( 411 | 4B6C2DBB1D8DA68E003D3A46 /* Base */, 412 | ); 413 | name = LaunchScreen.storyboard; 414 | sourceTree = ""; 415 | }; 416 | /* End PBXVariantGroup section */ 417 | 418 | /* Begin XCBuildConfiguration section */ 419 | 4B6C2D9F1D8DA3FB003D3A46 /* Debug */ = { 420 | isa = XCBuildConfiguration; 421 | buildSettings = { 422 | ALWAYS_SEARCH_USER_PATHS = NO; 423 | CLANG_ANALYZER_NONNULL = YES; 424 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 425 | CLANG_CXX_LIBRARY = "libc++"; 426 | CLANG_ENABLE_MODULES = YES; 427 | CLANG_ENABLE_OBJC_ARC = YES; 428 | CLANG_WARN_BOOL_CONVERSION = YES; 429 | CLANG_WARN_CONSTANT_CONVERSION = YES; 430 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 431 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 432 | CLANG_WARN_EMPTY_BODY = YES; 433 | CLANG_WARN_ENUM_CONVERSION = YES; 434 | CLANG_WARN_INFINITE_RECURSION = YES; 435 | CLANG_WARN_INT_CONVERSION = YES; 436 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 437 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 438 | CLANG_WARN_UNREACHABLE_CODE = YES; 439 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 440 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 441 | COPY_PHASE_STRIP = NO; 442 | CURRENT_PROJECT_VERSION = 1; 443 | DEBUG_INFORMATION_FORMAT = dwarf; 444 | ENABLE_STRICT_OBJC_MSGSEND = YES; 445 | ENABLE_TESTABILITY = YES; 446 | GCC_C_LANGUAGE_STANDARD = gnu99; 447 | GCC_DYNAMIC_NO_PIC = NO; 448 | GCC_NO_COMMON_BLOCKS = YES; 449 | GCC_OPTIMIZATION_LEVEL = 0; 450 | GCC_PREPROCESSOR_DEFINITIONS = ( 451 | "DEBUG=1", 452 | "$(inherited)", 453 | ); 454 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 455 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 456 | GCC_WARN_UNDECLARED_SELECTOR = YES; 457 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 458 | GCC_WARN_UNUSED_FUNCTION = YES; 459 | GCC_WARN_UNUSED_VARIABLE = YES; 460 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 461 | MTL_ENABLE_DEBUG_INFO = YES; 462 | ONLY_ACTIVE_ARCH = YES; 463 | SDKROOT = iphoneos; 464 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 465 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 466 | SWIFT_VERSION = 5.0; 467 | TARGETED_DEVICE_FAMILY = "1,2"; 468 | VERSIONING_SYSTEM = "apple-generic"; 469 | VERSION_INFO_PREFIX = ""; 470 | }; 471 | name = Debug; 472 | }; 473 | 4B6C2DA01D8DA3FB003D3A46 /* Release */ = { 474 | isa = XCBuildConfiguration; 475 | buildSettings = { 476 | ALWAYS_SEARCH_USER_PATHS = NO; 477 | CLANG_ANALYZER_NONNULL = YES; 478 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 479 | CLANG_CXX_LIBRARY = "libc++"; 480 | CLANG_ENABLE_MODULES = YES; 481 | CLANG_ENABLE_OBJC_ARC = YES; 482 | CLANG_WARN_BOOL_CONVERSION = YES; 483 | CLANG_WARN_CONSTANT_CONVERSION = YES; 484 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 485 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 486 | CLANG_WARN_EMPTY_BODY = YES; 487 | CLANG_WARN_ENUM_CONVERSION = YES; 488 | CLANG_WARN_INFINITE_RECURSION = YES; 489 | CLANG_WARN_INT_CONVERSION = YES; 490 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 491 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 492 | CLANG_WARN_UNREACHABLE_CODE = YES; 493 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 494 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 495 | COPY_PHASE_STRIP = NO; 496 | CURRENT_PROJECT_VERSION = 1; 497 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 498 | ENABLE_NS_ASSERTIONS = NO; 499 | ENABLE_STRICT_OBJC_MSGSEND = YES; 500 | GCC_C_LANGUAGE_STANDARD = gnu99; 501 | GCC_NO_COMMON_BLOCKS = YES; 502 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 503 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 504 | GCC_WARN_UNDECLARED_SELECTOR = YES; 505 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 506 | GCC_WARN_UNUSED_FUNCTION = YES; 507 | GCC_WARN_UNUSED_VARIABLE = YES; 508 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 509 | MTL_ENABLE_DEBUG_INFO = NO; 510 | SDKROOT = iphoneos; 511 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 512 | SWIFT_VERSION = 5.0; 513 | TARGETED_DEVICE_FAMILY = "1,2"; 514 | VALIDATE_PRODUCT = YES; 515 | VERSIONING_SYSTEM = "apple-generic"; 516 | VERSION_INFO_PREFIX = ""; 517 | }; 518 | name = Release; 519 | }; 520 | 4B6C2DA21D8DA3FB003D3A46 /* Debug */ = { 521 | isa = XCBuildConfiguration; 522 | buildSettings = { 523 | CLANG_ENABLE_MODULES = YES; 524 | CODE_SIGN_IDENTITY = ""; 525 | DEFINES_MODULE = YES; 526 | DEVELOPMENT_TEAM = KU2QEJ9K3Z; 527 | DYLIB_COMPATIBILITY_VERSION = 1; 528 | DYLIB_CURRENT_VERSION = 1; 529 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 530 | FRAMEWORK_SEARCH_PATHS = ( 531 | "$(inherited)", 532 | "$(PROJECT_DIR)/Carthage/Build/iOS", 533 | ); 534 | INFOPLIST_FILE = StackScrollView/Info.plist; 535 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 536 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 537 | PRODUCT_BUNDLE_IDENTIFIER = me.muukii.StackScrollView; 538 | PRODUCT_NAME = "$(TARGET_NAME)"; 539 | SKIP_INSTALL = YES; 540 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 541 | }; 542 | name = Debug; 543 | }; 544 | 4B6C2DA31D8DA3FB003D3A46 /* Release */ = { 545 | isa = XCBuildConfiguration; 546 | buildSettings = { 547 | CLANG_ENABLE_CODE_COVERAGE = NO; 548 | CLANG_ENABLE_MODULES = YES; 549 | CODE_SIGN_IDENTITY = ""; 550 | DEFINES_MODULE = YES; 551 | DEVELOPMENT_TEAM = KU2QEJ9K3Z; 552 | DYLIB_COMPATIBILITY_VERSION = 1; 553 | DYLIB_CURRENT_VERSION = 1; 554 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 555 | FRAMEWORK_SEARCH_PATHS = ( 556 | "$(inherited)", 557 | "$(PROJECT_DIR)/Carthage/Build/iOS", 558 | ); 559 | INFOPLIST_FILE = StackScrollView/Info.plist; 560 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 561 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 562 | PRODUCT_BUNDLE_IDENTIFIER = me.muukii.StackScrollView; 563 | PRODUCT_NAME = "$(TARGET_NAME)"; 564 | SKIP_INSTALL = YES; 565 | }; 566 | name = Release; 567 | }; 568 | 4B6C2DBE1D8DA68E003D3A46 /* Debug */ = { 569 | isa = XCBuildConfiguration; 570 | baseConfigurationReference = 5BBF590042534B380EB634CE /* Pods-StackScrollView-Demo.debug.xcconfig */; 571 | buildSettings = { 572 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 573 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 574 | DEVELOPMENT_TEAM = KU2QEJ9K3Z; 575 | INFOPLIST_FILE = "StackScrollView-Demo/Info.plist"; 576 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 577 | PRODUCT_BUNDLE_IDENTIFIER = "me.muukii.StackScrollView-Demo"; 578 | PRODUCT_NAME = "$(TARGET_NAME)"; 579 | }; 580 | name = Debug; 581 | }; 582 | 4B6C2DBF1D8DA68E003D3A46 /* Release */ = { 583 | isa = XCBuildConfiguration; 584 | baseConfigurationReference = 107B190065E8096D8802EE36 /* Pods-StackScrollView-Demo.release.xcconfig */; 585 | buildSettings = { 586 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 587 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 588 | DEVELOPMENT_TEAM = KU2QEJ9K3Z; 589 | INFOPLIST_FILE = "StackScrollView-Demo/Info.plist"; 590 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 591 | PRODUCT_BUNDLE_IDENTIFIER = "me.muukii.StackScrollView-Demo"; 592 | PRODUCT_NAME = "$(TARGET_NAME)"; 593 | }; 594 | name = Release; 595 | }; 596 | /* End XCBuildConfiguration section */ 597 | 598 | /* Begin XCConfigurationList section */ 599 | 4B6C2D931D8DA3FB003D3A46 /* Build configuration list for PBXProject "StackScrollView" */ = { 600 | isa = XCConfigurationList; 601 | buildConfigurations = ( 602 | 4B6C2D9F1D8DA3FB003D3A46 /* Debug */, 603 | 4B6C2DA01D8DA3FB003D3A46 /* Release */, 604 | ); 605 | defaultConfigurationIsVisible = 0; 606 | defaultConfigurationName = Release; 607 | }; 608 | 4B6C2DA11D8DA3FB003D3A46 /* Build configuration list for PBXNativeTarget "StackScrollView" */ = { 609 | isa = XCConfigurationList; 610 | buildConfigurations = ( 611 | 4B6C2DA21D8DA3FB003D3A46 /* Debug */, 612 | 4B6C2DA31D8DA3FB003D3A46 /* Release */, 613 | ); 614 | defaultConfigurationIsVisible = 0; 615 | defaultConfigurationName = Release; 616 | }; 617 | 4B6C2DC01D8DA68E003D3A46 /* Build configuration list for PBXNativeTarget "StackScrollView-Demo" */ = { 618 | isa = XCConfigurationList; 619 | buildConfigurations = ( 620 | 4B6C2DBE1D8DA68E003D3A46 /* Debug */, 621 | 4B6C2DBF1D8DA68E003D3A46 /* Release */, 622 | ); 623 | defaultConfigurationIsVisible = 0; 624 | defaultConfigurationName = Release; 625 | }; 626 | /* End XCConfigurationList section */ 627 | }; 628 | rootObject = 4B6C2D901D8DA3FB003D3A46 /* Project object */; 629 | } 630 | -------------------------------------------------------------------------------- /StackScrollView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /StackScrollView.xcodeproj/xcshareddata/xcschemes/StackScrollView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /StackScrollView.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /StackScrollView.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /StackScrollView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /StackScrollView/NibLoader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NibLoader.swift 3 | // 4 | // Copyright (c) 2017 muukii 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | import UIKit 25 | 26 | public struct NibLoader { 27 | 28 | public let name: String? 29 | public let bundle: Bundle? 30 | 31 | public init(name: String? = nil, bundle: Bundle? = nil) { 32 | self.name = name 33 | self.bundle = bundle 34 | } 35 | 36 | func nib() -> UINib { 37 | return UINib(nibName: name ?? String(describing: T.self), bundle: bundle ?? Bundle(for: T.self)) 38 | } 39 | 40 | public func load() -> T { 41 | let nib = self.nib() 42 | guard let view = nib.instantiate(withOwner: nil, options: nil).first as? T else { 43 | fatalError("The nib \(nib) expected its root view to be of type \(T.self)") 44 | } 45 | return view 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /StackScrollView/StackCell.swift: -------------------------------------------------------------------------------- 1 | 2 | import Foundation 3 | import UIKit 4 | 5 | open class StackCell: UIView, StackCellType { 6 | 7 | open var shouldAnimateLayoutChanges: Bool = true 8 | 9 | open override func invalidateIntrinsicContentSize() { 10 | super.invalidateIntrinsicContentSize() 11 | updateLayout(animated: shouldAnimateLayoutChanges) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /StackScrollView/StackCellType.swift: -------------------------------------------------------------------------------- 1 | // StackCellType.swift 2 | // 3 | // Copyright (c) 2016 muukii 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | public protocol StackCellType : AnyObject { 26 | 27 | } 28 | 29 | public protocol ManualLayoutStackCellType : StackCellType { 30 | 31 | func size(maxWidth: CGFloat?, maxHeight: CGFloat?) -> CGSize 32 | } 33 | 34 | extension StackCellType where Self : UIView { 35 | 36 | public var stackScrollView: StackScrollView? { 37 | var superview: UIView? = self 38 | 39 | while superview != nil && !(superview is StackScrollView) { 40 | superview = superview?.superview 41 | } 42 | 43 | return superview as? StackScrollView 44 | } 45 | 46 | public func scrollToSelf(animated: Bool) { 47 | 48 | stackScrollView?.scroll(to: self, at: .centeredVertically, animated: animated) 49 | } 50 | 51 | public func scrollToSelf(at position: UICollectionView.ScrollPosition, animated: Bool) { 52 | stackScrollView?.scroll(to: self, at: position, animated: animated) 53 | } 54 | 55 | public func updateLayout(animated: Bool) { 56 | invalidateIntrinsicContentSize() 57 | stackScrollView?.updateLayout(animated: animated) 58 | } 59 | 60 | public func remove() { 61 | stackScrollView?.remove(view: self, animated: true) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /StackScrollView/StackScrollView.h: -------------------------------------------------------------------------------- 1 | // 2 | // StackScrollView.h 3 | // StackScrollView 4 | // 5 | // Created by muukii on 9/18/16. 6 | // Copyright © 2016 muukii. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for StackScrollView. 12 | FOUNDATION_EXPORT double StackScrollViewVersionNumber; 13 | 14 | //! Project version string for StackScrollView. 15 | FOUNDATION_EXPORT const unsigned char StackScrollViewVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /StackScrollView/StackScrollView.swift: -------------------------------------------------------------------------------- 1 | // StackScrollView.swift 2 | // 3 | // Copyright (c) 2016 muukii 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | open class StackScrollView: UICollectionView, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { 26 | 27 | private enum LayoutKeys { 28 | static let top = "me.muukii.StackScrollView.top" 29 | static let right = "me.muukii.StackScrollView.right" 30 | static let left = "me.muukii.StackScrollView.left" 31 | static let bottom = "me.muukii.StackScrollView.bottom" 32 | static let width = "me.muukii.StackScrollView.width" 33 | static let height = "me.muukii.StackScrollView.height" 34 | } 35 | 36 | private static func defaultLayout() -> UICollectionViewFlowLayout { 37 | let layout = UICollectionViewFlowLayout() 38 | layout.minimumLineSpacing = 0 39 | layout.minimumInteritemSpacing = 0 40 | layout.sectionInset = .zero 41 | return layout 42 | } 43 | 44 | @available(*, unavailable) 45 | open override var dataSource: UICollectionViewDataSource? { 46 | didSet { 47 | } 48 | } 49 | 50 | @available(*, unavailable) 51 | open override var delegate: UICollectionViewDelegate? { 52 | didSet { 53 | } 54 | } 55 | 56 | private var direction: UICollectionView.ScrollDirection { 57 | (collectionViewLayout as! UICollectionViewFlowLayout).scrollDirection 58 | } 59 | 60 | // MARK: - Initializers 61 | 62 | public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { 63 | super.init(frame: frame, collectionViewLayout: layout) 64 | setup() 65 | } 66 | 67 | public convenience init(frame: CGRect) { 68 | self.init(frame: frame, collectionViewLayout: StackScrollView.defaultLayout()) 69 | setup() 70 | } 71 | 72 | public required init?(coder aDecoder: NSCoder) { 73 | super.init(coder: aDecoder) 74 | setup() 75 | } 76 | 77 | private(set) public var views: [UIView] = [] 78 | 79 | private func identifier(_ v: UIView) -> String { 80 | return v.hashValue.description 81 | } 82 | 83 | private func setup() { 84 | 85 | backgroundColor = .white 86 | 87 | switch direction { 88 | case .vertical: 89 | alwaysBounceVertical = true 90 | case .horizontal: 91 | alwaysBounceHorizontal = true 92 | @unknown default: 93 | fatalError() 94 | } 95 | 96 | delaysContentTouches = false 97 | keyboardDismissMode = .interactive 98 | backgroundColor = .clear 99 | 100 | super.delegate = self 101 | super.dataSource = self 102 | } 103 | 104 | open override func touchesShouldCancel(in view: UIView) -> Bool { 105 | return true 106 | } 107 | 108 | open func append(view: UIView) { 109 | 110 | views.append(view) 111 | register(Cell.self, forCellWithReuseIdentifier: identifier(view)) 112 | reloadData() 113 | } 114 | 115 | open func append(views _views: [UIView]) { 116 | 117 | views += _views 118 | _views.forEach { view in 119 | register(Cell.self, forCellWithReuseIdentifier: identifier(view)) 120 | } 121 | reloadData() 122 | } 123 | 124 | @available(*, unavailable, message: "Unimplemented") 125 | func append(lazy: @escaping () -> UIView) { 126 | 127 | } 128 | 129 | open func insert(views _views: [UIView], at index: Int, animated: Bool, completion: @escaping () -> Void) { 130 | 131 | layoutIfNeeded() 132 | 133 | var _views = _views 134 | _views.removeAll(where: views.contains(_:)) 135 | views.insert(contentsOf: _views, at: index) 136 | _views.forEach { view in 137 | register(Cell.self, forCellWithReuseIdentifier: identifier(view)) 138 | } 139 | let batchUpdates: () -> Void = { 140 | self.performBatchUpdates({ 141 | self.insertItems(at: (index ..< index.advanced(by: _views.count)).map({ IndexPath(item: $0, section: 0) })) 142 | }, completion: { _ in completion() }) 143 | } 144 | if animated { 145 | UIView.animate( 146 | withDuration: 0.5, 147 | delay: 0, 148 | usingSpringWithDamping: 1, 149 | initialSpringVelocity: 0, 150 | options: [ 151 | .beginFromCurrentState, 152 | .allowUserInteraction, 153 | .overrideInheritedCurve, 154 | .overrideInheritedOptions, 155 | .overrideInheritedDuration 156 | ], 157 | animations: batchUpdates, 158 | completion: nil) 159 | 160 | } else { 161 | UIView.performWithoutAnimation(batchUpdates) 162 | } 163 | } 164 | 165 | open func insert(views _views: [UIView], before view: UIView, animated: Bool, completion: @escaping () -> Void = {}) { 166 | 167 | guard let index = views.firstIndex(of: view) else { 168 | completion() 169 | return 170 | } 171 | insert(views: _views, at: index, animated: animated, completion: completion) 172 | } 173 | 174 | open func insert(views _views: [UIView], after view: UIView, animated: Bool, completion: @escaping () -> Void = {}) { 175 | 176 | guard let index = views.firstIndex(of: view)?.advanced(by: 1) else { 177 | completion() 178 | return 179 | } 180 | insert(views: _views, at: index, animated: animated, completion: completion) 181 | } 182 | 183 | open func remove(view: UIView, animated: Bool, completion: @escaping () -> Void = {}) { 184 | 185 | layoutIfNeeded() 186 | 187 | if let index = views.firstIndex(of: view) { 188 | views.remove(at: index) 189 | if animated { 190 | UIView.animate( 191 | withDuration: 0.5, 192 | delay: 0, 193 | usingSpringWithDamping: 1, 194 | initialSpringVelocity: 0, 195 | options: [ 196 | .beginFromCurrentState, 197 | .allowUserInteraction, 198 | .overrideInheritedCurve, 199 | .overrideInheritedOptions, 200 | .overrideInheritedDuration 201 | ], 202 | animations: { 203 | self.performBatchUpdates({ 204 | self.deleteItems(at: [IndexPath(item: index, section: 0)]) 205 | }, completion: { _ in completion() }) 206 | }) { (finish) in 207 | 208 | } 209 | 210 | } else { 211 | UIView.performWithoutAnimation { 212 | performBatchUpdates({ 213 | self.deleteItems(at: [IndexPath(item: index, section: 0)]) 214 | }, completion: { _ in completion() }) 215 | } 216 | } 217 | } 218 | } 219 | 220 | open func remove(views: [UIView], animated: Bool, completion: @escaping () -> Void = {}) { 221 | 222 | layoutIfNeeded() 223 | 224 | var indicesForRemove: [Int] = [] 225 | 226 | for view in views { 227 | if let index = self.views.firstIndex(of: view) { 228 | indicesForRemove.append(index) 229 | } 230 | } 231 | 232 | // It seems that the layout is not updated properly unless the order is aligned. 233 | indicesForRemove.sort(by: >) 234 | 235 | for index in indicesForRemove { 236 | self.views.remove(at: index) 237 | } 238 | 239 | if animated { 240 | UIView.animate( 241 | withDuration: 0.5, 242 | delay: 0, 243 | usingSpringWithDamping: 1, 244 | initialSpringVelocity: 0, 245 | options: [ 246 | .beginFromCurrentState, 247 | .allowUserInteraction, 248 | .overrideInheritedCurve, 249 | .overrideInheritedOptions, 250 | .overrideInheritedDuration 251 | ], 252 | animations: { 253 | self.performBatchUpdates({ 254 | self.deleteItems(at: indicesForRemove.map { IndexPath.init(item: $0, section: 0) }) 255 | }, completion: { _ in completion() }) 256 | }) 257 | } else { 258 | UIView.performWithoutAnimation { 259 | performBatchUpdates({ 260 | self.deleteItems(at: indicesForRemove.map { IndexPath.init(item: $0, section: 0) }) 261 | }, completion: { _ in completion() }) 262 | } 263 | } 264 | } 265 | 266 | /// This method might fail if the view is out of bounds. Use `func scroll(to view: UIView, at position: UICollectionView.ScrollPosition, animated: Bool)` instead 267 | @available(*, deprecated, message: "Use `scroll(to view: UIView, at position: UICollectionView.ScrollPosition, animated: Bool)` instead") 268 | open func scroll(to view: UIView, animated: Bool) { 269 | 270 | let targetRect = view.convert(view.bounds, to: self) 271 | scrollRectToVisible(targetRect, animated: true) 272 | } 273 | 274 | open func scroll(to view: UIView, at position: UICollectionView.ScrollPosition, animated: Bool) { 275 | if let index = views.firstIndex(of: view) { 276 | scrollToItem(at: IndexPath(item: index, section: 0), at: position, animated: animated) 277 | } 278 | } 279 | 280 | public func numberOfSections(in collectionView: UICollectionView) -> Int { 281 | return 1 282 | } 283 | 284 | public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 285 | return views.count 286 | } 287 | 288 | public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 289 | 290 | let view = views[indexPath.item] 291 | let _identifier = identifier(view) 292 | 293 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: _identifier, for: indexPath) 294 | 295 | if view.superview == cell.contentView { 296 | return cell 297 | } 298 | 299 | precondition(cell.contentView.subviews.isEmpty) 300 | 301 | if view is ManualLayoutStackCellType { 302 | 303 | cell.contentView.addSubview(view) 304 | 305 | } else { 306 | 307 | view.translatesAutoresizingMaskIntoConstraints = false 308 | cell.contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 309 | 310 | cell.contentView.addSubview(view) 311 | 312 | let top = view.topAnchor.constraint(equalTo: cell.contentView.topAnchor) 313 | let right = view.rightAnchor.constraint(equalTo: cell.contentView.rightAnchor) 314 | let bottom = view.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor) 315 | let left = view.leftAnchor.constraint(equalTo: cell.contentView.leftAnchor) 316 | 317 | top.identifier = LayoutKeys.top 318 | right.identifier = LayoutKeys.right 319 | bottom.identifier = LayoutKeys.bottom 320 | left.identifier = LayoutKeys.left 321 | 322 | NSLayoutConstraint.activate([ 323 | top, 324 | right, 325 | bottom, 326 | left, 327 | ]) 328 | } 329 | 330 | return cell 331 | } 332 | 333 | public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 334 | 335 | let view = views[indexPath.item] 336 | 337 | if let view = view as? ManualLayoutStackCellType { 338 | 339 | switch direction { 340 | case .vertical: 341 | return view.size(maxWidth: collectionView.bounds.width, maxHeight: nil) 342 | case .horizontal: 343 | return view.size(maxWidth: nil, maxHeight: collectionView.bounds.height) 344 | @unknown default: 345 | fatalError() 346 | } 347 | 348 | } else { 349 | 350 | switch direction { 351 | case .vertical: 352 | 353 | let width: NSLayoutConstraint = { 354 | 355 | guard let c = view.constraints.first(where: { $0.identifier == LayoutKeys.width }) else { 356 | let width = view.widthAnchor.constraint(equalToConstant: collectionView.bounds.width) 357 | width.identifier = LayoutKeys.width 358 | width.isActive = true 359 | return width 360 | } 361 | 362 | return c 363 | }() 364 | 365 | width.constant = collectionView.bounds.width 366 | 367 | let size = view.superview?.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) ?? view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) 368 | 369 | return size 370 | 371 | case .horizontal: 372 | 373 | let heightConstraint: NSLayoutConstraint = { 374 | 375 | guard let c = view.constraints.first(where: { $0.identifier == LayoutKeys.height }) else { 376 | let heightConstraint = view.heightAnchor.constraint(equalToConstant: collectionView.bounds.height) 377 | heightConstraint.identifier = LayoutKeys.height 378 | heightConstraint.isActive = true 379 | return heightConstraint 380 | } 381 | 382 | return c 383 | }() 384 | 385 | heightConstraint.constant = collectionView.bounds.height 386 | 387 | let size = view.superview?.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) ?? view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) 388 | 389 | return size 390 | 391 | @unknown default: 392 | fatalError() 393 | } 394 | 395 | } 396 | } 397 | 398 | public func updateLayout(animated: Bool) { 399 | 400 | if animated { 401 | UIView.animate( 402 | withDuration: 0.5, 403 | delay: 0, 404 | usingSpringWithDamping: 1, 405 | initialSpringVelocity: 0, 406 | options: [ 407 | .beginFromCurrentState, 408 | .allowUserInteraction, 409 | .overrideInheritedCurve, 410 | .overrideInheritedOptions, 411 | .overrideInheritedDuration 412 | ], 413 | animations: { 414 | self.performBatchUpdates(nil, completion: nil) 415 | self.layoutIfNeeded() 416 | }) { (finish) in 417 | 418 | } 419 | } else { 420 | UIView.performWithoutAnimation { 421 | self.performBatchUpdates(nil, completion: nil) 422 | self.layoutIfNeeded() 423 | } 424 | } 425 | } 426 | 427 | final class Cell: UICollectionViewCell { 428 | 429 | override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { 430 | return layoutAttributes 431 | } 432 | } 433 | } 434 | --------------------------------------------------------------------------------