├── BubbleCollectionViewLayout.xcworkspace ├── contents.xcworkspacedata ├── xcshareddata │ └── IDEWorkspaceChecks.plist └── xcuserdata │ ├── mac-0007.xcuserdatad │ └── UserInterfaceState.xcuserstate │ ├── mind-0023.xcuserdatad │ ├── UserInterfaceState.xcuserstate │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── mind.xcuserdatad │ └── UserInterfaceState.xcuserstate ├── LICENSE ├── ObjectiveCDemo ├── ObjectiveCDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ │ ├── mind-0023.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ │ │ └── mind.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ ├── mac-0007.xcuserdatad │ │ └── xcschemes │ │ │ └── xcschememanagement.plist │ │ ├── mind-0023.xcuserdatad │ │ └── xcschemes │ │ │ └── xcschememanagement.plist │ │ └── mind.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── ObjectiveCDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── Images │ │ ├── Contents.json │ │ └── chocolate.imageset │ │ │ ├── Contents.json │ │ │ └── chocolate.jpg │ ├── close_circle.imageset │ │ ├── Contents.json │ │ └── close_circle.png │ └── close_circle_selected.imageset │ │ ├── Contents.json │ │ └── close_circle_selected.png │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Class │ ├── MIBubbleCollectionViewCell.h │ ├── MIBubbleCollectionViewCell.m │ ├── MICollectionViewBubbleLayout.h │ ├── MICollectionViewBubbleLayout.m │ ├── MIMainViewController.h │ └── MIMainViewController.m │ ├── Info.plist │ └── main.m ├── README.md └── SwiftDemo ├── SwiftDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ ├── mind-0023.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── mind.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── mac-0007.xcuserdatad │ └── xcschemes │ │ └── xcschememanagement.plist │ ├── mind-0023.xcuserdatad │ └── xcschemes │ │ └── xcschememanagement.plist │ └── mind.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist └── SwiftDemo ├── AppDelegate.swift ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Class ├── HeaderView.swift ├── MIBubbleCollectionViewCell.swift ├── MICollectionViewBubbleLayout.swift ├── MIMainViewController.swift └── RoundImageView.swift ├── Info.plist ├── Model └── Item.swift └── Resources ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json ├── Contents.json ├── Images │ ├── Contents.json │ └── chocolate.imageset │ │ ├── Contents.json │ │ └── chocolate.jpg ├── close_circle.imageset │ ├── Contents.json │ └── close_circle.png └── close_circle_selected.imageset │ ├── Contents.json │ └── close_circle_selected.png └── ItemResources.swift /BubbleCollectionViewLayout.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /BubbleCollectionViewLayout.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BubbleCollectionViewLayout.xcworkspace/xcuserdata/mac-0007.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/BubbleCollectionViewLayout/af879244f8aaf6dafb46c88f7a542df9e77326ee/BubbleCollectionViewLayout.xcworkspace/xcuserdata/mac-0007.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /BubbleCollectionViewLayout.xcworkspace/xcuserdata/mind-0023.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/BubbleCollectionViewLayout/af879244f8aaf6dafb46c88f7a542df9e77326ee/BubbleCollectionViewLayout.xcworkspace/xcuserdata/mind-0023.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /BubbleCollectionViewLayout.xcworkspace/xcuserdata/mind-0023.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 17 | 18 | 19 | 21 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /BubbleCollectionViewLayout.xcworkspace/xcuserdata/mind.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/BubbleCollectionViewLayout/af879244f8aaf6dafb46c88f7a542df9e77326ee/BubbleCollectionViewLayout.xcworkspace/xcuserdata/mind.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Mindinventory 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 781F56881FD6A2E3002E226A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 781F56871FD6A2E3002E226A /* AppDelegate.m */; }; 11 | 781F568E1FD6A2E3002E226A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 781F568C1FD6A2E3002E226A /* Main.storyboard */; }; 12 | 781F56901FD6A2E3002E226A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 781F568F1FD6A2E3002E226A /* Assets.xcassets */; }; 13 | 781F56931FD6A2E3002E226A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 781F56911FD6A2E3002E226A /* LaunchScreen.storyboard */; }; 14 | 781F56961FD6A2E3002E226A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 781F56951FD6A2E3002E226A /* main.m */; }; 15 | 781F56A01FD6B045002E226A /* MICollectionViewBubbleLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 781F569F1FD6B045002E226A /* MICollectionViewBubbleLayout.m */; }; 16 | 781F56A31FD6B713002E226A /* MIMainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 781F56A21FD6B713002E226A /* MIMainViewController.m */; }; 17 | 781F56A61FD6B73F002E226A /* MIBubbleCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 781F56A51FD6B73F002E226A /* MIBubbleCollectionViewCell.m */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 781F56831FD6A2E3002E226A /* ObjectiveCDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ObjectiveCDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 781F56861FD6A2E3002E226A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 23 | 781F56871FD6A2E3002E226A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 24 | 781F568D1FD6A2E3002E226A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 25 | 781F568F1FD6A2E3002E226A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 26 | 781F56921FD6A2E3002E226A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 27 | 781F56941FD6A2E3002E226A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 28 | 781F56951FD6A2E3002E226A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 29 | 781F569E1FD6B045002E226A /* MICollectionViewBubbleLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MICollectionViewBubbleLayout.h; sourceTree = ""; }; 30 | 781F569F1FD6B045002E226A /* MICollectionViewBubbleLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MICollectionViewBubbleLayout.m; sourceTree = ""; }; 31 | 781F56A11FD6B713002E226A /* MIMainViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MIMainViewController.h; sourceTree = ""; }; 32 | 781F56A21FD6B713002E226A /* MIMainViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MIMainViewController.m; sourceTree = ""; }; 33 | 781F56A41FD6B73F002E226A /* MIBubbleCollectionViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MIBubbleCollectionViewCell.h; sourceTree = ""; }; 34 | 781F56A51FD6B73F002E226A /* MIBubbleCollectionViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MIBubbleCollectionViewCell.m; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | 781F56801FD6A2E3002E226A /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | 781F567A1FD6A2E3002E226A = { 49 | isa = PBXGroup; 50 | children = ( 51 | 781F56851FD6A2E3002E226A /* ObjectiveCDemo */, 52 | 781F56841FD6A2E3002E226A /* Products */, 53 | ); 54 | sourceTree = ""; 55 | }; 56 | 781F56841FD6A2E3002E226A /* Products */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | 781F56831FD6A2E3002E226A /* ObjectiveCDemo.app */, 60 | ); 61 | name = Products; 62 | sourceTree = ""; 63 | }; 64 | 781F56851FD6A2E3002E226A /* ObjectiveCDemo */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 781F56861FD6A2E3002E226A /* AppDelegate.h */, 68 | 781F56871FD6A2E3002E226A /* AppDelegate.m */, 69 | 781F56941FD6A2E3002E226A /* Info.plist */, 70 | 781F568F1FD6A2E3002E226A /* Assets.xcassets */, 71 | 781F56911FD6A2E3002E226A /* LaunchScreen.storyboard */, 72 | 781F568C1FD6A2E3002E226A /* Main.storyboard */, 73 | 781F569D1FD6A4A5002E226A /* Class */, 74 | 781F56951FD6A2E3002E226A /* main.m */, 75 | ); 76 | path = ObjectiveCDemo; 77 | sourceTree = ""; 78 | }; 79 | 781F569D1FD6A4A5002E226A /* Class */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 781F56A11FD6B713002E226A /* MIMainViewController.h */, 83 | 781F56A21FD6B713002E226A /* MIMainViewController.m */, 84 | 781F56A41FD6B73F002E226A /* MIBubbleCollectionViewCell.h */, 85 | 781F56A51FD6B73F002E226A /* MIBubbleCollectionViewCell.m */, 86 | 781F569E1FD6B045002E226A /* MICollectionViewBubbleLayout.h */, 87 | 781F569F1FD6B045002E226A /* MICollectionViewBubbleLayout.m */, 88 | ); 89 | path = Class; 90 | sourceTree = ""; 91 | }; 92 | /* End PBXGroup section */ 93 | 94 | /* Begin PBXNativeTarget section */ 95 | 781F56821FD6A2E3002E226A /* ObjectiveCDemo */ = { 96 | isa = PBXNativeTarget; 97 | buildConfigurationList = 781F56991FD6A2E3002E226A /* Build configuration list for PBXNativeTarget "ObjectiveCDemo" */; 98 | buildPhases = ( 99 | 781F567F1FD6A2E3002E226A /* Sources */, 100 | 781F56801FD6A2E3002E226A /* Frameworks */, 101 | 781F56811FD6A2E3002E226A /* Resources */, 102 | ); 103 | buildRules = ( 104 | ); 105 | dependencies = ( 106 | ); 107 | name = ObjectiveCDemo; 108 | productName = ObjectiveCDemo; 109 | productReference = 781F56831FD6A2E3002E226A /* ObjectiveCDemo.app */; 110 | productType = "com.apple.product-type.application"; 111 | }; 112 | /* End PBXNativeTarget section */ 113 | 114 | /* Begin PBXProject section */ 115 | 781F567B1FD6A2E3002E226A /* Project object */ = { 116 | isa = PBXProject; 117 | attributes = { 118 | LastUpgradeCheck = 1240; 119 | ORGANIZATIONNAME = "Jignesh-0007"; 120 | TargetAttributes = { 121 | 781F56821FD6A2E3002E226A = { 122 | CreatedOnToolsVersion = 9.1; 123 | ProvisioningStyle = Manual; 124 | }; 125 | }; 126 | }; 127 | buildConfigurationList = 781F567E1FD6A2E3002E226A /* Build configuration list for PBXProject "ObjectiveCDemo" */; 128 | compatibilityVersion = "Xcode 8.0"; 129 | developmentRegion = en; 130 | hasScannedForEncodings = 0; 131 | knownRegions = ( 132 | en, 133 | Base, 134 | ); 135 | mainGroup = 781F567A1FD6A2E3002E226A; 136 | productRefGroup = 781F56841FD6A2E3002E226A /* Products */; 137 | projectDirPath = ""; 138 | projectRoot = ""; 139 | targets = ( 140 | 781F56821FD6A2E3002E226A /* ObjectiveCDemo */, 141 | ); 142 | }; 143 | /* End PBXProject section */ 144 | 145 | /* Begin PBXResourcesBuildPhase section */ 146 | 781F56811FD6A2E3002E226A /* Resources */ = { 147 | isa = PBXResourcesBuildPhase; 148 | buildActionMask = 2147483647; 149 | files = ( 150 | 781F56931FD6A2E3002E226A /* LaunchScreen.storyboard in Resources */, 151 | 781F56901FD6A2E3002E226A /* Assets.xcassets in Resources */, 152 | 781F568E1FD6A2E3002E226A /* Main.storyboard in Resources */, 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | }; 156 | /* End PBXResourcesBuildPhase section */ 157 | 158 | /* Begin PBXSourcesBuildPhase section */ 159 | 781F567F1FD6A2E3002E226A /* Sources */ = { 160 | isa = PBXSourcesBuildPhase; 161 | buildActionMask = 2147483647; 162 | files = ( 163 | 781F56A61FD6B73F002E226A /* MIBubbleCollectionViewCell.m in Sources */, 164 | 781F56A01FD6B045002E226A /* MICollectionViewBubbleLayout.m in Sources */, 165 | 781F56961FD6A2E3002E226A /* main.m in Sources */, 166 | 781F56881FD6A2E3002E226A /* AppDelegate.m in Sources */, 167 | 781F56A31FD6B713002E226A /* MIMainViewController.m in Sources */, 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | }; 171 | /* End PBXSourcesBuildPhase section */ 172 | 173 | /* Begin PBXVariantGroup section */ 174 | 781F568C1FD6A2E3002E226A /* Main.storyboard */ = { 175 | isa = PBXVariantGroup; 176 | children = ( 177 | 781F568D1FD6A2E3002E226A /* Base */, 178 | ); 179 | name = Main.storyboard; 180 | sourceTree = ""; 181 | }; 182 | 781F56911FD6A2E3002E226A /* LaunchScreen.storyboard */ = { 183 | isa = PBXVariantGroup; 184 | children = ( 185 | 781F56921FD6A2E3002E226A /* Base */, 186 | ); 187 | name = LaunchScreen.storyboard; 188 | sourceTree = ""; 189 | }; 190 | /* End PBXVariantGroup section */ 191 | 192 | /* Begin XCBuildConfiguration section */ 193 | 781F56971FD6A2E3002E226A /* Debug */ = { 194 | isa = XCBuildConfiguration; 195 | buildSettings = { 196 | ALWAYS_SEARCH_USER_PATHS = NO; 197 | CLANG_ANALYZER_NONNULL = YES; 198 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 199 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 200 | CLANG_CXX_LIBRARY = "libc++"; 201 | CLANG_ENABLE_MODULES = YES; 202 | CLANG_ENABLE_OBJC_ARC = YES; 203 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 204 | CLANG_WARN_BOOL_CONVERSION = YES; 205 | CLANG_WARN_COMMA = YES; 206 | CLANG_WARN_CONSTANT_CONVERSION = YES; 207 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 208 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 209 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 210 | CLANG_WARN_EMPTY_BODY = YES; 211 | CLANG_WARN_ENUM_CONVERSION = YES; 212 | CLANG_WARN_INFINITE_RECURSION = YES; 213 | CLANG_WARN_INT_CONVERSION = YES; 214 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 215 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 216 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 217 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 218 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 219 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 220 | CLANG_WARN_STRICT_PROTOTYPES = YES; 221 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 222 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 223 | CLANG_WARN_UNREACHABLE_CODE = YES; 224 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 225 | CODE_SIGN_IDENTITY = "iPhone Developer"; 226 | COPY_PHASE_STRIP = NO; 227 | DEBUG_INFORMATION_FORMAT = dwarf; 228 | ENABLE_STRICT_OBJC_MSGSEND = YES; 229 | ENABLE_TESTABILITY = YES; 230 | GCC_C_LANGUAGE_STANDARD = gnu11; 231 | GCC_DYNAMIC_NO_PIC = NO; 232 | GCC_NO_COMMON_BLOCKS = YES; 233 | GCC_OPTIMIZATION_LEVEL = 0; 234 | GCC_PREPROCESSOR_DEFINITIONS = ( 235 | "DEBUG=1", 236 | "$(inherited)", 237 | ); 238 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 239 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 240 | GCC_WARN_UNDECLARED_SELECTOR = YES; 241 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 242 | GCC_WARN_UNUSED_FUNCTION = YES; 243 | GCC_WARN_UNUSED_VARIABLE = YES; 244 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 245 | MTL_ENABLE_DEBUG_INFO = YES; 246 | ONLY_ACTIVE_ARCH = YES; 247 | SDKROOT = iphoneos; 248 | }; 249 | name = Debug; 250 | }; 251 | 781F56981FD6A2E3002E226A /* Release */ = { 252 | isa = XCBuildConfiguration; 253 | buildSettings = { 254 | ALWAYS_SEARCH_USER_PATHS = NO; 255 | CLANG_ANALYZER_NONNULL = YES; 256 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 257 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 258 | CLANG_CXX_LIBRARY = "libc++"; 259 | CLANG_ENABLE_MODULES = YES; 260 | CLANG_ENABLE_OBJC_ARC = YES; 261 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 262 | CLANG_WARN_BOOL_CONVERSION = YES; 263 | CLANG_WARN_COMMA = YES; 264 | CLANG_WARN_CONSTANT_CONVERSION = YES; 265 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 266 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 267 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 268 | CLANG_WARN_EMPTY_BODY = YES; 269 | CLANG_WARN_ENUM_CONVERSION = YES; 270 | CLANG_WARN_INFINITE_RECURSION = YES; 271 | CLANG_WARN_INT_CONVERSION = YES; 272 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 273 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 274 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 275 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 276 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 277 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 278 | CLANG_WARN_STRICT_PROTOTYPES = YES; 279 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 280 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 281 | CLANG_WARN_UNREACHABLE_CODE = YES; 282 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 283 | CODE_SIGN_IDENTITY = "iPhone Developer"; 284 | COPY_PHASE_STRIP = NO; 285 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 286 | ENABLE_NS_ASSERTIONS = NO; 287 | ENABLE_STRICT_OBJC_MSGSEND = YES; 288 | GCC_C_LANGUAGE_STANDARD = gnu11; 289 | GCC_NO_COMMON_BLOCKS = YES; 290 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 291 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 292 | GCC_WARN_UNDECLARED_SELECTOR = YES; 293 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 294 | GCC_WARN_UNUSED_FUNCTION = YES; 295 | GCC_WARN_UNUSED_VARIABLE = YES; 296 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 297 | MTL_ENABLE_DEBUG_INFO = NO; 298 | SDKROOT = iphoneos; 299 | VALIDATE_PRODUCT = YES; 300 | }; 301 | name = Release; 302 | }; 303 | 781F569A1FD6A2E3002E226A /* Debug */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 307 | CODE_SIGN_STYLE = Manual; 308 | DEVELOPMENT_TEAM = ""; 309 | INFOPLIST_FILE = ObjectiveCDemo/Info.plist; 310 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 311 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 312 | PRODUCT_BUNDLE_IDENTIFIER = com.mi.ObjectiveCDemo; 313 | PRODUCT_NAME = "$(TARGET_NAME)"; 314 | PROVISIONING_PROFILE_SPECIFIER = ""; 315 | TARGETED_DEVICE_FAMILY = "1,2"; 316 | }; 317 | name = Debug; 318 | }; 319 | 781F569B1FD6A2E3002E226A /* Release */ = { 320 | isa = XCBuildConfiguration; 321 | buildSettings = { 322 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 323 | CODE_SIGN_STYLE = Manual; 324 | DEVELOPMENT_TEAM = ""; 325 | INFOPLIST_FILE = ObjectiveCDemo/Info.plist; 326 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 327 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 328 | PRODUCT_BUNDLE_IDENTIFIER = com.mi.ObjectiveCDemo; 329 | PRODUCT_NAME = "$(TARGET_NAME)"; 330 | PROVISIONING_PROFILE_SPECIFIER = ""; 331 | TARGETED_DEVICE_FAMILY = "1,2"; 332 | }; 333 | name = Release; 334 | }; 335 | /* End XCBuildConfiguration section */ 336 | 337 | /* Begin XCConfigurationList section */ 338 | 781F567E1FD6A2E3002E226A /* Build configuration list for PBXProject "ObjectiveCDemo" */ = { 339 | isa = XCConfigurationList; 340 | buildConfigurations = ( 341 | 781F56971FD6A2E3002E226A /* Debug */, 342 | 781F56981FD6A2E3002E226A /* Release */, 343 | ); 344 | defaultConfigurationIsVisible = 0; 345 | defaultConfigurationName = Release; 346 | }; 347 | 781F56991FD6A2E3002E226A /* Build configuration list for PBXNativeTarget "ObjectiveCDemo" */ = { 348 | isa = XCConfigurationList; 349 | buildConfigurations = ( 350 | 781F569A1FD6A2E3002E226A /* Debug */, 351 | 781F569B1FD6A2E3002E226A /* Release */, 352 | ); 353 | defaultConfigurationIsVisible = 0; 354 | defaultConfigurationName = Release; 355 | }; 356 | /* End XCConfigurationList section */ 357 | }; 358 | rootObject = 781F567B1FD6A2E3002E226A /* Project object */; 359 | } 360 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo.xcodeproj/project.xcworkspace/xcuserdata/mind-0023.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/BubbleCollectionViewLayout/af879244f8aaf6dafb46c88f7a542df9e77326ee/ObjectiveCDemo/ObjectiveCDemo.xcodeproj/project.xcworkspace/xcuserdata/mind-0023.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo.xcodeproj/project.xcworkspace/xcuserdata/mind.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/BubbleCollectionViewLayout/af879244f8aaf6dafb46c88f7a542df9e77326ee/ObjectiveCDemo/ObjectiveCDemo.xcodeproj/project.xcworkspace/xcuserdata/mind.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo.xcodeproj/xcuserdata/mac-0007.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ObjectiveCDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 781F56821FD6A2E3002E226A 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo.xcodeproj/xcuserdata/mind-0023.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ObjectiveCDemo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo.xcodeproj/xcuserdata/mind.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ObjectiveCDemo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ObjectiveCDemo 4 | // 5 | // Created by mac-0007 on 05/12/17. 6 | // Copyright © 2017 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ObjectiveCDemo 4 | // 5 | // Created by mac-0007 on 05/12/17. 6 | // Copyright © 2017 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // 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. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Assets.xcassets/Images/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Assets.xcassets/Images/chocolate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "chocolate.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Assets.xcassets/Images/chocolate.imageset/chocolate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/BubbleCollectionViewLayout/af879244f8aaf6dafb46c88f7a542df9e77326ee/ObjectiveCDemo/ObjectiveCDemo/Assets.xcassets/Images/chocolate.imageset/chocolate.jpg -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Assets.xcassets/close_circle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "close_circle.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "template-rendering-intent" : "template" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Assets.xcassets/close_circle.imageset/close_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/BubbleCollectionViewLayout/af879244f8aaf6dafb46c88f7a542df9e77326ee/ObjectiveCDemo/ObjectiveCDemo/Assets.xcassets/close_circle.imageset/close_circle.png -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Assets.xcassets/close_circle_selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "close_circle_selected.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Assets.xcassets/close_circle_selected.imageset/close_circle_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/BubbleCollectionViewLayout/af879244f8aaf6dafb46c88f7a542df9e77326ee/ObjectiveCDemo/ObjectiveCDemo/Assets.xcassets/close_circle_selected.imageset/close_circle_selected.png -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | HelveticaNeue-Bold 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/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 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 106 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Class/MIBubbleCollectionViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // MIBubbleCollectionViewCell.h 3 | // ObjectiveCDemo 4 | // 5 | // Created by mac-0007 on 05/12/17. 6 | // Copyright © 2017 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MIBubbleCollectionViewCell : UICollectionViewCell 12 | @property (weak, nonatomic) IBOutlet UILabel *lblTitle; 13 | @property (weak, nonatomic) IBOutlet UIImageView *imageRoundItem; 14 | @property (weak, nonatomic) IBOutlet UIButton *btnClose; 15 | @property (strong, nonatomic) NSArray *arrayFiltered; 16 | @end 17 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Class/MIBubbleCollectionViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // MIBubbleCollectionViewCell.m 3 | // ObjectiveCDemo 4 | // 5 | // Created by mac-0007 on 05/12/17. 6 | // Copyright © 2017 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | #import "MIBubbleCollectionViewCell.h" 10 | 11 | @implementation MIBubbleCollectionViewCell 12 | 13 | 14 | - (void)awakeFromNib { 15 | [super awakeFromNib]; 16 | } 17 | 18 | - (void)layoutSubviews { 19 | [super layoutSubviews]; 20 | [self.layer setCornerRadius:self.frame.size.height/2]; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Class/MICollectionViewBubbleLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // MICollectionViewBubbleLayout.h 3 | // ObjectiveCDemo 4 | // 5 | // Created by mac-0007 on 05/12/17. 6 | // Copyright © 2017 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol MICollectionViewBubbleLayoutDelegate 12 | 13 | - (CGSize)collectionView:(UICollectionView *)collectionView itemSizeAtIndexPath:(NSIndexPath *)indexPath; 14 | 15 | @end 16 | 17 | @interface MICollectionViewBubbleLayout : UICollectionViewFlowLayout 18 | 19 | @property (nonatomic, weak) id delegate; 20 | 21 | - (id)initWithDelegate:(id )delegate; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Class/MICollectionViewBubbleLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // MICollectionViewBubbleLayout.m 3 | // ObjectiveCDemo 4 | // 5 | // Created by mac-0007 on 05/12/17. 6 | // Copyright © 2017 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | #import "MICollectionViewBubbleLayout.h" 10 | 11 | #define kDefaultInterItemSpacing 5.0f 12 | #define kDefaultLineSpacing 5.0f 13 | 14 | @interface MICollectionViewBubbleLayout () 15 | 16 | @property (nonatomic, strong) NSDictionary *itemLayoutDictionary; 17 | @property (nonatomic) CGSize contentSize; 18 | 19 | @end 20 | 21 | @implementation MICollectionViewBubbleLayout 22 | 23 | - (id)initWithDelegate:(id )delegate 24 | { 25 | self = [super init]; 26 | 27 | if (self) { 28 | self.delegate = delegate; 29 | self.scrollDirection = UICollectionViewScrollDirectionVertical; 30 | self.minimumLineSpacing = kDefaultLineSpacing; 31 | self.minimumInteritemSpacing = kDefaultInterItemSpacing; 32 | } 33 | 34 | return self; 35 | } 36 | 37 | - (void)prepareLayout 38 | { 39 | [super prepareLayout]; 40 | 41 | if (self.collectionView.numberOfSections == 0 || [self.collectionView numberOfItemsInSection:0] == 0) 42 | return; 43 | 44 | if (self.itemLayoutDictionary) self.itemLayoutDictionary = nil; 45 | 46 | CGFloat x = 0.0f; 47 | CGFloat y = 0.0f; 48 | CGSize iSize = CGSizeZero; 49 | 50 | NSIndexPath *indexPath; 51 | NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:0]; 52 | NSMutableDictionary *itemLayoutInformation = [NSMutableDictionary dictionary]; 53 | 54 | for (int itemIndex = 0; itemIndex < numberOfItems; itemIndex ++) { 55 | indexPath = [NSIndexPath indexPathForItem:itemIndex inSection:0]; 56 | iSize = [self.delegate collectionView:self.collectionView itemSizeAtIndexPath:indexPath]; 57 | 58 | CGRect itemRect = CGRectMake(x, y, iSize.width, iSize.height); 59 | if (x > 0 && x + iSize.width + self.minimumInteritemSpacing > self.collectionView.frame.size.width) { 60 | //...Checking if item width is greater than collection view width then set item in new row. 61 | itemRect.origin.x = 0.0f; 62 | itemRect.origin.y = y + iSize.height + self.minimumLineSpacing; 63 | } 64 | 65 | UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; 66 | [attributes setFrame:itemRect]; 67 | [itemLayoutInformation setObject:attributes forKey:indexPath]; 68 | 69 | x = itemRect.origin.x + iSize.width + self.minimumInteritemSpacing; 70 | y = itemRect.origin.y; 71 | } 72 | y += iSize.height; 73 | x = 0.0f; 74 | 75 | self.contentSize = CGSizeMake(self.collectionView.frame.size.width, y + self.minimumLineSpacing); 76 | self.itemLayoutDictionary = [NSDictionary dictionaryWithDictionary:itemLayoutInformation]; 77 | } 78 | 79 | - (CGSize)collectionViewContentSize { 80 | return self.contentSize; 81 | } 82 | 83 | - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { 84 | NSMutableArray *attributes = [NSMutableArray array]; 85 | 86 | NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:0]; 87 | for (int itemIndex = 0; itemIndex < numberOfItems; itemIndex ++) { 88 | NSIndexPath *indexPath = [NSIndexPath indexPathForItem:itemIndex inSection:0]; 89 | UICollectionViewLayoutAttributes *itemAttributes = [self layoutAttributesForItemAtIndexPath:indexPath]; 90 | if (CGRectIntersectsRect(rect, itemAttributes.frame)) { 91 | [attributes addObject:itemAttributes]; 92 | } 93 | } 94 | 95 | return attributes; 96 | } 97 | 98 | - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { 99 | UICollectionViewLayoutAttributes *attributes = [self.itemLayoutDictionary objectForKey:indexPath]; 100 | return attributes; 101 | } 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Class/MIMainViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MIMainViewController.h 3 | // ObjectiveCDemo 4 | // 5 | // Created by mac-0007 on 05/12/17. 6 | // Copyright © 2017 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MIMainViewController : UIViewController 12 | 13 | @property(nonatomic, strong) IBOutlet UICollectionView *collVData; 14 | @property(nonatomic, strong) IBOutlet UITextField *textfiledEnterDetails; 15 | @property(nonatomic, strong) IBOutlet UIButton *buttonDone; 16 | @property(nonatomic, strong) IBOutlet UIButton *buttonAddMoreData; 17 | @property(nonatomic, strong) IBOutlet UIView *viewAddMoreData; 18 | 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/Class/MIMainViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MIMainViewController.m 3 | // ObjectiveCDemo 4 | // 5 | // Created by mac-0007 on 05/12/17. 6 | // Copyright © 2017 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | #import "MIMainViewController.h" 10 | #import "MICollectionViewBubbleLayout.h" 11 | #import "MIBubbleCollectionViewCell.h" 12 | #define CTitlePadding 15 13 | 14 | @interface MIMainViewController () 15 | { 16 | IBOutlet UICollectionView *collVData; 17 | NSMutableArray *arrData; 18 | NSMutableDictionary *Items; 19 | } 20 | @end 21 | 22 | @implementation MIMainViewController 23 | 24 | @synthesize collVData; 25 | 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | 30 | arrData = [[NSMutableArray alloc] init]; 31 | 32 | [arrData addObject:[self createDictionary:@"Glazed" isSelected:[NSNumber numberWithBool:false]]]; 33 | [arrData addObject:[self createDictionary:@"Sugar" isSelected:[NSNumber numberWithBool:false]]]; 34 | [arrData addObject:[self createDictionary:@"Powdered Sugar" isSelected:[NSNumber numberWithBool:false]]]; 35 | [arrData addObject:[self createDictionary:@"Chocolate with Sprinkles" isSelected:[NSNumber numberWithBool:false]]]; 36 | [arrData addObject:[self createDictionary:@"Chocolate" isSelected:[NSNumber numberWithBool:false]]]; 37 | [arrData addObject:[self createDictionary:@"Devil's Food" isSelected:[NSNumber numberWithBool:false]]]; 38 | [arrData addObject:[self createDictionary:@"Maple" isSelected:[NSNumber numberWithBool:false]]]; 39 | [arrData addObject:[self createDictionary:@"Sugar" isSelected:[NSNumber numberWithBool:false]]]; 40 | [arrData addObject:[self createDictionary:@"Blueberry" isSelected:[NSNumber numberWithBool:false]]]; 41 | [arrData addObject:[self createDictionary:@"Regular" isSelected:[NSNumber numberWithBool:false]]]; 42 | [arrData addObject:[self createDictionary:@"Powdered Sugar" isSelected:[NSNumber numberWithBool:false]]]; 43 | [arrData addObject:[self createDictionary:@"Glazed" isSelected:[NSNumber numberWithBool:false]]]; 44 | [arrData addObject:[self createDictionary:@"Sugar" isSelected:[NSNumber numberWithBool:false]]]; 45 | [arrData addObject:[self createDictionary:@"Powdered Sugar" isSelected:[NSNumber numberWithBool:false]]]; 46 | [arrData addObject:[self createDictionary:@"Chocolate with Sprinkles" isSelected:[NSNumber numberWithBool:false]]]; 47 | [arrData addObject:[self createDictionary:@"Chocolate" isSelected:[NSNumber numberWithBool:false]]]; 48 | [arrData addObject:[self createDictionary:@"Devil's Food" isSelected:[NSNumber numberWithBool:false]]]; 49 | [arrData addObject:[self createDictionary:@"Maple" isSelected:[NSNumber numberWithBool:false]]]; 50 | [arrData addObject:[self createDictionary:@"Sugar" isSelected:[NSNumber numberWithBool:false]]]; 51 | [arrData addObject:[self createDictionary:@"Blueberry" isSelected:[NSNumber numberWithBool:false]]]; 52 | [arrData addObject:[self createDictionary:@"Regular" isSelected:[NSNumber numberWithBool:false]]]; 53 | [arrData addObject:[self createDictionary:@"Chocolate with Sprinkles" isSelected:[NSNumber numberWithBool:false]]]; 54 | [arrData addObject:[self createDictionary:@"Sugar" isSelected:[NSNumber numberWithBool:false]]]; 55 | [arrData addObject:[self createDictionary:@"Powdered Sugar" isSelected:[NSNumber numberWithBool:false]]]; 56 | [arrData addObject:[self createDictionary:@"Blueberry" isSelected:[NSNumber numberWithBool:false]]]; 57 | 58 | 59 | //...UICollectionView setup 60 | MICollectionViewBubbleLayout *layout = [[MICollectionViewBubbleLayout alloc] initWithDelegate:self]; 61 | [layout setMinimumLineSpacing:6.0f]; 62 | [layout setMinimumInteritemSpacing:6.0f]; 63 | [collVData setCollectionViewLayout:layout]; 64 | } 65 | 66 | //MARK: - Function For Adding Value in Dictionary - 67 | -(NSMutableDictionary*)createDictionary:(NSString*)itemName isSelected:(id)isSelected{ 68 | 69 | NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; 70 | [dict setValue:itemName forKey:@"name"]; 71 | [dict setValue:isSelected forKey:@"isSelected"]; 72 | return dict; 73 | } 74 | 75 | - (void)didReceiveMemoryWarning { 76 | [super didReceiveMemoryWarning]; 77 | } 78 | 79 | //MARK: - Remove cell Method - 80 | -(IBAction)closeBtnTapped:(UIButton*)sender 81 | { 82 | NSInteger indexRow = sender.tag; 83 | [arrData removeObjectAtIndex:indexRow]; 84 | [collVData reloadData]; 85 | } 86 | -(IBAction)doneButtonTapped:(UIButton*)sender 87 | { 88 | if ([self.textfiledEnterDetails hasText]) 89 | { 90 | [arrData addObject:[self createDictionary:self.textfiledEnterDetails.text isSelected:[NSNumber numberWithBool:false]]]; 91 | [self.textfiledEnterDetails resignFirstResponder]; 92 | [collVData reloadData]; 93 | self.textfiledEnterDetails.text = @""; 94 | } 95 | } 96 | -(IBAction)addmoreButtonTapped:(UIButton*)sender 97 | { 98 | [self.buttonAddMoreData setHidden:YES]; 99 | [self.viewAddMoreData setHidden:NO]; 100 | } 101 | #pragma mark - 102 | #pragma mark - UICollectionView Delegate & Datasource 103 | 104 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 105 | return arrData.count; 106 | } 107 | 108 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { 109 | 110 | NSString *indentifier = @"MIBubbleCollectionViewCell"; 111 | 112 | MIBubbleCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:indentifier forIndexPath:indexPath]; 113 | cell.btnClose.tag = indexPath.row; 114 | cell.layer.borderColor = [[UIColor orangeColor] CGColor]; 115 | cell.layer.borderWidth = 1; 116 | [cell.btnClose addTarget:self action:@selector(closeBtnTapped:) forControlEvents:UIControlEventTouchUpInside]; 117 | [cell.lblTitle setText:[arrData[indexPath.row] valueForKey:@"name"]]; 118 | cell.imageRoundItem.image = [UIImage imageNamed:@"chocolate"]; 119 | cell.imageRoundItem.layer.cornerRadius = 10.0; 120 | if ([arrData[indexPath.row] valueForKey:@"isSelected"] == [NSNumber numberWithBool:true]){ 121 | cell.backgroundColor = [UIColor orangeColor]; 122 | cell.btnClose.tintColor = [UIColor whiteColor]; 123 | cell.lblTitle.textColor = [UIColor whiteColor]; 124 | }else{ 125 | cell.backgroundColor = [UIColor colorWithRed:1.0 green:0.5 blue:0.0 alpha:0.2]; 126 | cell.btnClose.tintColor = [UIColor orangeColor]; 127 | cell.lblTitle.textColor = [UIColor blackColor]; 128 | } 129 | return cell; 130 | } 131 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath 132 | { 133 | NSMutableDictionary *dict = arrData[indexPath.row]; 134 | 135 | if ([dict valueForKey:@"isSelected"] == [NSNumber numberWithBool:false]){ 136 | [dict setValue:[NSNumber numberWithBool:true] forKey:@"isSelected"]; 137 | }else{ 138 | [dict setValue:[NSNumber numberWithBool:false] forKey:@"isSelected"]; 139 | } 140 | 141 | [arrData replaceObjectAtIndex:indexPath.row withObject:dict]; 142 | [collVData reloadData]; 143 | 144 | } 145 | 146 | #pragma mark - 147 | #pragma mark - MICollectionViewBubbleLayoutDelegate 148 | 149 | - (CGSize)collectionView:(UICollectionView *)collectionView itemSizeAtIndexPath:(NSIndexPath *)indexPath { 150 | 151 | NSString *title = [arrData[indexPath.row] valueForKey:@"name"]; 152 | CGSize size = [title sizeWithAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue-Bold" size:15]}]; 153 | size.width = ceilf(size.width + CTitlePadding * 5.5); 154 | size.height = 35; 155 | 156 | //...Checking if item width is greater than collection view width then set item width == collection view width. 157 | if (size.width > collectionView.frame.size.width){ 158 | size.width = collectionView.frame.size.width; 159 | } 160 | return size; 161 | } 162 | 163 | 164 | @end 165 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/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 | -------------------------------------------------------------------------------- /ObjectiveCDemo/ObjectiveCDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ObjectiveCDemo 4 | // 5 | // Created by mac-0007 on 05/12/17. 6 | // Copyright © 2017 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BubbleCollectionViewLayout 2 | Used to get layout like following screens in UICollectionView using custom layout. 3 | 4 | 5 | ![bubble_landscape](https://user-images.githubusercontent.com/84714866/176373657-19d4f9e2-131e-48f5-9f2c-77a370808c03.png) 6 | ![bubble_portrait](https://user-images.githubusercontent.com/84714866/176371515-989f8bc7-6e45-4d6f-87a1-c3d0e87fa803.png) 7 | ![Simulator Screen Shot - iPhone 14 Pro - 2023-04-20 at 15 11 17](https://user-images.githubusercontent.com/42262083/233327928-31ac781d-adb1-427f-82b5-97fc41e76577.png) 8 | 9 | # Requirements 10 | Minimum OS 8.1 and later 11 | 12 | # Manual Installation 13 | You can directly add Below the source files from Class folder to your project. 14 | 15 | # Objective C 16 | 1) MIBubbleCollectionViewCell.h 17 | 2) MIBubbleCollectionViewCell.m 18 | 3) MICollectionViewBubbleLayout.h 19 | 4) MICollectionViewBubbleLayout.m 20 | # Swift 21 | 1) MIBubbleCollectionViewCell.swift 22 | 2) MICollectionViewBubbleLayout.swift 23 | 3) RoundImageView.swift 24 | 4) HeaderView.swift 25 | 26 | # Introduce 27 | MICollectionViewBubbleLayout is subclass of UICollectionViewFlowLayout with the custom layout. 28 | 29 | # Usage 30 | 31 | - The easy way is to use UICollectionViewFlowLayout as it is. You can also subclass it if you intend to change the default behaviour. 32 | - Either use it as a list to select the different tags and it's values or add the tags or value and show it in a list 33 | 34 | 35 | Here is the instruction of how to use UICollectionViewFlowLayout in the Storyboard. 36 | 37 | 1) Add native UICollectionView to the storyboard, establish relationships with its view controllers. 38 | 2) Add UICollectionViewCell in the UICollectionView to the storyboard. 39 | 3)Chose the MIBubbleCollectionViewCell as a custom class inside the UICollectionViewCell to the storyboard. 40 | Configuration for change DefaultInterItemSpacing & DefaultLineSpacing of collecttionview cell you just have to change the value of below two constant in MICollectionViewBubbleLayout.h 41 | 42 | #define kDefaultInterItemSpacing 5.0f 43 | 44 | #define kDefaultLineSpacing 5.0f 45 | 46 | ### Objective C - 47 | 1. Add "MICollectionViewBubbleLayout" ObjectiveC files to your project. 48 | 49 | 2. Conform delegate "MICollectionViewBubbleLayoutDelegate" to your class. 50 | 51 | 3. Set bubble layout in collectionview. 52 | 53 | MICollectionViewBubbleLayout *layout = [[MICollectionViewBubbleLayout alloc] initWithDelegate:self]; 54 | [layout setMinimumLineSpacing:6.0f]; 55 | [layout setMinimumInteritemSpacing:6.0f]; 56 | [collVData setCollectionViewLayout:layout]; 57 | 58 | 4. Implement MICollectionViewBubbleLayoutDelegate method to return size according to your text content. 59 | 60 | - (CGSize)collectionView:(UICollectionView *)collectionView itemSizeAtIndexPath:(NSIndexPath *)indexPath { 61 | 62 | NSString *title = arrData[indexPath.row]; 63 | CGSize size = [title sizeWithAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue-Bold" size:15]}]; 64 | size.width = ceilf(size.width + CTitlePadding * 2); 65 | size.height = 24; 66 | 67 | //...Checking if item width is greater than collection view width then set item width == collection view width. 68 | if (size.width > collectionView.frame.size.width) 69 | size.width = collectionView.frame.size.width; 70 | return size; 71 | } 72 | 73 | 74 | ### Swift - 75 | 1. Add "MICollectionViewBubbleLayout" Swift file to your project. 76 | 77 | 2. Conform delegate "MICollectionViewBubbleLayoutDelegate" to your class. 78 | 79 | 3. Set bubble layout in collectionview. 80 | 81 | let bubbleLayout = MICollectionViewBubbleLayout() 82 | bubbleLayout.minimumLineSpacing = 6.0 83 | bubbleLayout.minimumInteritemSpacing = 0 84 | bubbleLayout.horizontalAlignment = .leading 85 | collVData.setCollectionViewLayout(bubbleLayout, animated: false) 86 | bubbleLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize 87 | 88 | 4. Implement MICollectionViewBubbleLayoutDelegate method to return size according to your text content. 89 | 90 | func collectionView(_ collectionView:UICollectionView, itemSizeAt indexPath:NSIndexPath) -> CGSize 91 | { 92 | let title = arrData[indexPath.row].name 93 | var size = title.size(withAttributes: [NSAttributedString.Key.font: UIFont(name: "HelveticaNeue-Bold", size: 15)!]) 94 | let closeButtonWidth = 20.0 95 | let imageWidth = 20.0 96 | let spacing = 18.0 97 | let totalWidth = Float(size.width + closeButtonWidth + imageWidth + spacing + CGFloat(kItemPadding * 2)) 98 | size.width = CGFloat(ceilf(totalWidth)) 99 | size.height = 40 100 | 101 | //...Checking if item width is greater than collection view width then set item width == collection view width. 102 | if size.width > collectionView.frame.size.width { 103 | size.width = collectionView.frame.size.width 104 | } 105 | 106 | return size; 107 | } 108 | 109 | 110 | 5. If require to add header view 111 | 112 | func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { 113 | switch kind { 114 | case UICollectionView.elementKindSectionHeader: 115 | 116 | let headerView = collVData.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "HeaderView", for: indexPath) as! HeaderView 117 | headerView.lblTitleText.text = arrData[indexPath.section].title 118 | headerView.lblTitleText.sizeToFit() 119 | return headerView 120 | 121 | default: 122 | return UICollectionReusableView() 123 | 124 | } 125 | } 126 | 127 | 128 | 129 | # LICENSE! 130 | 131 | BubbleCollectionViewLayout is [MIT-licensed](https://github.com/mindinventory/BubbleCollectionViewLayout/blob/master/LICENSE). 132 | 133 | ## Let us know! 134 | We’d be really happy if you send us links to your projects where you use our component. Just send an email to sales@mindinventory.com And do let us know if you have any questions or suggestion regarding our work. 135 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 637EFAD82863444700364FEE /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = 637EFAD72863444700364FEE /* Item.swift */; }; 11 | 637EFADB286344AA00364FEE /* ItemResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 637EFADA286344AA00364FEE /* ItemResources.swift */; }; 12 | 637EFADD286344E500364FEE /* RoundImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 637EFADC286344E500364FEE /* RoundImageView.swift */; }; 13 | 781F56691FD6A2AB002E226A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 781F56681FD6A2AB002E226A /* AppDelegate.swift */; }; 14 | 781F56701FD6A2AB002E226A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 781F566F1FD6A2AB002E226A /* Assets.xcassets */; }; 15 | 781F56731FD6A2AB002E226A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 781F56711FD6A2AB002E226A /* LaunchScreen.storyboard */; }; 16 | 7839D8AD1FD7CA5100B5E49F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7839D8AB1FD7CA5100B5E49F /* Main.storyboard */; }; 17 | 788914431FD6D6C500FCFA4B /* MIMainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788914421FD6D6C500FCFA4B /* MIMainViewController.swift */; }; 18 | 788914451FD6D6FC00FCFA4B /* MICollectionViewBubbleLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788914441FD6D6FC00FCFA4B /* MICollectionViewBubbleLayout.swift */; }; 19 | 788914471FD6D71700FCFA4B /* MIBubbleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 788914461FD6D71700FCFA4B /* MIBubbleCollectionViewCell.swift */; }; 20 | B80879D029C9767000F1D89E /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80879CF29C9767000F1D89E /* HeaderView.swift */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 637EFAD72863444700364FEE /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = ""; }; 25 | 637EFADA286344AA00364FEE /* ItemResources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemResources.swift; sourceTree = ""; }; 26 | 637EFADC286344E500364FEE /* RoundImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundImageView.swift; sourceTree = ""; }; 27 | 781F56651FD6A2AB002E226A /* SwiftDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 781F56681FD6A2AB002E226A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 29 | 781F566F1FD6A2AB002E226A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 30 | 781F56721FD6A2AB002E226A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 31 | 781F56741FD6A2AB002E226A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 32 | 7839D8AC1FD7CA5100B5E49F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 33 | 788914421FD6D6C500FCFA4B /* MIMainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MIMainViewController.swift; sourceTree = ""; }; 34 | 788914441FD6D6FC00FCFA4B /* MICollectionViewBubbleLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MICollectionViewBubbleLayout.swift; sourceTree = ""; }; 35 | 788914461FD6D71700FCFA4B /* MIBubbleCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MIBubbleCollectionViewCell.swift; sourceTree = ""; }; 36 | B80879CF29C9767000F1D89E /* HeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 781F56621FD6A2AB002E226A /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | 637EFAD62863443D00364FEE /* Model */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | 637EFAD72863444700364FEE /* Item.swift */, 54 | ); 55 | path = Model; 56 | sourceTree = ""; 57 | }; 58 | 637EFAD92863449300364FEE /* Resources */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 781F566F1FD6A2AB002E226A /* Assets.xcassets */, 62 | 637EFADA286344AA00364FEE /* ItemResources.swift */, 63 | ); 64 | path = Resources; 65 | sourceTree = ""; 66 | }; 67 | 781F565C1FD6A2AB002E226A = { 68 | isa = PBXGroup; 69 | children = ( 70 | 781F56671FD6A2AB002E226A /* SwiftDemo */, 71 | 781F56661FD6A2AB002E226A /* Products */, 72 | ); 73 | sourceTree = ""; 74 | }; 75 | 781F56661FD6A2AB002E226A /* Products */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 781F56651FD6A2AB002E226A /* SwiftDemo.app */, 79 | ); 80 | name = Products; 81 | sourceTree = ""; 82 | }; 83 | 781F56671FD6A2AB002E226A /* SwiftDemo */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 637EFAD92863449300364FEE /* Resources */, 87 | 637EFAD62863443D00364FEE /* Model */, 88 | 781F56681FD6A2AB002E226A /* AppDelegate.swift */, 89 | 781F56741FD6A2AB002E226A /* Info.plist */, 90 | 781F56711FD6A2AB002E226A /* LaunchScreen.storyboard */, 91 | 7839D8AB1FD7CA5100B5E49F /* Main.storyboard */, 92 | 788914411FD6D69600FCFA4B /* Class */, 93 | ); 94 | path = SwiftDemo; 95 | sourceTree = ""; 96 | }; 97 | 788914411FD6D69600FCFA4B /* Class */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 788914421FD6D6C500FCFA4B /* MIMainViewController.swift */, 101 | 788914461FD6D71700FCFA4B /* MIBubbleCollectionViewCell.swift */, 102 | 788914441FD6D6FC00FCFA4B /* MICollectionViewBubbleLayout.swift */, 103 | 637EFADC286344E500364FEE /* RoundImageView.swift */, 104 | B80879CF29C9767000F1D89E /* HeaderView.swift */, 105 | ); 106 | path = Class; 107 | sourceTree = ""; 108 | }; 109 | /* End PBXGroup section */ 110 | 111 | /* Begin PBXNativeTarget section */ 112 | 781F56641FD6A2AB002E226A /* SwiftDemo */ = { 113 | isa = PBXNativeTarget; 114 | buildConfigurationList = 781F56771FD6A2AB002E226A /* Build configuration list for PBXNativeTarget "SwiftDemo" */; 115 | buildPhases = ( 116 | 781F56611FD6A2AB002E226A /* Sources */, 117 | 781F56621FD6A2AB002E226A /* Frameworks */, 118 | 781F56631FD6A2AB002E226A /* Resources */, 119 | ); 120 | buildRules = ( 121 | ); 122 | dependencies = ( 123 | ); 124 | name = SwiftDemo; 125 | productName = SwiftDemo; 126 | productReference = 781F56651FD6A2AB002E226A /* SwiftDemo.app */; 127 | productType = "com.apple.product-type.application"; 128 | }; 129 | /* End PBXNativeTarget section */ 130 | 131 | /* Begin PBXProject section */ 132 | 781F565D1FD6A2AB002E226A /* Project object */ = { 133 | isa = PBXProject; 134 | attributes = { 135 | LastSwiftUpdateCheck = 0910; 136 | LastUpgradeCheck = 1240; 137 | ORGANIZATIONNAME = "Jignesh-0007"; 138 | TargetAttributes = { 139 | 781F56641FD6A2AB002E226A = { 140 | CreatedOnToolsVersion = 9.1; 141 | LastSwiftMigration = 1240; 142 | ProvisioningStyle = Manual; 143 | }; 144 | }; 145 | }; 146 | buildConfigurationList = 781F56601FD6A2AB002E226A /* Build configuration list for PBXProject "SwiftDemo" */; 147 | compatibilityVersion = "Xcode 8.0"; 148 | developmentRegion = en; 149 | hasScannedForEncodings = 0; 150 | knownRegions = ( 151 | en, 152 | Base, 153 | ); 154 | mainGroup = 781F565C1FD6A2AB002E226A; 155 | productRefGroup = 781F56661FD6A2AB002E226A /* Products */; 156 | projectDirPath = ""; 157 | projectRoot = ""; 158 | targets = ( 159 | 781F56641FD6A2AB002E226A /* SwiftDemo */, 160 | ); 161 | }; 162 | /* End PBXProject section */ 163 | 164 | /* Begin PBXResourcesBuildPhase section */ 165 | 781F56631FD6A2AB002E226A /* Resources */ = { 166 | isa = PBXResourcesBuildPhase; 167 | buildActionMask = 2147483647; 168 | files = ( 169 | 7839D8AD1FD7CA5100B5E49F /* Main.storyboard in Resources */, 170 | 781F56731FD6A2AB002E226A /* LaunchScreen.storyboard in Resources */, 171 | 781F56701FD6A2AB002E226A /* Assets.xcassets in Resources */, 172 | ); 173 | runOnlyForDeploymentPostprocessing = 0; 174 | }; 175 | /* End PBXResourcesBuildPhase section */ 176 | 177 | /* Begin PBXSourcesBuildPhase section */ 178 | 781F56611FD6A2AB002E226A /* Sources */ = { 179 | isa = PBXSourcesBuildPhase; 180 | buildActionMask = 2147483647; 181 | files = ( 182 | 637EFAD82863444700364FEE /* Item.swift in Sources */, 183 | 788914471FD6D71700FCFA4B /* MIBubbleCollectionViewCell.swift in Sources */, 184 | 637EFADD286344E500364FEE /* RoundImageView.swift in Sources */, 185 | 788914451FD6D6FC00FCFA4B /* MICollectionViewBubbleLayout.swift in Sources */, 186 | 781F56691FD6A2AB002E226A /* AppDelegate.swift in Sources */, 187 | 637EFADB286344AA00364FEE /* ItemResources.swift in Sources */, 188 | 788914431FD6D6C500FCFA4B /* MIMainViewController.swift in Sources */, 189 | B80879D029C9767000F1D89E /* HeaderView.swift in Sources */, 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | }; 193 | /* End PBXSourcesBuildPhase section */ 194 | 195 | /* Begin PBXVariantGroup section */ 196 | 781F56711FD6A2AB002E226A /* LaunchScreen.storyboard */ = { 197 | isa = PBXVariantGroup; 198 | children = ( 199 | 781F56721FD6A2AB002E226A /* Base */, 200 | ); 201 | name = LaunchScreen.storyboard; 202 | sourceTree = ""; 203 | }; 204 | 7839D8AB1FD7CA5100B5E49F /* Main.storyboard */ = { 205 | isa = PBXVariantGroup; 206 | children = ( 207 | 7839D8AC1FD7CA5100B5E49F /* Base */, 208 | ); 209 | name = Main.storyboard; 210 | sourceTree = ""; 211 | }; 212 | /* End PBXVariantGroup section */ 213 | 214 | /* Begin XCBuildConfiguration section */ 215 | 781F56751FD6A2AB002E226A /* Debug */ = { 216 | isa = XCBuildConfiguration; 217 | buildSettings = { 218 | ALWAYS_SEARCH_USER_PATHS = NO; 219 | CLANG_ANALYZER_NONNULL = YES; 220 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 221 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 222 | CLANG_CXX_LIBRARY = "libc++"; 223 | CLANG_ENABLE_MODULES = YES; 224 | CLANG_ENABLE_OBJC_ARC = YES; 225 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 226 | CLANG_WARN_BOOL_CONVERSION = YES; 227 | CLANG_WARN_COMMA = YES; 228 | CLANG_WARN_CONSTANT_CONVERSION = YES; 229 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 230 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 231 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 232 | CLANG_WARN_EMPTY_BODY = YES; 233 | CLANG_WARN_ENUM_CONVERSION = YES; 234 | CLANG_WARN_INFINITE_RECURSION = YES; 235 | CLANG_WARN_INT_CONVERSION = YES; 236 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 237 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 238 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 239 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 240 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 241 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 242 | CLANG_WARN_STRICT_PROTOTYPES = YES; 243 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 244 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 245 | CLANG_WARN_UNREACHABLE_CODE = YES; 246 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 247 | CODE_SIGN_IDENTITY = "iPhone Developer"; 248 | COPY_PHASE_STRIP = NO; 249 | DEBUG_INFORMATION_FORMAT = dwarf; 250 | ENABLE_STRICT_OBJC_MSGSEND = YES; 251 | ENABLE_TESTABILITY = YES; 252 | GCC_C_LANGUAGE_STANDARD = gnu11; 253 | GCC_DYNAMIC_NO_PIC = NO; 254 | GCC_NO_COMMON_BLOCKS = YES; 255 | GCC_OPTIMIZATION_LEVEL = 0; 256 | GCC_PREPROCESSOR_DEFINITIONS = ( 257 | "DEBUG=1", 258 | "$(inherited)", 259 | ); 260 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 261 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 262 | GCC_WARN_UNDECLARED_SELECTOR = YES; 263 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 264 | GCC_WARN_UNUSED_FUNCTION = YES; 265 | GCC_WARN_UNUSED_VARIABLE = YES; 266 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 267 | MTL_ENABLE_DEBUG_INFO = YES; 268 | ONLY_ACTIVE_ARCH = YES; 269 | SDKROOT = iphoneos; 270 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 271 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 272 | }; 273 | name = Debug; 274 | }; 275 | 781F56761FD6A2AB002E226A /* Release */ = { 276 | isa = XCBuildConfiguration; 277 | buildSettings = { 278 | ALWAYS_SEARCH_USER_PATHS = NO; 279 | CLANG_ANALYZER_NONNULL = YES; 280 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 281 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 282 | CLANG_CXX_LIBRARY = "libc++"; 283 | CLANG_ENABLE_MODULES = YES; 284 | CLANG_ENABLE_OBJC_ARC = YES; 285 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 286 | CLANG_WARN_BOOL_CONVERSION = YES; 287 | CLANG_WARN_COMMA = YES; 288 | CLANG_WARN_CONSTANT_CONVERSION = YES; 289 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 290 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 291 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 292 | CLANG_WARN_EMPTY_BODY = YES; 293 | CLANG_WARN_ENUM_CONVERSION = YES; 294 | CLANG_WARN_INFINITE_RECURSION = YES; 295 | CLANG_WARN_INT_CONVERSION = YES; 296 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 297 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 298 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 299 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 300 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 301 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 302 | CLANG_WARN_STRICT_PROTOTYPES = YES; 303 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 304 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 305 | CLANG_WARN_UNREACHABLE_CODE = YES; 306 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 307 | CODE_SIGN_IDENTITY = "iPhone Developer"; 308 | COPY_PHASE_STRIP = NO; 309 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 310 | ENABLE_NS_ASSERTIONS = NO; 311 | ENABLE_STRICT_OBJC_MSGSEND = YES; 312 | GCC_C_LANGUAGE_STANDARD = gnu11; 313 | GCC_NO_COMMON_BLOCKS = YES; 314 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 315 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 316 | GCC_WARN_UNDECLARED_SELECTOR = YES; 317 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 318 | GCC_WARN_UNUSED_FUNCTION = YES; 319 | GCC_WARN_UNUSED_VARIABLE = YES; 320 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 321 | MTL_ENABLE_DEBUG_INFO = NO; 322 | SDKROOT = iphoneos; 323 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 324 | VALIDATE_PRODUCT = YES; 325 | }; 326 | name = Release; 327 | }; 328 | 781F56781FD6A2AB002E226A /* Debug */ = { 329 | isa = XCBuildConfiguration; 330 | buildSettings = { 331 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 332 | CODE_SIGN_STYLE = Manual; 333 | DEVELOPMENT_TEAM = ""; 334 | INFOPLIST_FILE = SwiftDemo/Info.plist; 335 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 336 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 337 | PRODUCT_BUNDLE_IDENTIFIER = com.mi.SwiftDemo; 338 | PRODUCT_NAME = "$(TARGET_NAME)"; 339 | PROVISIONING_PROFILE_SPECIFIER = ""; 340 | SWIFT_VERSION = 5.0; 341 | TARGETED_DEVICE_FAMILY = "1,2"; 342 | }; 343 | name = Debug; 344 | }; 345 | 781F56791FD6A2AB002E226A /* Release */ = { 346 | isa = XCBuildConfiguration; 347 | buildSettings = { 348 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 349 | CODE_SIGN_STYLE = Manual; 350 | DEVELOPMENT_TEAM = ""; 351 | INFOPLIST_FILE = SwiftDemo/Info.plist; 352 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 353 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 354 | PRODUCT_BUNDLE_IDENTIFIER = com.mi.SwiftDemo; 355 | PRODUCT_NAME = "$(TARGET_NAME)"; 356 | PROVISIONING_PROFILE_SPECIFIER = ""; 357 | SWIFT_VERSION = 5.0; 358 | TARGETED_DEVICE_FAMILY = "1,2"; 359 | }; 360 | name = Release; 361 | }; 362 | /* End XCBuildConfiguration section */ 363 | 364 | /* Begin XCConfigurationList section */ 365 | 781F56601FD6A2AB002E226A /* Build configuration list for PBXProject "SwiftDemo" */ = { 366 | isa = XCConfigurationList; 367 | buildConfigurations = ( 368 | 781F56751FD6A2AB002E226A /* Debug */, 369 | 781F56761FD6A2AB002E226A /* Release */, 370 | ); 371 | defaultConfigurationIsVisible = 0; 372 | defaultConfigurationName = Release; 373 | }; 374 | 781F56771FD6A2AB002E226A /* Build configuration list for PBXNativeTarget "SwiftDemo" */ = { 375 | isa = XCConfigurationList; 376 | buildConfigurations = ( 377 | 781F56781FD6A2AB002E226A /* Debug */, 378 | 781F56791FD6A2AB002E226A /* Release */, 379 | ); 380 | defaultConfigurationIsVisible = 0; 381 | defaultConfigurationName = Release; 382 | }; 383 | /* End XCConfigurationList section */ 384 | }; 385 | rootObject = 781F565D1FD6A2AB002E226A /* Project object */; 386 | } 387 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo.xcodeproj/project.xcworkspace/xcuserdata/mind-0023.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/BubbleCollectionViewLayout/af879244f8aaf6dafb46c88f7a542df9e77326ee/SwiftDemo/SwiftDemo.xcodeproj/project.xcworkspace/xcuserdata/mind-0023.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo.xcodeproj/project.xcworkspace/xcuserdata/mind.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/BubbleCollectionViewLayout/af879244f8aaf6dafb46c88f7a542df9e77326ee/SwiftDemo/SwiftDemo.xcodeproj/project.xcworkspace/xcuserdata/mind.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo.xcodeproj/xcuserdata/mac-0007.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SwiftDemo.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 781F56641FD6A2AB002E226A 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo.xcodeproj/xcuserdata/mind-0023.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SwiftDemo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo.xcodeproj/xcuserdata/mind.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SwiftDemo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftDemo 4 | // 5 | // Created by mac-0007 on 05/12/17. 6 | // Copyright © 2017 Jignesh-0007. 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 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | HelveticaNeue-Bold 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/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 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Class/HeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeaderView.swift 3 | // SwiftDemo 4 | // 5 | // Created by MI-161 on 14/03/23. 6 | // Copyright © 2023 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class HeaderView: UICollectionReusableView { 12 | 13 | @IBOutlet weak var lblTitleText: UILabel! 14 | 15 | override func awakeFromNib() { 16 | super.awakeFromNib() 17 | // Initialization code 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Class/MIBubbleCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MIBubbleCollectionViewCell.swift 3 | // SwiftDemo 4 | // 5 | // Created by mac-0007 on 05/12/17. 6 | // Copyright © 2017 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class MIBubbleCollectionViewCell: UICollectionViewCell { 12 | 13 | @IBOutlet var lblTitle:UILabel! 14 | @IBOutlet weak var itemImageView: RoundImageView! 15 | @IBOutlet weak var closeButton: UIButton! 16 | 17 | weak var delegate: ItemAction? 18 | var item: Item? 19 | 20 | override func awakeFromNib() { 21 | super.awakeFromNib() 22 | self.layer.borderWidth = 1 23 | } 24 | 25 | override func layoutSubviews() { 26 | super.layoutSubviews() 27 | self.layer.cornerRadius = self.frame.size.height / 2 28 | } 29 | 30 | func configure(with item: Item) { 31 | 32 | self.item = item 33 | lblTitle.text = item.name 34 | lblTitle.textColor = item.isSelected ? .white : .black 35 | itemImageView.image = item.image 36 | layer.borderColor = item.color.cgColor 37 | backgroundColor = item.color.withAlphaComponent(item.isSelected ? 1: 0.2) 38 | closeButton.tintColor = item.isSelected ? .white : item.color 39 | } 40 | } 41 | 42 | extension MIBubbleCollectionViewCell { 43 | 44 | @IBAction func onCloseButtonTapped(_ sender: UIButton) { 45 | delegate?.onCloseButtonTapped(with: item, sender: sender) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Class/MICollectionViewBubbleLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MICollectionViewBubbleLayout.swift 3 | // SwiftDemo 4 | // 5 | // Created by mac-0007 on 05/12/17. 6 | // Copyright © 2017 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol MICollectionViewBubbleLayoutDelegate { 12 | func collectionView(_ collectionView:UICollectionView, itemSizeAt indexPath:NSIndexPath) -> CGSize 13 | } 14 | 15 | protocol Alignment {} 16 | 17 | /// Defines a horizontal alignment for UI elements. 18 | public enum HorizontalAlignment: Alignment { 19 | case left 20 | case right 21 | case leading 22 | case trailing 23 | case justified 24 | } 25 | 26 | /// Defines a vertical alignment for UI elements. 27 | public enum VerticalAlignment: Alignment { 28 | case top 29 | case center 30 | case bottom 31 | } 32 | 33 | /// A horizontal alignment used internally by `AlignedCollectionViewFlowLayout` 34 | /// to layout the items, after resolving layout direction specifics. 35 | private enum EffectiveHorizontalAlignment: Alignment { 36 | case left 37 | case right 38 | case justified 39 | } 40 | 41 | /// Describes an axis with respect to which items can be aligned. 42 | private struct AlignmentAxis { 43 | 44 | /// Determines how items are aligned relative to the axis. 45 | let alignment: A 46 | 47 | /// Defines the position of the axis. 48 | /// * If the `Alignment` is horizontal, the alignment axis is vertical and this is the position on the `x` axis. 49 | /// * If the `Alignment` is vertical, the alignment axis is horizontal and this is the position on the `y` axis. 50 | let position: CGFloat 51 | } 52 | 53 | 54 | 55 | // MARK: - Flow Layout 56 | 57 | /// A `UICollectionViewFlowLayout` subclass that gives you control 58 | /// over the horizontal and vertical alignment of the cells. 59 | /// You can use it to align the cells like words in a left- or right-aligned text 60 | /// and you can specify how the cells are vertically aligned in their row. 61 | open class MICollectionViewBubbleLayout: UICollectionViewFlowLayout { 62 | 63 | 64 | // MARK: - 🔶 Properties 65 | 66 | /// Determines how the cells are horizontally aligned in a row. 67 | /// - Note: The default is `.justified`. 68 | public var horizontalAlignment: HorizontalAlignment = .justified 69 | 70 | /// Determines how the cells are vertically aligned in a row. 71 | /// - Note: The default is `.center`. 72 | public var verticalAlignment: VerticalAlignment = .center 73 | 74 | /// The `horizontalAlignment` with its layout direction specifics resolved, 75 | /// i.e. `.leading` and `.trailing` alignments are mapped to `.left` or `right`, 76 | /// depending on the current layout direction. 77 | fileprivate var effectiveHorizontalAlignment: EffectiveHorizontalAlignment { 78 | 79 | var trivialMapping: [HorizontalAlignment: EffectiveHorizontalAlignment] { 80 | return [ 81 | .left: .left, 82 | .right: .right, 83 | .justified: .justified 84 | ] 85 | } 86 | 87 | let layoutDirection = UIApplication.shared.userInterfaceLayoutDirection 88 | 89 | switch layoutDirection { 90 | case .leftToRight: 91 | switch horizontalAlignment { 92 | case .leading: 93 | return .left 94 | case .trailing: 95 | return .right 96 | default: 97 | break 98 | } 99 | 100 | case .rightToLeft: 101 | switch horizontalAlignment { 102 | case .leading: 103 | return .right 104 | case .trailing: 105 | return .left 106 | default: 107 | break 108 | } 109 | @unknown default: 110 | fatalError("not set rightToLeft") 111 | } 112 | 113 | // It's safe to force-unwrap as `.leading` and `.trailing` are covered 114 | // above and the `trivialMapping` dictionary contains all other keys. 115 | return trivialMapping[horizontalAlignment]! 116 | } 117 | 118 | /// The vertical axis with respect to which the cells are horizontally aligned. 119 | /// For a `justified` alignment the alignment axis is not defined and this value is `nil`. 120 | fileprivate var alignmentAxis: AlignmentAxis? { 121 | switch effectiveHorizontalAlignment { 122 | case .left: 123 | return AlignmentAxis(alignment: HorizontalAlignment.left, position: sectionInset.left) 124 | case .right: 125 | guard let collectionViewWidth = collectionView?.frame.size.width else { 126 | return nil 127 | } 128 | return AlignmentAxis(alignment: HorizontalAlignment.right, position: collectionViewWidth - sectionInset.right) 129 | default: 130 | return nil 131 | } 132 | } 133 | 134 | /// The width of the area inside the collection view that can be filled with cells. 135 | private var contentWidth: CGFloat? { 136 | guard let collectionViewWidth = collectionView?.frame.size.width else { 137 | return nil 138 | } 139 | return collectionViewWidth - sectionInset.left - sectionInset.right 140 | } 141 | 142 | 143 | // MARK: - 👶 Initialization 144 | 145 | /// The designated initializer. 146 | /// 147 | /// - Parameters: 148 | /// - horizontalAlignment: Specifies how the cells are horizontally aligned in a row. -- 149 | /// (Default: `.justified`) 150 | /// - verticalAlignment: Specified how the cells are vertically aligned in a row. -- 151 | /// (Default: `.center`) 152 | public init(horizontalAlignment: HorizontalAlignment = .justified, verticalAlignment: VerticalAlignment = .center) { 153 | super.init() 154 | self.horizontalAlignment = horizontalAlignment 155 | self.verticalAlignment = verticalAlignment 156 | } 157 | 158 | required public init?(coder aDecoder: NSCoder) { 159 | super.init(coder: aDecoder) 160 | } 161 | 162 | // MARK: - 🅾️ Overrides 163 | 164 | override open func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { 165 | 166 | // 💡 IDEA: 167 | // The approach for computing a cell's frame is to create a rectangle that covers the current line. 168 | // Then we check if the preceding cell's frame intersects with this rectangle. 169 | // If it does, the current item is not the first item in the line. Otherwise it is. 170 | // (Vice-versa for right-aligned cells.) 171 | // 172 | // +---------+----------------------------------------------------------------+---------+ 173 | // | | | | 174 | // | | +------------+ | | 175 | // | | | | | | 176 | // | section |- - -|- - - - - - |- - - - +---------------------+ - - - - - - -| section | 177 | // | inset | |intersection| | | line rect | inset | 178 | // | |- - -|- - - - - - |- - - - +---------------------+ - - - - - - -| | 179 | // | (left) | | | current item | (right) | 180 | // | | +------------+ | | 181 | // | | previous item | | 182 | // +---------+----------------------------------------------------------------+---------+ 183 | // 184 | // ℹ️ We need this rather complicated approach because the first item in a line 185 | // is not always left-aligned and the last item in a line is not always right-aligned: 186 | // If there is only one item in a line UICollectionViewFlowLayout will center it. 187 | 188 | // We may not change the original layout attributes or UICollectionViewFlowLayout might complain. 189 | guard let layoutAttributes = super.layoutAttributesForItem(at: indexPath)?.copy() as? UICollectionViewLayoutAttributes else { 190 | return nil 191 | } 192 | 193 | // For a justified layout there's nothing to do here 194 | // as UICollectionViewFlowLayout justifies the items in a line by default. 195 | if horizontalAlignment != .justified { 196 | layoutAttributes.alignHorizontally(collectionViewLayout: self) 197 | } 198 | 199 | // For a vertically centered layout there's nothing to do here 200 | // as UICollectionViewFlowLayout center-aligns the items in a line by default. 201 | if verticalAlignment != .center { 202 | layoutAttributes.alignVertically(collectionViewLayout: self) 203 | } 204 | 205 | return layoutAttributes 206 | } 207 | 208 | override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 209 | // We may not change the original layout attributes or UICollectionViewFlowLayout might complain. 210 | let layoutAttributesObjects = copy(super.layoutAttributesForElements(in: rect)) 211 | layoutAttributesObjects?.forEach({ (layoutAttributes) in 212 | setFrame(forLayoutAttributes: layoutAttributes) 213 | }) 214 | return layoutAttributesObjects 215 | } 216 | 217 | 218 | // MARK: - 👷 Private layout helpers 219 | 220 | /// Sets the frame for the passed layout attributes object by calling the `layoutAttributesForItem(at:)` function. 221 | private func setFrame(forLayoutAttributes layoutAttributes: UICollectionViewLayoutAttributes) { 222 | if layoutAttributes.representedElementCategory == .cell { // Do not modify header views etc. 223 | let indexPath = layoutAttributes.indexPath 224 | if let newFrame = layoutAttributesForItem(at: indexPath)?.frame { 225 | layoutAttributes.frame = newFrame 226 | } 227 | } 228 | } 229 | 230 | /// A function to access the `super` implementation of `layoutAttributesForItem(at:)` externally. 231 | /// 232 | /// - Parameter indexPath: The index path of the item for which to return the layout attributes. 233 | /// - Returns: The unmodified layout attributes for the item at the specified index path 234 | /// as computed by `UICollectionViewFlowLayout`. 235 | fileprivate func originalLayoutAttribute(forItemAt indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { 236 | return super.layoutAttributesForItem(at: indexPath) 237 | } 238 | 239 | /// Determines if the `firstItemAttributes`' frame is in the same line 240 | /// as the `secondItemAttributes`' frame. 241 | /// 242 | /// - Parameters: 243 | /// - firstItemAttributes: The first layout attributes object to be compared. 244 | /// - secondItemAttributes: The second layout attributes object to be compared. 245 | /// - Returns: `true` if the frames of the two layout attributes are in the same line, else `false`. 246 | /// `false` is also returned when the layout's `collectionView` property is `nil`. 247 | fileprivate func isFrame(for firstItemAttributes: UICollectionViewLayoutAttributes, inSameLineAsFrameFor secondItemAttributes: UICollectionViewLayoutAttributes) -> Bool { 248 | guard let lineWidth = contentWidth else { 249 | return false 250 | } 251 | let firstItemFrame = firstItemAttributes.frame 252 | let lineFrame = CGRect(x: sectionInset.left, 253 | y: firstItemFrame.origin.y, 254 | width: lineWidth, 255 | height: firstItemFrame.size.height) 256 | return lineFrame.intersects(secondItemAttributes.frame) 257 | } 258 | 259 | /// Determines the layout attributes objects for all items displayed in the same line as the item 260 | /// represented by the passed `layoutAttributes` object. 261 | /// 262 | /// - Parameter layoutAttributes: The layout attributed that represents the reference item. 263 | /// - Returns: The layout attributes objects representing all other items in the same line. 264 | /// The passed `layoutAttributes` object itself is always contained in the returned array. 265 | fileprivate func layoutAttributes(forItemsInLineWith layoutAttributes: UICollectionViewLayoutAttributes) -> [UICollectionViewLayoutAttributes] { 266 | guard let lineWidth = contentWidth else { 267 | return [layoutAttributes] 268 | } 269 | var lineFrame = layoutAttributes.frame 270 | lineFrame.origin.x = sectionInset.left 271 | lineFrame.size.width = lineWidth 272 | return super.layoutAttributesForElements(in: lineFrame) ?? [] 273 | } 274 | 275 | /// Copmutes the alignment axis with which to align the items represented by the `layoutAttributes` objects vertically. 276 | /// 277 | /// - Parameter layoutAttributes: The layout attributes objects to be vertically aligned. 278 | /// - Returns: The axis with respect to which the layout attributes can be aligned 279 | /// or `nil` if the `layoutAttributes` array is empty. 280 | private func verticalAlignmentAxisForLine(with layoutAttributes: [UICollectionViewLayoutAttributes]) -> AlignmentAxis? { 281 | 282 | guard let firstAttribute = layoutAttributes.first else { 283 | return nil 284 | } 285 | 286 | switch verticalAlignment { 287 | case .top: 288 | let minY = layoutAttributes.reduce(CGFloat.greatestFiniteMagnitude) { min($0, $1.frame.minY) } 289 | return AlignmentAxis(alignment: .top, position: minY) 290 | 291 | case .bottom: 292 | let maxY = layoutAttributes.reduce(0) { max($0, $1.frame.maxY) } 293 | return AlignmentAxis(alignment: .bottom, position: maxY) 294 | 295 | default: 296 | let centerY = firstAttribute.center.y 297 | return AlignmentAxis(alignment: .center, position: centerY) 298 | } 299 | } 300 | 301 | /// Computes the axis with which to align the item represented by the `currentLayoutAttributes` vertically. 302 | /// 303 | /// - Parameter currentLayoutAttributes: The layout attributes representing the item to be vertically aligned. 304 | /// - Returns: The axis with respect to which the item can be aligned. 305 | fileprivate func verticalAlignmentAxis(for currentLayoutAttributes: UICollectionViewLayoutAttributes) -> AlignmentAxis { 306 | let layoutAttributesInLine = layoutAttributes(forItemsInLineWith: currentLayoutAttributes) 307 | // It's okay to force-unwrap here because we pass a non-empty array. 308 | return verticalAlignmentAxisForLine(with: layoutAttributesInLine)! 309 | } 310 | 311 | /// Creates a deep copy of the passed array by copying all its items. 312 | /// 313 | /// - Parameter layoutAttributesArray: The array to be copied. 314 | /// - Returns: A deep copy of the passed array. 315 | private func copy(_ layoutAttributesArray: [UICollectionViewLayoutAttributes]?) -> [UICollectionViewLayoutAttributes]? { 316 | return layoutAttributesArray?.map{ $0.copy() } as? [UICollectionViewLayoutAttributes] 317 | } 318 | 319 | } 320 | 321 | 322 | 323 | // MARK: - 👷 Layout attributes helpers 324 | 325 | fileprivate extension UICollectionViewLayoutAttributes { 326 | 327 | private var currentSection: Int { 328 | return indexPath.section 329 | } 330 | 331 | private var currentItem: Int { 332 | return indexPath.item 333 | } 334 | 335 | /// The index path for the item preceding the item represented by this layout attributes object. 336 | private var precedingIndexPath: IndexPath { 337 | return IndexPath(item: currentItem - 1, section: currentSection) 338 | } 339 | 340 | /// The index path for the item following the item represented by this layout attributes object. 341 | private var followingIndexPath: IndexPath { 342 | return IndexPath(item: currentItem + 1, section: currentSection) 343 | } 344 | 345 | /// Checks if the item represetend by this layout attributes object is the first item in the line. 346 | /// 347 | /// - Parameter collectionViewLayout: The layout for which to perform the check. 348 | /// - Returns: `true` if the represented item is the first item in the line, else `false`. 349 | func isRepresentingFirstItemInLine(collectionViewLayout: MICollectionViewBubbleLayout) -> Bool { 350 | if currentItem <= 0 { 351 | return true 352 | } 353 | else { 354 | if let layoutAttributesForPrecedingItem = collectionViewLayout.originalLayoutAttribute(forItemAt: precedingIndexPath) { 355 | return !collectionViewLayout.isFrame(for: self, inSameLineAsFrameFor: layoutAttributesForPrecedingItem) 356 | } 357 | else { 358 | return true 359 | } 360 | } 361 | } 362 | 363 | /// Checks if the item represetend by this layout attributes object is the last item in the line. 364 | /// 365 | /// - Parameter collectionViewLayout: The layout for which to perform the check. 366 | /// - Returns: `true` if the represented item is the last item in the line, else `false`. 367 | func isRepresentingLastItemInLine(collectionViewLayout: MICollectionViewBubbleLayout) -> Bool { 368 | guard let itemCount = collectionViewLayout.collectionView?.numberOfItems(inSection: currentSection) else { 369 | return false 370 | } 371 | 372 | if currentItem >= itemCount - 1 { 373 | return true 374 | } 375 | else { 376 | if let layoutAttributesForFollowingItem = collectionViewLayout.originalLayoutAttribute(forItemAt: followingIndexPath) { 377 | return !collectionViewLayout.isFrame(for: self, inSameLineAsFrameFor: layoutAttributesForFollowingItem) 378 | } 379 | else { 380 | return true 381 | } 382 | } 383 | } 384 | 385 | /// Moves the layout attributes object's frame so that it is aligned horizontally with the alignment axis. 386 | func align(toAlignmentAxis alignmentAxis: AlignmentAxis) { 387 | switch alignmentAxis.alignment { 388 | case .left: 389 | frame.origin.x = alignmentAxis.position 390 | case .right: 391 | frame.origin.x = alignmentAxis.position - frame.size.width 392 | default: 393 | break 394 | } 395 | } 396 | 397 | /// Moves the layout attributes object's frame so that it is aligned vertically with the alignment axis. 398 | func align(toAlignmentAxis alignmentAxis: AlignmentAxis) { 399 | switch alignmentAxis.alignment { 400 | case .top: 401 | frame.origin.y = alignmentAxis.position 402 | case .bottom: 403 | frame.origin.y = alignmentAxis.position - frame.size.height 404 | default: 405 | center.y = alignmentAxis.position 406 | } 407 | } 408 | 409 | /// Positions the frame right of the preceding item's frame, leaving a spacing between the frames 410 | /// as defined by the collection view layout's `minimumInteritemSpacing`. 411 | /// 412 | /// - Parameter collectionViewLayout: The layout on which to perfom the calculations. 413 | private func alignToPrecedingItem(collectionViewLayout: MICollectionViewBubbleLayout) { 414 | let itemSpacing = collectionViewLayout.minimumInteritemSpacing 415 | 416 | if let precedingItemAttributes = collectionViewLayout.layoutAttributesForItem(at: precedingIndexPath) { 417 | frame.origin.x = precedingItemAttributes.frame.maxX + itemSpacing 418 | } 419 | } 420 | 421 | /// Positions the frame left of the following item's frame, leaving a spacing between the frames 422 | /// as defined by the collection view layout's `minimumInteritemSpacing`. 423 | /// 424 | /// - Parameter collectionViewLayout: The layout on which to perfom the calculations. 425 | private func alignToFollowingItem(collectionViewLayout: MICollectionViewBubbleLayout) { 426 | let itemSpacing = collectionViewLayout.minimumInteritemSpacing 427 | 428 | if let followingItemAttributes = collectionViewLayout.layoutAttributesForItem(at: followingIndexPath) { 429 | frame.origin.x = followingItemAttributes.frame.minX - itemSpacing - frame.size.width 430 | } 431 | } 432 | 433 | /// Aligns the frame horizontally as specified by the collection view layout's `horizontalAlignment`. 434 | /// 435 | /// - Parameters: 436 | /// - collectionViewLayout: The layout providing the alignment information. 437 | func alignHorizontally(collectionViewLayout: MICollectionViewBubbleLayout) { 438 | 439 | guard let alignmentAxis = collectionViewLayout.alignmentAxis else { 440 | return 441 | } 442 | 443 | switch collectionViewLayout.effectiveHorizontalAlignment { 444 | 445 | case .left: 446 | if isRepresentingFirstItemInLine(collectionViewLayout: collectionViewLayout) { 447 | align(toAlignmentAxis: alignmentAxis) 448 | } else { 449 | alignToPrecedingItem(collectionViewLayout: collectionViewLayout) 450 | } 451 | 452 | case .right: 453 | if isRepresentingLastItemInLine(collectionViewLayout: collectionViewLayout) { 454 | align(toAlignmentAxis: alignmentAxis) 455 | } else { 456 | alignToFollowingItem(collectionViewLayout: collectionViewLayout) 457 | } 458 | 459 | default: 460 | return 461 | } 462 | } 463 | 464 | /// Aligns the frame vertically as specified by the collection view layout's `verticalAlignment`. 465 | /// 466 | /// - Parameter collectionViewLayout: The layout providing the alignment information. 467 | func alignVertically(collectionViewLayout: MICollectionViewBubbleLayout) { 468 | let alignmentAxis = collectionViewLayout.verticalAlignmentAxis(for: self) 469 | align(toAlignmentAxis: alignmentAxis) 470 | } 471 | 472 | } 473 | 474 | extension UICollectionView { 475 | 476 | func indexPathForView(view: AnyObject) -> IndexPath? { 477 | let originInCollectioView = self.convert(CGPoint.zero, from: (view as! UIView)) 478 | return self.indexPathForItem(at: originInCollectioView) as IndexPath? 479 | } 480 | } 481 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Class/MIMainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MIMainViewController.swift 3 | // SwiftDemo 4 | // 5 | // Created by mac-0007 on 05/12/17. 6 | // Copyright © 2017 Jignesh-0007. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ItemAction: AnyObject { 12 | func onCloseButtonTapped(with item: Item? ,sender: UIButton) 13 | } 14 | 15 | final class MIMainViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate,MICollectionViewBubbleLayoutDelegate { 16 | 17 | @IBOutlet private var collVData: UICollectionView! 18 | @IBOutlet private var buttonDone: UIButton! 19 | @IBOutlet private var buttonAddMoreData: UIButton! 20 | @IBOutlet private var viewAddMoreData: UIView! 21 | @IBOutlet private var textfiledEnterDetails: UITextField! 22 | 23 | private let kItemPadding = 10 24 | private var arrData: [DataCollection] = DataCollection.sectionItems { 25 | didSet { 26 | collVData.reloadData() 27 | } 28 | } 29 | 30 | override func viewDidLoad() { 31 | super.viewDidLoad() 32 | 33 | //...UICollectionView setup 34 | 35 | let bubbleLayout = MICollectionViewBubbleLayout() 36 | bubbleLayout.minimumLineSpacing = 6.0 37 | bubbleLayout.minimumInteritemSpacing = 0 38 | bubbleLayout.horizontalAlignment = .leading 39 | collVData.setCollectionViewLayout(bubbleLayout, animated: false) 40 | bubbleLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize 41 | 42 | } 43 | // MARK: - 44 | // MARK: - IBACTION METHODS 45 | @IBAction func addmoreButtonTapped(_ sender: Any) { 46 | buttonAddMoreData.isHidden = true 47 | viewAddMoreData.isHidden = false 48 | } 49 | @IBAction func doneButtonTapped(_ sender: UIButton) { 50 | 51 | if(self.textfiledEnterDetails.text?.isEmpty == false){ 52 | arrData[sender.tag].add(newItem: Item(id: UUID(), name: self.textfiledEnterDetails.text ?? "",image: ItemResource.chocolate.image, 53 | color: .orange, 54 | isSelected: false)) 55 | } 56 | collVData.reloadData() 57 | self.textfiledEnterDetails.text = "" 58 | self.textfiledEnterDetails.resignFirstResponder() 59 | } 60 | 61 | // MARK: - 62 | // MARK: - UICollectionView Delegate & Datasource 63 | func numberOfSections(in collectionView: UICollectionView) -> Int { 64 | arrData.count 65 | } 66 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 67 | return arrData[section].item.count 68 | } 69 | 70 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 71 | 72 | let indentifier = "MIBubbleCollectionViewCell" 73 | 74 | if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: indentifier, for: indexPath) as? MIBubbleCollectionViewCell { 75 | cell.configure(with: arrData[indexPath.section].item[indexPath.row]) 76 | cell.delegate = self 77 | return cell 78 | } 79 | return UICollectionViewCell() 80 | } 81 | 82 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 83 | arrData[indexPath.section].item[indexPath.row].isSelected.toggle() 84 | } 85 | 86 | // MARK: - 87 | // MARK: - MICollectionViewBubbleLayoutDelegate 88 | 89 | func collectionView(_ collectionView:UICollectionView, itemSizeAt indexPath:NSIndexPath) -> CGSize { 90 | 91 | let title = arrData[indexPath.section].item[indexPath.row].name 92 | var size = title.size(withAttributes: [NSAttributedString.Key.font: UIFont(name: "HelveticaNeue-Bold", size: 15)!]) 93 | let closeButtonWidth = 20.0 94 | let imageWidth = 20.0 95 | let spacing = 18.0 96 | let totalWidth = Float(size.width + closeButtonWidth + imageWidth + spacing + CGFloat(kItemPadding * 2)) 97 | size.width = CGFloat(ceilf(totalWidth)) 98 | size.height = 40 99 | 100 | //...Checking if item width is greater than collection view width then set item width == collection view width. 101 | if size.width > collectionView.frame.size.width { 102 | size.width = collectionView.frame.size.width 103 | } 104 | return size 105 | } 106 | 107 | func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { 108 | 109 | switch kind { 110 | 111 | case UICollectionView.elementKindSectionHeader: 112 | 113 | let headerView = collVData.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "HeaderView", for: indexPath) as! HeaderView 114 | headerView.lblTitleText.text = arrData[indexPath.section].title 115 | headerView.lblTitleText.sizeToFit() 116 | return headerView 117 | 118 | 119 | default: 120 | return UICollectionReusableView() 121 | 122 | } 123 | } 124 | } 125 | 126 | extension MIMainViewController: ItemAction { 127 | 128 | func onCloseButtonTapped(with item: Item?,sender:UIButton) { 129 | 130 | guard item != nil else { return } 131 | guard let indexPath = self.collVData.indexPathForView(view: sender) else { return } 132 | arrData[(indexPath.section)].remove(newItem: item!) 133 | } 134 | } 135 | extension MIMainViewController: UICollectionViewDelegateFlowLayout { 136 | 137 | // MARK: - Collection View Delegate Flow Layout Methods 138 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 139 | return CGSize(width: collectionView.bounds.width, height: 44.0) 140 | } 141 | 142 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { 143 | return UIEdgeInsets.zero 144 | } 145 | 146 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { 147 | return 6.0 148 | } 149 | 150 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { 151 | return 2.0 152 | } 153 | 154 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { 155 | if arrData[section].item.count == 0 { 156 | return CGSize(width: collectionView.bounds.width, height: 0) 157 | } else { 158 | return CGSize(width: collectionView.bounds.width, height: 80.0) 159 | } 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Class/RoundImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoundImageView.swift 3 | // SwiftDemo 4 | // 5 | // Created by Parth Gohel on 22/06/22. 6 | // Copyright © 2022 Parth Gohel. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class RoundImageView: UIImageView { 12 | 13 | override func layoutSubviews() { 14 | super.layoutSubviews() 15 | layer.cornerRadius = frame.size.height/2 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/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 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Model/Item.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Item.swift 3 | // SwiftDemo 4 | // 5 | // Created by Parth Gohel on 22/06/22. 6 | // Copyright © 2022 Parth Gohel. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct DataCollection { 12 | let id: Int 13 | let title:String 14 | var item: [Item] 15 | 16 | mutating func add(newItem: Item) { 17 | item.append(newItem) 18 | } 19 | mutating func remove(newItem: Item) { 20 | item = item.filter({$0.id != newItem.id}) 21 | } 22 | } 23 | struct Item { 24 | let id: UUID 25 | let name: String 26 | let image: UIImage 27 | let color: UIColor 28 | var isSelected: Bool 29 | } 30 | 31 | extension DataCollection { 32 | static var sectionItems: [Self] { 33 | return [ 34 | DataCollection(id: 0, title: "Items name", item: [ 35 | Item(id: UUID(), 36 | name: "None", 37 | image: ItemResource.chocolate.image, 38 | color: .orange, 39 | isSelected: false), 40 | Item(id: UUID(), 41 | name: "Glazed", 42 | image: ItemResource.chocolate.image, 43 | color: .orange, 44 | isSelected: false), 45 | Item(id: UUID(), 46 | name: "Sugar", 47 | image: ItemResource.chocolate.image, 48 | color: .orange, 49 | isSelected: false), 50 | Item(id: UUID(), 51 | name: "Powdered Sugar", 52 | image: ItemResource.chocolate.image, 53 | color: .orange, 54 | isSelected: false), 55 | Item(id: UUID(), 56 | name: "Chocolate with Sprinkles", 57 | image: ItemResource.chocolate.image, 58 | color: .orange, 59 | isSelected: false), 60 | Item(id: UUID(), 61 | name: "Chocolate", 62 | image: ItemResource.chocolate.image, 63 | color: .orange, 64 | isSelected: false), 65 | Item(id: UUID(), 66 | name: "Maple", 67 | image: ItemResource.chocolate.image, 68 | color: .orange, 69 | isSelected: false), 70 | Item(id: UUID(), 71 | name: "Regular", 72 | image: ItemResource.chocolate.image, 73 | color: .orange, 74 | isSelected: false), 75 | Item(id: UUID(), 76 | name: "Chocolate", 77 | image: ItemResource.chocolate.image, 78 | color: .orange, 79 | isSelected: false), 80 | Item(id: UUID(), 81 | name: "Blueberry", 82 | image: ItemResource.chocolate.image, 83 | color: .orange, 84 | isSelected: false), 85 | ]), 86 | DataCollection(id: 1, title: "Items name", item: [ 87 | Item(id: UUID(), 88 | name: "Nothing", 89 | image: ItemResource.chocolate.image, 90 | color: .orange, 91 | isSelected: false), 92 | Item(id: UUID(), 93 | name: "Glazeded", 94 | image: ItemResource.chocolate.image, 95 | color: .orange, 96 | isSelected: false), 97 | Item(id: UUID(), 98 | name: "Sugars", 99 | image: ItemResource.chocolate.image, 100 | color: .orange, 101 | isSelected: false), 102 | Item(id: UUID(), 103 | name: "Powdered Sugars", 104 | image: ItemResource.chocolate.image, 105 | color: .orange, 106 | isSelected: false), 107 | Item(id: UUID(), 108 | name: "Chocolates with Sprinkles", 109 | image: ItemResource.chocolate.image, 110 | color: .orange, 111 | isSelected: false), 112 | Item(id: UUID(), 113 | name: "Chocolates", 114 | image: ItemResource.chocolate.image, 115 | color: .orange, 116 | isSelected: false), 117 | Item(id: UUID(), 118 | name: "Maples", 119 | image: ItemResource.chocolate.image, 120 | color: .orange, 121 | isSelected: false), 122 | Item(id: UUID(), 123 | name: "Regulars", 124 | image: ItemResource.chocolate.image, 125 | color: .orange, 126 | isSelected: false), 127 | Item(id: UUID(), 128 | name: "Chocolates", 129 | image: ItemResource.chocolate.image, 130 | color: .orange, 131 | isSelected: false), 132 | Item(id: UUID(), 133 | name: "Blueberrys", 134 | image: ItemResource.chocolate.image, 135 | color: .orange, 136 | isSelected: false), 137 | ]), 138 | DataCollection(id: 2, title: "Items name", item: [ 139 | Item(id: UUID(), 140 | name: "Nothing", 141 | image: ItemResource.chocolate.image, 142 | color: .orange, 143 | isSelected: false), 144 | Item(id: UUID(), 145 | name: "Glazeded", 146 | image: ItemResource.chocolate.image, 147 | color: .orange, 148 | isSelected: false), 149 | Item(id: UUID(), 150 | name: "Sugars", 151 | image: ItemResource.chocolate.image, 152 | color: .orange, 153 | isSelected: false), 154 | Item(id: UUID(), 155 | name: "Powdered Sugars", 156 | image: ItemResource.chocolate.image, 157 | color: .orange, 158 | isSelected: false), 159 | Item(id: UUID(), 160 | name: "Chocolates with Sprinkles", 161 | image: ItemResource.chocolate.image, 162 | color: .orange, 163 | isSelected: false), 164 | Item(id: UUID(), 165 | name: "Chocolates", 166 | image: ItemResource.chocolate.image, 167 | color: .orange, 168 | isSelected: false), 169 | Item(id: UUID(), 170 | name: "Maples", 171 | image: ItemResource.chocolate.image, 172 | color: .orange, 173 | isSelected: false), 174 | Item(id: UUID(), 175 | name: "Regulars", 176 | image: ItemResource.chocolate.image, 177 | color: .orange, 178 | isSelected: false), 179 | Item(id: UUID(), 180 | name: "Chocolates", 181 | image: ItemResource.chocolate.image, 182 | color: .orange, 183 | isSelected: false), 184 | Item(id: UUID(), 185 | name: "Blueberrys", 186 | image: ItemResource.chocolate.image, 187 | color: .orange, 188 | isSelected: false), 189 | ]) 190 | ] 191 | } 192 | } 193 | 194 | 195 | extension Item { 196 | static var mockItems: [Self] { 197 | return [ 198 | Item(id: UUID(), 199 | name: "None", 200 | image: ItemResource.chocolate.image, 201 | color: .orange, 202 | isSelected: false), 203 | Item(id: UUID(), 204 | name: "Glazed", 205 | image: ItemResource.chocolate.image, 206 | color: .orange, 207 | isSelected: false), 208 | Item(id: UUID(), 209 | name: "Sugar", 210 | image: ItemResource.chocolate.image, 211 | color: .orange, 212 | isSelected: false), 213 | Item(id: UUID(), 214 | name: "Powdered Sugar", 215 | image: ItemResource.chocolate.image, 216 | color: .orange, 217 | isSelected: false), 218 | Item(id: UUID(), 219 | name: "Chocolate with Sprinkles", 220 | image: ItemResource.chocolate.image, 221 | color: .orange, 222 | isSelected: false), 223 | Item(id: UUID(), 224 | name: "Chocolate", 225 | image: ItemResource.chocolate.image, 226 | color: .orange, 227 | isSelected: false), 228 | Item(id: UUID(), 229 | name: "Maple", 230 | image: ItemResource.chocolate.image, 231 | color: .orange, 232 | isSelected: false), 233 | Item(id: UUID(), 234 | name: "Regular", 235 | image: ItemResource.chocolate.image, 236 | color: .orange, 237 | isSelected: false), 238 | Item(id: UUID(), 239 | name: "Chocolate", 240 | image: ItemResource.chocolate.image, 241 | color: .orange, 242 | isSelected: false), 243 | Item(id: UUID(), 244 | name: "Blueberry", 245 | image: ItemResource.chocolate.image, 246 | color: .orange, 247 | isSelected: false) 248 | ] 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Resources/Assets.xcassets/Images/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Resources/Assets.xcassets/Images/chocolate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "chocolate.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Resources/Assets.xcassets/Images/chocolate.imageset/chocolate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/BubbleCollectionViewLayout/af879244f8aaf6dafb46c88f7a542df9e77326ee/SwiftDemo/SwiftDemo/Resources/Assets.xcassets/Images/chocolate.imageset/chocolate.jpg -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Resources/Assets.xcassets/close_circle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "close_circle.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | }, 21 | "properties" : { 22 | "template-rendering-intent" : "template" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Resources/Assets.xcassets/close_circle.imageset/close_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/BubbleCollectionViewLayout/af879244f8aaf6dafb46c88f7a542df9e77326ee/SwiftDemo/SwiftDemo/Resources/Assets.xcassets/close_circle.imageset/close_circle.png -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Resources/Assets.xcassets/close_circle_selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "close_circle_selected.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Resources/Assets.xcassets/close_circle_selected.imageset/close_circle_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mindinventory/BubbleCollectionViewLayout/af879244f8aaf6dafb46c88f7a542df9e77326ee/SwiftDemo/SwiftDemo/Resources/Assets.xcassets/close_circle_selected.imageset/close_circle_selected.png -------------------------------------------------------------------------------- /SwiftDemo/SwiftDemo/Resources/ItemResources.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemResources.swift 3 | // SwiftDemo 4 | // 5 | // Created by Parth Gohel on 22/06/22. 6 | // Copyright © 2022 Parth Gohel. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum ItemResource { 12 | 13 | case chocolate 14 | 15 | var image: UIImage { 16 | switch self { 17 | case .chocolate: 18 | return UIImage(named: "chocolate") ?? UIImage() 19 | } 20 | } 21 | } 22 | --------------------------------------------------------------------------------