├── .gitignore ├── AccordionSwift.podspec ├── AccordionSwift.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── AccordionSwift.xcscheme │ └── Example.xcscheme ├── AccordionSwiftTests └── Info.plist ├── Example ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── argentina.imageset │ │ ├── Contents.json │ │ └── argentina.pdf │ ├── australia.imageset │ │ ├── Contents.json │ │ └── australia.pdf │ ├── belgium.imageset │ │ ├── Contents.json │ │ └── belgium.pdf │ ├── brazil.imageset │ │ ├── Contents.json │ │ └── brazil.pdf │ ├── colombia.imageset │ │ ├── Contents.json │ │ └── colombia.pdf │ ├── costa rica.imageset │ │ ├── Contents.json │ │ └── costa-rica.pdf │ ├── croatia.imageset │ │ ├── Contents.json │ │ └── croatia.pdf │ ├── denmark.imageset │ │ ├── Contents.json │ │ └── denmark.pdf │ ├── egypt.imageset │ │ ├── Contents.json │ │ └── egypt.pdf │ ├── england.imageset │ │ ├── Contents.json │ │ └── england.pdf │ ├── france.imageset │ │ ├── Contents.json │ │ └── france.pdf │ ├── germany.imageset │ │ ├── Contents.json │ │ └── germany.pdf │ ├── iceland.imageset │ │ ├── Contents.json │ │ └── iceland.pdf │ ├── iran.imageset │ │ ├── Contents.json │ │ └── iran.pdf │ ├── japan.imageset │ │ ├── Contents.json │ │ └── japan.pdf │ ├── mexico.imageset │ │ ├── Contents.json │ │ └── mexico.pdf │ ├── morocco.imageset │ │ ├── Contents.json │ │ └── morocco.pdf │ ├── nigeria.imageset │ │ ├── Contents.json │ │ └── nigeria.pdf │ ├── panama.imageset │ │ ├── Contents.json │ │ └── panama.pdf │ ├── peru.imageset │ │ ├── Contents.json │ │ └── peru.pdf │ ├── poland.imageset │ │ ├── Contents.json │ │ └── poland.pdf │ ├── portugal.imageset │ │ ├── Contents.json │ │ └── portugal.pdf │ ├── russia.imageset │ │ ├── Contents.json │ │ └── russia.pdf │ ├── saudi arabia.imageset │ │ ├── Contents.json │ │ └── saudi arabia.pdf │ ├── senegal.imageset │ │ ├── Contents.json │ │ └── senegal.pdf │ ├── serbia.imageset │ │ ├── Contents.json │ │ └── serbia.pdf │ ├── south korea.imageset │ │ ├── Contents.json │ │ └── south-korea.pdf │ ├── spain.imageset │ │ ├── Contents.json │ │ └── spain.pdf │ ├── sweden.imageset │ │ ├── Contents.json │ │ └── sweden.pdf │ ├── switzerland.imageset │ │ ├── Contents.json │ │ └── switzerland.pdf │ ├── tunisia.imageset │ │ ├── Contents.json │ │ └── tunisia.pdf │ └── uruguay.imageset │ │ ├── Contents.json │ │ └── uruguay.pdf ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── Models │ └── Models.swift ├── README.md └── Views │ ├── AccordionTableViewController.swift │ ├── AccordionViewController.swift │ ├── CountryTableViewCell.swift │ └── MainTableViewController.swift ├── ExampleTests ├── ExampleTests.swift └── Info.plist ├── LICENSE ├── Package.swift ├── README.md ├── Source ├── AccordionSwift.h ├── CellViewConfig.swift ├── DataSource.swift ├── DataSourceProvider.swift ├── DataSourceType.swift ├── Info.plist ├── Parent.swift ├── Section.swift ├── TableViewDataSource.swift └── TableViewDelegate.swift └── repo-logo.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | # Pods/ 27 | -------------------------------------------------------------------------------- /AccordionSwift.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint AccordionSwift.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'AccordionSwift' 11 | s.version = '2.0.3' 12 | s.summary = 'An Accordion Menu using an UITableView in Swift.' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | The best way of implement an accordion menu using an UITableView in Swift 22 | DESC 23 | 24 | s.homepage = 'https://github.com/Vkt0r/AccordionSwift' 25 | s.license = { :type => 'MIT', :file => 'LICENSE' } 26 | s.author = { 'Victor Sigler' => 'vikt0r.sigler@gmail.com' } 27 | s.source = { :git => 'https://github.com/Vkt0r/AccordionSwift.git', :tag => "v#{s.version.to_s}" } 28 | s.social_media_url = 'https://twitter.com/Vkt0r' 29 | s.swift_version = '5.0' 30 | 31 | s.ios.deployment_target = '10.0' 32 | s.source_files = 'Source/*.swift' 33 | end 34 | -------------------------------------------------------------------------------- /AccordionSwift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 04441A00211AB24C00D2A699 /* AccordionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 044419FF211AB24C00D2A699 /* AccordionViewController.swift */; }; 11 | 04441A04211AB3FE00D2A699 /* MainTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04441A03211AB3FE00D2A699 /* MainTableViewController.swift */; }; 12 | 0483C2C622A1993100DD6E4D /* AccordionSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = A36D6CFC20EC1F8600224B9B /* AccordionSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 13 | A36D6D0620EC1F8600224B9B /* AccordionSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A36D6CFC20EC1F8600224B9B /* AccordionSwift.framework */; }; 14 | A36D6D0D20EC1F8700224B9B /* AccordionSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = A36D6CFF20EC1F8600224B9B /* AccordionSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | A36D6D1920EC20FC00224B9B /* DataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36D6D1820EC20FC00224B9B /* DataSourceType.swift */; }; 16 | A36D6D1B20EC238300224B9B /* DataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36D6D1A20EC238300224B9B /* DataSource.swift */; }; 17 | A36D6D1D20EC23F700224B9B /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36D6D1C20EC23F700224B9B /* Section.swift */; }; 18 | A36D6D1F20EC280D00224B9B /* CellViewConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36D6D1E20EC280D00224B9B /* CellViewConfig.swift */; }; 19 | A36D6D2120EC2C2800224B9B /* DataSourceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36D6D2020EC2C2800224B9B /* DataSourceProvider.swift */; }; 20 | A36D6D2320EE9A8C00224B9B /* TableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36D6D2220EE9A8C00224B9B /* TableViewDataSource.swift */; }; 21 | A36D6D2520EEAB0A00224B9B /* Parent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36D6D2420EEAB0A00224B9B /* Parent.swift */; }; 22 | A36D6D2720EEBD7B00224B9B /* TableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36D6D2620EEBD7B00224B9B /* TableViewDelegate.swift */; }; 23 | A36D6D2F20EEC7FB00224B9B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36D6D2E20EEC7FB00224B9B /* AppDelegate.swift */; }; 24 | A36D6D3420EEC7FB00224B9B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A36D6D3220EEC7FB00224B9B /* Main.storyboard */; }; 25 | A36D6D3620EEC7FD00224B9B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A36D6D3520EEC7FD00224B9B /* Assets.xcassets */; }; 26 | A36D6D3920EEC7FD00224B9B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A36D6D3720EEC7FD00224B9B /* LaunchScreen.storyboard */; }; 27 | A36D6D4420EEC7FE00224B9B /* ExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36D6D4320EEC7FE00224B9B /* ExampleTests.swift */; }; 28 | A36D6D4D20EEC83100224B9B /* AccordionTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36D6D4C20EEC83100224B9B /* AccordionTableViewController.swift */; }; 29 | A36D6D4F20EEC94400224B9B /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36D6D4E20EEC94400224B9B /* Models.swift */; }; 30 | A36D6D5120EECE0300224B9B /* AccordionSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A36D6CFC20EC1F8600224B9B /* AccordionSwift.framework */; }; 31 | A36D6D5320EFBE3800224B9B /* CountryTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36D6D5220EFBE3800224B9B /* CountryTableViewCell.swift */; }; 32 | /* End PBXBuildFile section */ 33 | 34 | /* Begin PBXContainerItemProxy section */ 35 | A36D6D0720EC1F8600224B9B /* PBXContainerItemProxy */ = { 36 | isa = PBXContainerItemProxy; 37 | containerPortal = A36D6CF320EC1F8600224B9B /* Project object */; 38 | proxyType = 1; 39 | remoteGlobalIDString = A36D6CFB20EC1F8600224B9B; 40 | remoteInfo = AccordionSwift; 41 | }; 42 | A36D6D4020EEC7FE00224B9B /* PBXContainerItemProxy */ = { 43 | isa = PBXContainerItemProxy; 44 | containerPortal = A36D6CF320EC1F8600224B9B /* Project object */; 45 | proxyType = 1; 46 | remoteGlobalIDString = A36D6D2B20EEC7FB00224B9B; 47 | remoteInfo = Example; 48 | }; 49 | /* End PBXContainerItemProxy section */ 50 | 51 | /* Begin PBXCopyFilesBuildPhase section */ 52 | 0483C2C522A1992700DD6E4D /* CopyFiles */ = { 53 | isa = PBXCopyFilesBuildPhase; 54 | buildActionMask = 2147483647; 55 | dstPath = ""; 56 | dstSubfolderSpec = 10; 57 | files = ( 58 | 0483C2C622A1993100DD6E4D /* AccordionSwift.framework in CopyFiles */, 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | /* End PBXCopyFilesBuildPhase section */ 63 | 64 | /* Begin PBXFileReference section */ 65 | 044419FF211AB24C00D2A699 /* AccordionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccordionViewController.swift; sourceTree = ""; }; 66 | 04441A03211AB3FE00D2A699 /* MainTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTableViewController.swift; sourceTree = ""; }; 67 | A36D6CFC20EC1F8600224B9B /* AccordionSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AccordionSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | A36D6CFF20EC1F8600224B9B /* AccordionSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AccordionSwift.h; sourceTree = ""; }; 69 | A36D6D0020EC1F8600224B9B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 70 | A36D6D0520EC1F8600224B9B /* AccordionSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AccordionSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | A36D6D0C20EC1F8700224B9B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 72 | A36D6D1820EC20FC00224B9B /* DataSourceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSourceType.swift; sourceTree = ""; }; 73 | A36D6D1A20EC238300224B9B /* DataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSource.swift; sourceTree = ""; }; 74 | A36D6D1C20EC23F700224B9B /* Section.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Section.swift; sourceTree = ""; }; 75 | A36D6D1E20EC280D00224B9B /* CellViewConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellViewConfig.swift; sourceTree = ""; }; 76 | A36D6D2020EC2C2800224B9B /* DataSourceProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataSourceProvider.swift; sourceTree = ""; }; 77 | A36D6D2220EE9A8C00224B9B /* TableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewDataSource.swift; sourceTree = ""; }; 78 | A36D6D2420EEAB0A00224B9B /* Parent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Parent.swift; sourceTree = ""; }; 79 | A36D6D2620EEBD7B00224B9B /* TableViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewDelegate.swift; sourceTree = ""; }; 80 | A36D6D2C20EEC7FB00224B9B /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 81 | A36D6D2E20EEC7FB00224B9B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 82 | A36D6D3320EEC7FB00224B9B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 83 | A36D6D3520EEC7FD00224B9B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 84 | A36D6D3820EEC7FD00224B9B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 85 | A36D6D3A20EEC7FD00224B9B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 86 | A36D6D3F20EEC7FE00224B9B /* ExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 87 | A36D6D4320EEC7FE00224B9B /* ExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleTests.swift; sourceTree = ""; }; 88 | A36D6D4520EEC7FE00224B9B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 89 | A36D6D4C20EEC83100224B9B /* AccordionTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccordionTableViewController.swift; sourceTree = ""; }; 90 | A36D6D4E20EEC94400224B9B /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = ""; }; 91 | A36D6D5220EFBE3800224B9B /* CountryTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountryTableViewCell.swift; sourceTree = ""; }; 92 | /* End PBXFileReference section */ 93 | 94 | /* Begin PBXFrameworksBuildPhase section */ 95 | A36D6CF820EC1F8600224B9B /* Frameworks */ = { 96 | isa = PBXFrameworksBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | ); 100 | runOnlyForDeploymentPostprocessing = 0; 101 | }; 102 | A36D6D0220EC1F8600224B9B /* Frameworks */ = { 103 | isa = PBXFrameworksBuildPhase; 104 | buildActionMask = 2147483647; 105 | files = ( 106 | A36D6D0620EC1F8600224B9B /* AccordionSwift.framework in Frameworks */, 107 | ); 108 | runOnlyForDeploymentPostprocessing = 0; 109 | }; 110 | A36D6D2920EEC7FB00224B9B /* Frameworks */ = { 111 | isa = PBXFrameworksBuildPhase; 112 | buildActionMask = 2147483647; 113 | files = ( 114 | A36D6D5120EECE0300224B9B /* AccordionSwift.framework in Frameworks */, 115 | ); 116 | runOnlyForDeploymentPostprocessing = 0; 117 | }; 118 | A36D6D3C20EEC7FE00224B9B /* Frameworks */ = { 119 | isa = PBXFrameworksBuildPhase; 120 | buildActionMask = 2147483647; 121 | files = ( 122 | ); 123 | runOnlyForDeploymentPostprocessing = 0; 124 | }; 125 | /* End PBXFrameworksBuildPhase section */ 126 | 127 | /* Begin PBXGroup section */ 128 | 04441A01211AB2A100D2A699 /* Models */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | A36D6D4E20EEC94400224B9B /* Models.swift */, 132 | ); 133 | path = Models; 134 | sourceTree = ""; 135 | }; 136 | 04441A02211AB2AD00D2A699 /* Views */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | A36D6D4C20EEC83100224B9B /* AccordionTableViewController.swift */, 140 | 044419FF211AB24C00D2A699 /* AccordionViewController.swift */, 141 | A36D6D5220EFBE3800224B9B /* CountryTableViewCell.swift */, 142 | 04441A03211AB3FE00D2A699 /* MainTableViewController.swift */, 143 | ); 144 | path = Views; 145 | sourceTree = ""; 146 | }; 147 | A36D6CF220EC1F8600224B9B = { 148 | isa = PBXGroup; 149 | children = ( 150 | A36D6CFE20EC1F8600224B9B /* Source */, 151 | A36D6D0920EC1F8600224B9B /* AccordionSwiftTests */, 152 | A36D6D2D20EEC7FB00224B9B /* Example */, 153 | A36D6D4220EEC7FE00224B9B /* ExampleTests */, 154 | A36D6CFD20EC1F8600224B9B /* Products */, 155 | A36D6D5020EECE0300224B9B /* Frameworks */, 156 | ); 157 | sourceTree = ""; 158 | }; 159 | A36D6CFD20EC1F8600224B9B /* Products */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | A36D6CFC20EC1F8600224B9B /* AccordionSwift.framework */, 163 | A36D6D0520EC1F8600224B9B /* AccordionSwiftTests.xctest */, 164 | A36D6D2C20EEC7FB00224B9B /* Example.app */, 165 | A36D6D3F20EEC7FE00224B9B /* ExampleTests.xctest */, 166 | ); 167 | name = Products; 168 | sourceTree = ""; 169 | }; 170 | A36D6CFE20EC1F8600224B9B /* Source */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | A36D6CFF20EC1F8600224B9B /* AccordionSwift.h */, 174 | A36D6D0020EC1F8600224B9B /* Info.plist */, 175 | A36D6D1820EC20FC00224B9B /* DataSourceType.swift */, 176 | A36D6D1A20EC238300224B9B /* DataSource.swift */, 177 | A36D6D1C20EC23F700224B9B /* Section.swift */, 178 | A36D6D1E20EC280D00224B9B /* CellViewConfig.swift */, 179 | A36D6D2020EC2C2800224B9B /* DataSourceProvider.swift */, 180 | A36D6D2220EE9A8C00224B9B /* TableViewDataSource.swift */, 181 | A36D6D2620EEBD7B00224B9B /* TableViewDelegate.swift */, 182 | A36D6D2420EEAB0A00224B9B /* Parent.swift */, 183 | ); 184 | path = Source; 185 | sourceTree = ""; 186 | }; 187 | A36D6D0920EC1F8600224B9B /* AccordionSwiftTests */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | A36D6D0C20EC1F8700224B9B /* Info.plist */, 191 | ); 192 | path = AccordionSwiftTests; 193 | sourceTree = ""; 194 | }; 195 | A36D6D2D20EEC7FB00224B9B /* Example */ = { 196 | isa = PBXGroup; 197 | children = ( 198 | 04441A02211AB2AD00D2A699 /* Views */, 199 | 04441A01211AB2A100D2A699 /* Models */, 200 | A36D6D2E20EEC7FB00224B9B /* AppDelegate.swift */, 201 | A36D6D3220EEC7FB00224B9B /* Main.storyboard */, 202 | A36D6D3720EEC7FD00224B9B /* LaunchScreen.storyboard */, 203 | A36D6D3520EEC7FD00224B9B /* Assets.xcassets */, 204 | A36D6D3A20EEC7FD00224B9B /* Info.plist */, 205 | ); 206 | path = Example; 207 | sourceTree = ""; 208 | }; 209 | A36D6D4220EEC7FE00224B9B /* ExampleTests */ = { 210 | isa = PBXGroup; 211 | children = ( 212 | A36D6D4320EEC7FE00224B9B /* ExampleTests.swift */, 213 | A36D6D4520EEC7FE00224B9B /* Info.plist */, 214 | ); 215 | path = ExampleTests; 216 | sourceTree = ""; 217 | }; 218 | A36D6D5020EECE0300224B9B /* Frameworks */ = { 219 | isa = PBXGroup; 220 | children = ( 221 | ); 222 | name = Frameworks; 223 | sourceTree = ""; 224 | }; 225 | /* End PBXGroup section */ 226 | 227 | /* Begin PBXHeadersBuildPhase section */ 228 | A36D6CF920EC1F8600224B9B /* Headers */ = { 229 | isa = PBXHeadersBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | A36D6D0D20EC1F8700224B9B /* AccordionSwift.h in Headers */, 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | }; 236 | /* End PBXHeadersBuildPhase section */ 237 | 238 | /* Begin PBXNativeTarget section */ 239 | A36D6CFB20EC1F8600224B9B /* AccordionSwift */ = { 240 | isa = PBXNativeTarget; 241 | buildConfigurationList = A36D6D1020EC1F8700224B9B /* Build configuration list for PBXNativeTarget "AccordionSwift" */; 242 | buildPhases = ( 243 | A36D6CF720EC1F8600224B9B /* Sources */, 244 | A36D6CF820EC1F8600224B9B /* Frameworks */, 245 | A36D6CF920EC1F8600224B9B /* Headers */, 246 | A36D6CFA20EC1F8600224B9B /* Resources */, 247 | ); 248 | buildRules = ( 249 | ); 250 | dependencies = ( 251 | ); 252 | name = AccordionSwift; 253 | productName = AccordionSwift; 254 | productReference = A36D6CFC20EC1F8600224B9B /* AccordionSwift.framework */; 255 | productType = "com.apple.product-type.framework"; 256 | }; 257 | A36D6D0420EC1F8600224B9B /* AccordionSwiftTests */ = { 258 | isa = PBXNativeTarget; 259 | buildConfigurationList = A36D6D1320EC1F8700224B9B /* Build configuration list for PBXNativeTarget "AccordionSwiftTests" */; 260 | buildPhases = ( 261 | A36D6D0120EC1F8600224B9B /* Sources */, 262 | A36D6D0220EC1F8600224B9B /* Frameworks */, 263 | A36D6D0320EC1F8600224B9B /* Resources */, 264 | ); 265 | buildRules = ( 266 | ); 267 | dependencies = ( 268 | A36D6D0820EC1F8600224B9B /* PBXTargetDependency */, 269 | ); 270 | name = AccordionSwiftTests; 271 | productName = AccordionSwiftTests; 272 | productReference = A36D6D0520EC1F8600224B9B /* AccordionSwiftTests.xctest */; 273 | productType = "com.apple.product-type.bundle.unit-test"; 274 | }; 275 | A36D6D2B20EEC7FB00224B9B /* Example */ = { 276 | isa = PBXNativeTarget; 277 | buildConfigurationList = A36D6D4620EEC7FE00224B9B /* Build configuration list for PBXNativeTarget "Example" */; 278 | buildPhases = ( 279 | A36D6D2820EEC7FB00224B9B /* Sources */, 280 | A36D6D2920EEC7FB00224B9B /* Frameworks */, 281 | A36D6D2A20EEC7FB00224B9B /* Resources */, 282 | 0483C2C522A1992700DD6E4D /* CopyFiles */, 283 | ); 284 | buildRules = ( 285 | ); 286 | dependencies = ( 287 | ); 288 | name = Example; 289 | productName = Example; 290 | productReference = A36D6D2C20EEC7FB00224B9B /* Example.app */; 291 | productType = "com.apple.product-type.application"; 292 | }; 293 | A36D6D3E20EEC7FE00224B9B /* ExampleTests */ = { 294 | isa = PBXNativeTarget; 295 | buildConfigurationList = A36D6D4920EEC7FE00224B9B /* Build configuration list for PBXNativeTarget "ExampleTests" */; 296 | buildPhases = ( 297 | A36D6D3B20EEC7FE00224B9B /* Sources */, 298 | A36D6D3C20EEC7FE00224B9B /* Frameworks */, 299 | A36D6D3D20EEC7FE00224B9B /* Resources */, 300 | ); 301 | buildRules = ( 302 | ); 303 | dependencies = ( 304 | A36D6D4120EEC7FE00224B9B /* PBXTargetDependency */, 305 | ); 306 | name = ExampleTests; 307 | productName = ExampleTests; 308 | productReference = A36D6D3F20EEC7FE00224B9B /* ExampleTests.xctest */; 309 | productType = "com.apple.product-type.bundle.unit-test"; 310 | }; 311 | /* End PBXNativeTarget section */ 312 | 313 | /* Begin PBXProject section */ 314 | A36D6CF320EC1F8600224B9B /* Project object */ = { 315 | isa = PBXProject; 316 | attributes = { 317 | LastSwiftUpdateCheck = 0940; 318 | LastUpgradeCheck = 0940; 319 | ORGANIZATIONNAME = "Victor Sigler"; 320 | TargetAttributes = { 321 | A36D6CFB20EC1F8600224B9B = { 322 | CreatedOnToolsVersion = 9.4.1; 323 | LastSwiftMigration = 1020; 324 | }; 325 | A36D6D0420EC1F8600224B9B = { 326 | CreatedOnToolsVersion = 9.4.1; 327 | }; 328 | A36D6D2B20EEC7FB00224B9B = { 329 | CreatedOnToolsVersion = 9.4.1; 330 | LastSwiftMigration = 1110; 331 | }; 332 | A36D6D3E20EEC7FE00224B9B = { 333 | CreatedOnToolsVersion = 9.4.1; 334 | TestTargetID = A36D6D2B20EEC7FB00224B9B; 335 | }; 336 | }; 337 | }; 338 | buildConfigurationList = A36D6CF620EC1F8600224B9B /* Build configuration list for PBXProject "AccordionSwift" */; 339 | compatibilityVersion = "Xcode 9.3"; 340 | developmentRegion = en; 341 | hasScannedForEncodings = 0; 342 | knownRegions = ( 343 | en, 344 | Base, 345 | ); 346 | mainGroup = A36D6CF220EC1F8600224B9B; 347 | productRefGroup = A36D6CFD20EC1F8600224B9B /* Products */; 348 | projectDirPath = ""; 349 | projectRoot = ""; 350 | targets = ( 351 | A36D6CFB20EC1F8600224B9B /* AccordionSwift */, 352 | A36D6D0420EC1F8600224B9B /* AccordionSwiftTests */, 353 | A36D6D2B20EEC7FB00224B9B /* Example */, 354 | A36D6D3E20EEC7FE00224B9B /* ExampleTests */, 355 | ); 356 | }; 357 | /* End PBXProject section */ 358 | 359 | /* Begin PBXResourcesBuildPhase section */ 360 | A36D6CFA20EC1F8600224B9B /* Resources */ = { 361 | isa = PBXResourcesBuildPhase; 362 | buildActionMask = 2147483647; 363 | files = ( 364 | ); 365 | runOnlyForDeploymentPostprocessing = 0; 366 | }; 367 | A36D6D0320EC1F8600224B9B /* Resources */ = { 368 | isa = PBXResourcesBuildPhase; 369 | buildActionMask = 2147483647; 370 | files = ( 371 | ); 372 | runOnlyForDeploymentPostprocessing = 0; 373 | }; 374 | A36D6D2A20EEC7FB00224B9B /* Resources */ = { 375 | isa = PBXResourcesBuildPhase; 376 | buildActionMask = 2147483647; 377 | files = ( 378 | A36D6D3920EEC7FD00224B9B /* LaunchScreen.storyboard in Resources */, 379 | A36D6D3620EEC7FD00224B9B /* Assets.xcassets in Resources */, 380 | A36D6D3420EEC7FB00224B9B /* Main.storyboard in Resources */, 381 | ); 382 | runOnlyForDeploymentPostprocessing = 0; 383 | }; 384 | A36D6D3D20EEC7FE00224B9B /* Resources */ = { 385 | isa = PBXResourcesBuildPhase; 386 | buildActionMask = 2147483647; 387 | files = ( 388 | ); 389 | runOnlyForDeploymentPostprocessing = 0; 390 | }; 391 | /* End PBXResourcesBuildPhase section */ 392 | 393 | /* Begin PBXSourcesBuildPhase section */ 394 | A36D6CF720EC1F8600224B9B /* Sources */ = { 395 | isa = PBXSourcesBuildPhase; 396 | buildActionMask = 2147483647; 397 | files = ( 398 | A36D6D1F20EC280D00224B9B /* CellViewConfig.swift in Sources */, 399 | A36D6D2520EEAB0A00224B9B /* Parent.swift in Sources */, 400 | A36D6D1B20EC238300224B9B /* DataSource.swift in Sources */, 401 | A36D6D1D20EC23F700224B9B /* Section.swift in Sources */, 402 | A36D6D2120EC2C2800224B9B /* DataSourceProvider.swift in Sources */, 403 | A36D6D1920EC20FC00224B9B /* DataSourceType.swift in Sources */, 404 | A36D6D2320EE9A8C00224B9B /* TableViewDataSource.swift in Sources */, 405 | A36D6D2720EEBD7B00224B9B /* TableViewDelegate.swift in Sources */, 406 | ); 407 | runOnlyForDeploymentPostprocessing = 0; 408 | }; 409 | A36D6D0120EC1F8600224B9B /* Sources */ = { 410 | isa = PBXSourcesBuildPhase; 411 | buildActionMask = 2147483647; 412 | files = ( 413 | ); 414 | runOnlyForDeploymentPostprocessing = 0; 415 | }; 416 | A36D6D2820EEC7FB00224B9B /* Sources */ = { 417 | isa = PBXSourcesBuildPhase; 418 | buildActionMask = 2147483647; 419 | files = ( 420 | A36D6D5320EFBE3800224B9B /* CountryTableViewCell.swift in Sources */, 421 | A36D6D4D20EEC83100224B9B /* AccordionTableViewController.swift in Sources */, 422 | 04441A00211AB24C00D2A699 /* AccordionViewController.swift in Sources */, 423 | A36D6D4F20EEC94400224B9B /* Models.swift in Sources */, 424 | 04441A04211AB3FE00D2A699 /* MainTableViewController.swift in Sources */, 425 | A36D6D2F20EEC7FB00224B9B /* AppDelegate.swift in Sources */, 426 | ); 427 | runOnlyForDeploymentPostprocessing = 0; 428 | }; 429 | A36D6D3B20EEC7FE00224B9B /* Sources */ = { 430 | isa = PBXSourcesBuildPhase; 431 | buildActionMask = 2147483647; 432 | files = ( 433 | A36D6D4420EEC7FE00224B9B /* ExampleTests.swift in Sources */, 434 | ); 435 | runOnlyForDeploymentPostprocessing = 0; 436 | }; 437 | /* End PBXSourcesBuildPhase section */ 438 | 439 | /* Begin PBXTargetDependency section */ 440 | A36D6D0820EC1F8600224B9B /* PBXTargetDependency */ = { 441 | isa = PBXTargetDependency; 442 | target = A36D6CFB20EC1F8600224B9B /* AccordionSwift */; 443 | targetProxy = A36D6D0720EC1F8600224B9B /* PBXContainerItemProxy */; 444 | }; 445 | A36D6D4120EEC7FE00224B9B /* PBXTargetDependency */ = { 446 | isa = PBXTargetDependency; 447 | target = A36D6D2B20EEC7FB00224B9B /* Example */; 448 | targetProxy = A36D6D4020EEC7FE00224B9B /* PBXContainerItemProxy */; 449 | }; 450 | /* End PBXTargetDependency section */ 451 | 452 | /* Begin PBXVariantGroup section */ 453 | A36D6D3220EEC7FB00224B9B /* Main.storyboard */ = { 454 | isa = PBXVariantGroup; 455 | children = ( 456 | A36D6D3320EEC7FB00224B9B /* Base */, 457 | ); 458 | name = Main.storyboard; 459 | sourceTree = ""; 460 | }; 461 | A36D6D3720EEC7FD00224B9B /* LaunchScreen.storyboard */ = { 462 | isa = PBXVariantGroup; 463 | children = ( 464 | A36D6D3820EEC7FD00224B9B /* Base */, 465 | ); 466 | name = LaunchScreen.storyboard; 467 | sourceTree = ""; 468 | }; 469 | /* End PBXVariantGroup section */ 470 | 471 | /* Begin XCBuildConfiguration section */ 472 | A36D6D0E20EC1F8700224B9B /* Debug */ = { 473 | isa = XCBuildConfiguration; 474 | buildSettings = { 475 | ALWAYS_SEARCH_USER_PATHS = NO; 476 | CLANG_ANALYZER_NONNULL = YES; 477 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 478 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 479 | CLANG_CXX_LIBRARY = "libc++"; 480 | CLANG_ENABLE_MODULES = YES; 481 | CLANG_ENABLE_OBJC_ARC = YES; 482 | CLANG_ENABLE_OBJC_WEAK = YES; 483 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 484 | CLANG_WARN_BOOL_CONVERSION = YES; 485 | CLANG_WARN_COMMA = YES; 486 | CLANG_WARN_CONSTANT_CONVERSION = YES; 487 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 488 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 489 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 490 | CLANG_WARN_EMPTY_BODY = YES; 491 | CLANG_WARN_ENUM_CONVERSION = YES; 492 | CLANG_WARN_INFINITE_RECURSION = YES; 493 | CLANG_WARN_INT_CONVERSION = YES; 494 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 495 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 496 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 497 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 498 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 499 | CLANG_WARN_STRICT_PROTOTYPES = YES; 500 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 501 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 502 | CLANG_WARN_UNREACHABLE_CODE = YES; 503 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 504 | CODE_SIGN_IDENTITY = "iPhone Developer"; 505 | COPY_PHASE_STRIP = NO; 506 | CURRENT_PROJECT_VERSION = 1; 507 | DEBUG_INFORMATION_FORMAT = dwarf; 508 | ENABLE_STRICT_OBJC_MSGSEND = YES; 509 | ENABLE_TESTABILITY = YES; 510 | GCC_C_LANGUAGE_STANDARD = gnu11; 511 | GCC_DYNAMIC_NO_PIC = NO; 512 | GCC_NO_COMMON_BLOCKS = YES; 513 | GCC_OPTIMIZATION_LEVEL = 0; 514 | GCC_PREPROCESSOR_DEFINITIONS = ( 515 | "DEBUG=1", 516 | "$(inherited)", 517 | ); 518 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 519 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 520 | GCC_WARN_UNDECLARED_SELECTOR = YES; 521 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 522 | GCC_WARN_UNUSED_FUNCTION = YES; 523 | GCC_WARN_UNUSED_VARIABLE = YES; 524 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 525 | MTL_ENABLE_DEBUG_INFO = YES; 526 | ONLY_ACTIVE_ARCH = YES; 527 | SDKROOT = iphoneos; 528 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 529 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 530 | VERSIONING_SYSTEM = "apple-generic"; 531 | VERSION_INFO_PREFIX = ""; 532 | }; 533 | name = Debug; 534 | }; 535 | A36D6D0F20EC1F8700224B9B /* Release */ = { 536 | isa = XCBuildConfiguration; 537 | buildSettings = { 538 | ALWAYS_SEARCH_USER_PATHS = NO; 539 | CLANG_ANALYZER_NONNULL = YES; 540 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 541 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 542 | CLANG_CXX_LIBRARY = "libc++"; 543 | CLANG_ENABLE_MODULES = YES; 544 | CLANG_ENABLE_OBJC_ARC = YES; 545 | CLANG_ENABLE_OBJC_WEAK = YES; 546 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 547 | CLANG_WARN_BOOL_CONVERSION = YES; 548 | CLANG_WARN_COMMA = YES; 549 | CLANG_WARN_CONSTANT_CONVERSION = YES; 550 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 551 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 552 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 553 | CLANG_WARN_EMPTY_BODY = YES; 554 | CLANG_WARN_ENUM_CONVERSION = YES; 555 | CLANG_WARN_INFINITE_RECURSION = YES; 556 | CLANG_WARN_INT_CONVERSION = YES; 557 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 558 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 559 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 560 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 561 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 562 | CLANG_WARN_STRICT_PROTOTYPES = YES; 563 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 564 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 565 | CLANG_WARN_UNREACHABLE_CODE = YES; 566 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 567 | CODE_SIGN_IDENTITY = "iPhone Developer"; 568 | COPY_PHASE_STRIP = NO; 569 | CURRENT_PROJECT_VERSION = 1; 570 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 571 | ENABLE_NS_ASSERTIONS = NO; 572 | ENABLE_STRICT_OBJC_MSGSEND = YES; 573 | GCC_C_LANGUAGE_STANDARD = gnu11; 574 | GCC_NO_COMMON_BLOCKS = YES; 575 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 576 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 577 | GCC_WARN_UNDECLARED_SELECTOR = YES; 578 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 579 | GCC_WARN_UNUSED_FUNCTION = YES; 580 | GCC_WARN_UNUSED_VARIABLE = YES; 581 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 582 | MTL_ENABLE_DEBUG_INFO = NO; 583 | SDKROOT = iphoneos; 584 | SWIFT_COMPILATION_MODE = wholemodule; 585 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 586 | VALIDATE_PRODUCT = YES; 587 | VERSIONING_SYSTEM = "apple-generic"; 588 | VERSION_INFO_PREFIX = ""; 589 | }; 590 | name = Release; 591 | }; 592 | A36D6D1120EC1F8700224B9B /* Debug */ = { 593 | isa = XCBuildConfiguration; 594 | buildSettings = { 595 | CLANG_ENABLE_MODULES = YES; 596 | CODE_SIGN_IDENTITY = ""; 597 | CODE_SIGN_STYLE = Manual; 598 | DEFINES_MODULE = YES; 599 | DEVELOPMENT_TEAM = ""; 600 | DYLIB_COMPATIBILITY_VERSION = 1; 601 | DYLIB_CURRENT_VERSION = 1; 602 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 603 | INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; 604 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 605 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 606 | LD_RUNPATH_SEARCH_PATHS = ( 607 | "$(inherited)", 608 | "@executable_path/Frameworks", 609 | "@loader_path/Frameworks", 610 | ); 611 | PRODUCT_BUNDLE_IDENTIFIER = com.private.AccordionSwift; 612 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 613 | PROVISIONING_PROFILE_SPECIFIER = ""; 614 | SKIP_INSTALL = YES; 615 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 616 | SWIFT_VERSION = 5.0; 617 | TARGETED_DEVICE_FAMILY = "1,2"; 618 | }; 619 | name = Debug; 620 | }; 621 | A36D6D1220EC1F8700224B9B /* Release */ = { 622 | isa = XCBuildConfiguration; 623 | buildSettings = { 624 | CLANG_ENABLE_MODULES = YES; 625 | CODE_SIGN_IDENTITY = ""; 626 | CODE_SIGN_STYLE = Manual; 627 | DEFINES_MODULE = YES; 628 | DEVELOPMENT_TEAM = ""; 629 | DYLIB_COMPATIBILITY_VERSION = 1; 630 | DYLIB_CURRENT_VERSION = 1; 631 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 632 | INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; 633 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 634 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 635 | LD_RUNPATH_SEARCH_PATHS = ( 636 | "$(inherited)", 637 | "@executable_path/Frameworks", 638 | "@loader_path/Frameworks", 639 | ); 640 | PRODUCT_BUNDLE_IDENTIFIER = com.private.AccordionSwift; 641 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 642 | PROVISIONING_PROFILE_SPECIFIER = ""; 643 | SKIP_INSTALL = YES; 644 | SWIFT_VERSION = 5.0; 645 | TARGETED_DEVICE_FAMILY = "1,2"; 646 | }; 647 | name = Release; 648 | }; 649 | A36D6D1420EC1F8700224B9B /* Debug */ = { 650 | isa = XCBuildConfiguration; 651 | buildSettings = { 652 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 653 | CODE_SIGN_IDENTITY = "iPhone Developer"; 654 | CODE_SIGN_STYLE = Automatic; 655 | DEVELOPMENT_TEAM = ZLZHS4FPF3; 656 | INFOPLIST_FILE = AccordionSwiftTests/Info.plist; 657 | LD_RUNPATH_SEARCH_PATHS = ( 658 | "$(inherited)", 659 | "@executable_path/Frameworks", 660 | "@loader_path/Frameworks", 661 | ); 662 | PRODUCT_BUNDLE_IDENTIFIER = com.private.AccordionSwiftTests; 663 | PRODUCT_NAME = "$(TARGET_NAME)"; 664 | PROVISIONING_PROFILE_SPECIFIER = ""; 665 | SWIFT_VERSION = 4.0; 666 | TARGETED_DEVICE_FAMILY = "1,2"; 667 | }; 668 | name = Debug; 669 | }; 670 | A36D6D1520EC1F8700224B9B /* Release */ = { 671 | isa = XCBuildConfiguration; 672 | buildSettings = { 673 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 674 | CODE_SIGN_IDENTITY = "iPhone Developer"; 675 | CODE_SIGN_STYLE = Automatic; 676 | DEVELOPMENT_TEAM = ZLZHS4FPF3; 677 | INFOPLIST_FILE = AccordionSwiftTests/Info.plist; 678 | LD_RUNPATH_SEARCH_PATHS = ( 679 | "$(inherited)", 680 | "@executable_path/Frameworks", 681 | "@loader_path/Frameworks", 682 | ); 683 | PRODUCT_BUNDLE_IDENTIFIER = com.private.AccordionSwiftTests; 684 | PRODUCT_NAME = "$(TARGET_NAME)"; 685 | PROVISIONING_PROFILE_SPECIFIER = ""; 686 | SWIFT_VERSION = 4.0; 687 | TARGETED_DEVICE_FAMILY = "1,2"; 688 | }; 689 | name = Release; 690 | }; 691 | A36D6D4720EEC7FE00224B9B /* Debug */ = { 692 | isa = XCBuildConfiguration; 693 | buildSettings = { 694 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 695 | CODE_SIGN_IDENTITY = "iPhone Developer"; 696 | CODE_SIGN_STYLE = Automatic; 697 | DEVELOPMENT_TEAM = ZLZHS4FPF3; 698 | INFOPLIST_FILE = Example/Info.plist; 699 | LD_RUNPATH_SEARCH_PATHS = ( 700 | "$(inherited)", 701 | "@executable_path/Frameworks", 702 | ); 703 | PRODUCT_BUNDLE_IDENTIFIER = com.private.Example; 704 | PRODUCT_NAME = "$(TARGET_NAME)"; 705 | PROVISIONING_PROFILE_SPECIFIER = ""; 706 | SWIFT_VERSION = 5.0; 707 | TARGETED_DEVICE_FAMILY = "1,2"; 708 | }; 709 | name = Debug; 710 | }; 711 | A36D6D4820EEC7FE00224B9B /* Release */ = { 712 | isa = XCBuildConfiguration; 713 | buildSettings = { 714 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 715 | CODE_SIGN_IDENTITY = "iPhone Developer"; 716 | CODE_SIGN_STYLE = Automatic; 717 | DEVELOPMENT_TEAM = ZLZHS4FPF3; 718 | INFOPLIST_FILE = Example/Info.plist; 719 | LD_RUNPATH_SEARCH_PATHS = ( 720 | "$(inherited)", 721 | "@executable_path/Frameworks", 722 | ); 723 | PRODUCT_BUNDLE_IDENTIFIER = com.private.Example; 724 | PRODUCT_NAME = "$(TARGET_NAME)"; 725 | PROVISIONING_PROFILE_SPECIFIER = ""; 726 | SWIFT_VERSION = 5.0; 727 | TARGETED_DEVICE_FAMILY = "1,2"; 728 | }; 729 | name = Release; 730 | }; 731 | A36D6D4A20EEC7FE00224B9B /* Debug */ = { 732 | isa = XCBuildConfiguration; 733 | buildSettings = { 734 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 735 | BUNDLE_LOADER = "$(TEST_HOST)"; 736 | CODE_SIGN_STYLE = Automatic; 737 | DEVELOPMENT_TEAM = F744RWFJH5; 738 | INFOPLIST_FILE = ExampleTests/Info.plist; 739 | LD_RUNPATH_SEARCH_PATHS = ( 740 | "$(inherited)", 741 | "@executable_path/Frameworks", 742 | "@loader_path/Frameworks", 743 | ); 744 | PRODUCT_BUNDLE_IDENTIFIER = com.private.ExampleTests; 745 | PRODUCT_NAME = "$(TARGET_NAME)"; 746 | SWIFT_VERSION = 4.0; 747 | TARGETED_DEVICE_FAMILY = "1,2"; 748 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/Example"; 749 | }; 750 | name = Debug; 751 | }; 752 | A36D6D4B20EEC7FE00224B9B /* Release */ = { 753 | isa = XCBuildConfiguration; 754 | buildSettings = { 755 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 756 | BUNDLE_LOADER = "$(TEST_HOST)"; 757 | CODE_SIGN_STYLE = Automatic; 758 | DEVELOPMENT_TEAM = F744RWFJH5; 759 | INFOPLIST_FILE = ExampleTests/Info.plist; 760 | LD_RUNPATH_SEARCH_PATHS = ( 761 | "$(inherited)", 762 | "@executable_path/Frameworks", 763 | "@loader_path/Frameworks", 764 | ); 765 | PRODUCT_BUNDLE_IDENTIFIER = com.private.ExampleTests; 766 | PRODUCT_NAME = "$(TARGET_NAME)"; 767 | SWIFT_VERSION = 4.0; 768 | TARGETED_DEVICE_FAMILY = "1,2"; 769 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Example.app/Example"; 770 | }; 771 | name = Release; 772 | }; 773 | /* End XCBuildConfiguration section */ 774 | 775 | /* Begin XCConfigurationList section */ 776 | A36D6CF620EC1F8600224B9B /* Build configuration list for PBXProject "AccordionSwift" */ = { 777 | isa = XCConfigurationList; 778 | buildConfigurations = ( 779 | A36D6D0E20EC1F8700224B9B /* Debug */, 780 | A36D6D0F20EC1F8700224B9B /* Release */, 781 | ); 782 | defaultConfigurationIsVisible = 0; 783 | defaultConfigurationName = Release; 784 | }; 785 | A36D6D1020EC1F8700224B9B /* Build configuration list for PBXNativeTarget "AccordionSwift" */ = { 786 | isa = XCConfigurationList; 787 | buildConfigurations = ( 788 | A36D6D1120EC1F8700224B9B /* Debug */, 789 | A36D6D1220EC1F8700224B9B /* Release */, 790 | ); 791 | defaultConfigurationIsVisible = 0; 792 | defaultConfigurationName = Release; 793 | }; 794 | A36D6D1320EC1F8700224B9B /* Build configuration list for PBXNativeTarget "AccordionSwiftTests" */ = { 795 | isa = XCConfigurationList; 796 | buildConfigurations = ( 797 | A36D6D1420EC1F8700224B9B /* Debug */, 798 | A36D6D1520EC1F8700224B9B /* Release */, 799 | ); 800 | defaultConfigurationIsVisible = 0; 801 | defaultConfigurationName = Release; 802 | }; 803 | A36D6D4620EEC7FE00224B9B /* Build configuration list for PBXNativeTarget "Example" */ = { 804 | isa = XCConfigurationList; 805 | buildConfigurations = ( 806 | A36D6D4720EEC7FE00224B9B /* Debug */, 807 | A36D6D4820EEC7FE00224B9B /* Release */, 808 | ); 809 | defaultConfigurationIsVisible = 0; 810 | defaultConfigurationName = Release; 811 | }; 812 | A36D6D4920EEC7FE00224B9B /* Build configuration list for PBXNativeTarget "ExampleTests" */ = { 813 | isa = XCConfigurationList; 814 | buildConfigurations = ( 815 | A36D6D4A20EEC7FE00224B9B /* Debug */, 816 | A36D6D4B20EEC7FE00224B9B /* Release */, 817 | ); 818 | defaultConfigurationIsVisible = 0; 819 | defaultConfigurationName = Release; 820 | }; 821 | /* End XCConfigurationList section */ 822 | }; 823 | rootObject = A36D6CF320EC1F8600224B9B /* Project object */; 824 | } 825 | -------------------------------------------------------------------------------- /AccordionSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AccordionSwift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AccordionSwift.xcodeproj/xcshareddata/xcschemes/AccordionSwift.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /AccordionSwift.xcodeproj/xcshareddata/xcschemes/Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /AccordionSwiftTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Created by Victor Sigler Lopez on 7/5/18. 6 | // Copyright © 2018 Victor Sigler. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | return true 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Example/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 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/argentina.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "argentina.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/argentina.imageset/argentina.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/argentina.imageset/argentina.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/australia.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "australia.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/australia.imageset/australia.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/australia.imageset/australia.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/belgium.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "belgium.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/belgium.imageset/belgium.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/belgium.imageset/belgium.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/brazil.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "brazil.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/brazil.imageset/brazil.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/brazil.imageset/brazil.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/colombia.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "colombia.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/colombia.imageset/colombia.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/colombia.imageset/colombia.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/costa rica.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "costa-rica.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/costa rica.imageset/costa-rica.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/costa rica.imageset/costa-rica.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/croatia.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "croatia.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/croatia.imageset/croatia.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/croatia.imageset/croatia.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/denmark.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "denmark.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/denmark.imageset/denmark.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/denmark.imageset/denmark.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/egypt.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "egypt.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/egypt.imageset/egypt.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/egypt.imageset/egypt.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/england.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "england.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/england.imageset/england.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/england.imageset/england.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/france.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "france.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/france.imageset/france.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/france.imageset/france.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/germany.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "germany.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/germany.imageset/germany.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/germany.imageset/germany.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/iceland.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "iceland.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/iceland.imageset/iceland.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/iceland.imageset/iceland.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/iran.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "iran.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/iran.imageset/iran.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/iran.imageset/iran.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/japan.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "japan.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/japan.imageset/japan.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/japan.imageset/japan.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/mexico.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "mexico.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/mexico.imageset/mexico.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/mexico.imageset/mexico.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/morocco.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "morocco.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/morocco.imageset/morocco.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/morocco.imageset/morocco.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/nigeria.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "nigeria.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/nigeria.imageset/nigeria.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/nigeria.imageset/nigeria.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/panama.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "panama.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/panama.imageset/panama.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/panama.imageset/panama.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/peru.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "peru.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/peru.imageset/peru.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/peru.imageset/peru.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/poland.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "poland.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/poland.imageset/poland.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/poland.imageset/poland.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/portugal.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "portugal.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/portugal.imageset/portugal.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/portugal.imageset/portugal.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/russia.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "russia.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/russia.imageset/russia.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/russia.imageset/russia.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/saudi arabia.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "saudi arabia.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/saudi arabia.imageset/saudi arabia.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/saudi arabia.imageset/saudi arabia.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/senegal.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "senegal.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/senegal.imageset/senegal.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/senegal.imageset/senegal.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/serbia.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "serbia.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/serbia.imageset/serbia.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/serbia.imageset/serbia.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/south korea.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "south-korea.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/south korea.imageset/south-korea.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/south korea.imageset/south-korea.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/spain.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "spain.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/spain.imageset/spain.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/spain.imageset/spain.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/sweden.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sweden.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/sweden.imageset/sweden.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/sweden.imageset/sweden.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/switzerland.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "switzerland.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/switzerland.imageset/switzerland.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/switzerland.imageset/switzerland.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/tunisia.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tunisia.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/tunisia.imageset/tunisia.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/tunisia.imageset/tunisia.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/uruguay.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "uruguay.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/uruguay.imageset/uruguay.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/Example/Assets.xcassets/uruguay.imageset/uruguay.pdf -------------------------------------------------------------------------------- /Example/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Example/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 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 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 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 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 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /Example/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 | -------------------------------------------------------------------------------- /Example/Models/Models.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParentCellModel.swift 3 | // Example 4 | // 5 | // Created by Victor Sigler Lopez on 7/5/18. 6 | // Copyright © 2018 Victor Sigler. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct GroupCellModel { 12 | let name: String 13 | } 14 | 15 | struct CountryCellModel { 16 | let name: String 17 | } 18 | -------------------------------------------------------------------------------- /Example/README.md: -------------------------------------------------------------------------------- 1 | ## Examples: 2 | ### Implementation: 3 | 1. Using a UIViewController, please see: `AccordionViewController` 4 | 2. Using a UITableViewController, please see: `AccordionTableViewController` 5 | 6 | ### Behaviors: 7 | The library supports the following behaviors: 8 | 1. Multiple parent cells can be open at once (Example: [AccordionViewController](https://github.com/Vkt0r/AccordionSwift/blob/master/Example/Views/AccordionViewController.swift)) 9 | 2. Only one parent cell can be opened at once (Example: [AccordionTableViewController](https://github.com/Vkt0r/AccordionSwift/blob/master/Example/Views/AccordionTableViewController.swift)) 10 | 11 | The default behavior is to allow multiple parent cells to be open at once. The behavior can be changed by setting the `numberOfExpandedParentCells` in the `DataProvider` constructor to either `.single` or `.multiple`. One can also specify an element to be expanded initially, this can be achieved by setting the `numberOfExpandedParentCells` to `.single` and by setting the state of the `Parent` to `.expanded` (See [example](https://github.com/Vkt0r/AccordionSwift/blob/master/Example/Views/AccordionTableViewController.swift)) 12 | 13 | ```swift 14 | DataSourceProvider( 15 | dataSource: dataSource, 16 | parentCellConfig: parentCellConfig, 17 | childCellConfig: childCellConfig, 18 | didSelectParentAtIndexPath: didSelectParentCell, 19 | didSelectChildAtIndexPath: didSelectChildCell, 20 | scrollViewDidScroll: scrollViewDidScroll, 21 | // Configure DataSourceProvider to have only one parent expanded at a time 22 | numberOfExpandedParentCells: .single // .multiple 23 | ) 24 | ``` -------------------------------------------------------------------------------- /Example/Views/AccordionTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccordionTableViewController.swift 3 | // Example: AccordionTableViewController + Only one parent expanded at a time 4 | // 5 | // Created by Victor Sigler Lopez on 7/5/18. 6 | // Updated by Kyle Wood on 15/10/19. 7 | // Copyright © 2018 Victor Sigler. All rights reserved. 8 | // 9 | 10 | import UIKit 11 | import AccordionSwift 12 | 13 | class AccordionTableViewController: UITableViewController { 14 | 15 | // MARK: - Typealias 16 | 17 | typealias ParentCellModel = Parent 18 | typealias ParentCellConfig = CellViewConfig 19 | typealias ChildCellConfig = CellViewConfig 20 | 21 | // MARK: - Properties 22 | 23 | /// The Data Source Provider with the type of DataSource and the different models for the Parent and Child cell. 24 | var dataSourceProvider: DataSourceProvider, ParentCellConfig, ChildCellConfig>? 25 | 26 | // MARK: - UIViewController 27 | 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | configDataSource() 31 | navigationItem.title = "World Cup 2018" 32 | } 33 | } 34 | 35 | extension AccordionTableViewController { 36 | 37 | // MARK: - Methods 38 | 39 | /// Configure the data source 40 | private func configDataSource() { 41 | 42 | let groupA = Parent(item: GroupCellModel(name: "Group A"), 43 | children: [CountryCellModel(name: "Uruguay"), 44 | CountryCellModel(name: "Russia"), 45 | CountryCellModel(name: "Saudi Arabia"), 46 | CountryCellModel(name: "Egypt")] 47 | ) 48 | 49 | let groupB = Parent(state: .expanded, item: GroupCellModel(name: "Group B"), 50 | children: [CountryCellModel(name: "Spain"), 51 | CountryCellModel(name: "Portugal"), 52 | CountryCellModel(name: "Iran"), 53 | CountryCellModel(name: "Morocco")] 54 | ) 55 | 56 | let groupC = Parent(item: GroupCellModel(name: "Group C"), 57 | children: [CountryCellModel(name: "France"), 58 | CountryCellModel(name: "Denmark"), 59 | CountryCellModel(name: "Peru"), 60 | CountryCellModel(name: "Australia")] 61 | ) 62 | 63 | let groupD = Parent(item: GroupCellModel(name: "Group D"), 64 | children: [CountryCellModel(name: "Croatia"), 65 | CountryCellModel(name: "Argentina"), 66 | CountryCellModel(name: "Nigeria"), 67 | CountryCellModel(name: "Iceland")] 68 | ) 69 | 70 | let groupE = Parent(item: GroupCellModel(name: "Group E"), 71 | children: [CountryCellModel(name: "Brazil"), 72 | CountryCellModel(name: "Switzerland"), 73 | CountryCellModel(name: "Serbia"), 74 | CountryCellModel(name: "Costa Rica")] 75 | ) 76 | 77 | let groupF = Parent(item: GroupCellModel(name: "Group F"), 78 | children: [CountryCellModel(name: "Sweden"), 79 | CountryCellModel(name: "Mexico"), 80 | CountryCellModel(name: "South Korea"), 81 | CountryCellModel(name: "Germany")] 82 | ) 83 | 84 | let groupG = Parent(item: GroupCellModel(name: "Group G"), 85 | children: [CountryCellModel(name: "Belgium"), 86 | CountryCellModel(name: "England"), 87 | CountryCellModel(name: "Tunisia"), 88 | CountryCellModel(name: "Panama")] 89 | ) 90 | 91 | let groupH = Parent(item: GroupCellModel(name: "Group H"), 92 | children: [CountryCellModel(name: "Colombia"), 93 | CountryCellModel(name: "Japan"), 94 | CountryCellModel(name: "Senegal"), 95 | CountryCellModel(name: "Poland")] 96 | ) 97 | 98 | let sections = [groupA, groupB, groupC, groupD, groupE, groupF, groupG, groupH].map { parents in 99 | Section(items: parents) 100 | } 101 | 102 | let dataSource = DataSource(sections) 103 | 104 | let parentCellConfig = CellViewConfig, UITableViewCell>(reuseIdentifier: "GroupCell") { 105 | (cell, model, tableView, indexPath) -> UITableViewCell in 106 | cell.textLabel?.text = model?.item.name 107 | return cell 108 | } 109 | 110 | let childCellConfig = CellViewConfig(reuseIdentifier: "CountryCell") { 111 | (cell, item, tableView, indexPath) -> CountryTableViewCell in 112 | cell.contryLabel.text = item?.name 113 | cell.countryImageView.image = UIImage(named: "\(item!.name.lowercased())") 114 | return cell 115 | } 116 | 117 | let didSelectParentCell = { (tableView: UITableView, indexPath: IndexPath, item: ParentCellModel?) -> Void in 118 | print("Parent cell \(item!.item.name) tapped") 119 | } 120 | 121 | let didSelectChildCell = { (tableView: UITableView, indexPath: IndexPath, item: CountryCellModel?) -> Void in 122 | print("Child cell \(item!.name) tapped") 123 | } 124 | 125 | let scrollViewDidScroll = { (scrollView: UIScrollView) -> Void in 126 | print(scrollView.contentOffset) 127 | 128 | } 129 | 130 | dataSourceProvider = DataSourceProvider( 131 | dataSource: dataSource, 132 | parentCellConfig: parentCellConfig, 133 | childCellConfig: childCellConfig, 134 | didSelectParentAtIndexPath: didSelectParentCell, 135 | didSelectChildAtIndexPath: didSelectChildCell, 136 | scrollViewDidScroll: scrollViewDidScroll, 137 | // Configure DataSourceProvider to have only one parent expanded at a time 138 | numberOfExpandedParentCells: .single 139 | ) 140 | 141 | tableView.dataSource = dataSourceProvider?.tableViewDataSource 142 | tableView.delegate = dataSourceProvider?.tableViewDelegate 143 | tableView.tableFooterView = UIView() 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Example/Views/AccordionViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccordionViewController.swift 3 | // Example 4 | // 5 | // Created by Victor Sigler on 8/8/18. 6 | // Copyright © 2018 Victor Sigler. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AccordionSwift 11 | 12 | class AccordionViewController: UIViewController { 13 | 14 | // MARK: - IBOutlets 15 | 16 | @IBOutlet weak var tableView: UITableView! 17 | 18 | // MARK: - Typealias 19 | 20 | typealias ParentCellModel = Parent 21 | typealias ParentCellConfig = CellViewConfig 22 | typealias ChildCellConfig = CellViewConfig 23 | 24 | // MARK: - Properties 25 | 26 | /// The Data Source Provider with the type of DataSource and the different models for the Parent and Child cell. 27 | var dataSourceProvider: DataSourceProvider, ParentCellConfig, ChildCellConfig>? 28 | 29 | // MARK: - UIViewController 30 | 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | configDataSource() 34 | 35 | navigationItem.title = "World Cup 2018" 36 | } 37 | } 38 | 39 | extension AccordionViewController { 40 | 41 | // MARK: - Methods 42 | 43 | /// Configure the data source 44 | private func configDataSource() { 45 | 46 | let groupA = Parent(state: .expanded, item: GroupCellModel(name: "Group A"), children: [CountryCellModel]()) 47 | 48 | let groupB = Parent(state: .expanded, item: GroupCellModel(name: "Group B"), 49 | children: [CountryCellModel(name: "Spain"), 50 | CountryCellModel(name: "Portugal"), 51 | CountryCellModel(name: "Iran"), 52 | CountryCellModel(name: "Morocco")] 53 | ) 54 | 55 | let groupC = Parent(state: .collapsed, item: GroupCellModel(name: "Group C"), 56 | children: [CountryCellModel(name: "France"), 57 | CountryCellModel(name: "Denmark"), 58 | CountryCellModel(name: "Peru"), 59 | CountryCellModel(name: "Australia")] 60 | ) 61 | 62 | let groupD = Parent(state: .expanded, item: GroupCellModel(name: "Group D"), 63 | children: [CountryCellModel(name: "Croatia"), 64 | CountryCellModel(name: "Argentina"), 65 | CountryCellModel(name: "Nigeria"), 66 | CountryCellModel(name: "Iceland")] 67 | ) 68 | 69 | let groupE = Parent(state: .collapsed, item: GroupCellModel(name: "Group E"), 70 | children: [CountryCellModel(name: "Brazil"), 71 | CountryCellModel(name: "Switzerland"), 72 | CountryCellModel(name: "Serbia"), 73 | CountryCellModel(name: "Costa Rica")] 74 | ) 75 | 76 | let groupF = Parent(state: .collapsed, item: GroupCellModel(name: "Group F"), 77 | children: [CountryCellModel(name: "Sweden"), 78 | CountryCellModel(name: "Mexico"), 79 | CountryCellModel(name: "South Korea"), 80 | CountryCellModel(name: "Germany")] 81 | ) 82 | 83 | let groupG = Parent(state: .collapsed, item: GroupCellModel(name: "Group G"), 84 | children: [CountryCellModel(name: "Belgium"), 85 | CountryCellModel(name: "England"), 86 | CountryCellModel(name: "Tunisia"), 87 | CountryCellModel(name: "Panama")] 88 | ) 89 | 90 | let groupH = Parent(state: .expanded, item: GroupCellModel(name: "Group H"), 91 | children: [CountryCellModel(name: "Colombia"), 92 | CountryCellModel(name: "Japan"), 93 | CountryCellModel(name: "Senegal"), 94 | CountryCellModel(name: "Poland")] 95 | ) 96 | 97 | let section0 = Section([groupA, groupB, groupC, groupD, groupE, groupF, groupG, groupH], headerTitle: nil) 98 | let dataSource = DataSource(sections: section0) 99 | 100 | let parentCellConfig = CellViewConfig( 101 | reuseIdentifier: "GroupCell") { (cell, model, tableView, indexPath) -> UITableViewCell in 102 | cell.textLabel?.text = model?.item.name 103 | return cell 104 | } 105 | 106 | let childCellConfig = CellViewConfig( 107 | reuseIdentifier: "CountryCell") { (cell, item, tableView, indexPath) -> CountryTableViewCell in 108 | cell.contryLabel.text = item?.name 109 | cell.countryImageView.image = UIImage(named: "\(item!.name.lowercased())") 110 | return cell 111 | } 112 | 113 | let didSelectParentCell = { (tableView: UITableView, indexPath: IndexPath, item: ParentCellModel?) -> Void in 114 | print("Parent cell \(item!.item.name) tapped") 115 | } 116 | 117 | let didSelectChildCell = { (tableView: UITableView, indexPath: IndexPath, item: CountryCellModel?) -> Void in 118 | print("Child cell \(item!.name) tapped") 119 | } 120 | 121 | let heightForParentCell = { (tableView: UITableView, indexPath: IndexPath, item: ParentCellModel?) -> CGFloat in 122 | return 55 123 | } 124 | 125 | let heightForChildCell = { (tableView: UITableView, indexPath: IndexPath, item: CountryCellModel?) -> CGFloat in 126 | return 40 127 | } 128 | 129 | let scrollViewDidScroll = { (scrollView: UIScrollView) -> Void in 130 | print(scrollView.contentOffset) 131 | 132 | } 133 | 134 | dataSourceProvider = DataSourceProvider( 135 | dataSource: dataSource, 136 | parentCellConfig: parentCellConfig, 137 | childCellConfig: childCellConfig, 138 | didSelectParentAtIndexPath: didSelectParentCell, 139 | didSelectChildAtIndexPath: didSelectChildCell, 140 | heightForParentCellAtIndexPath: heightForParentCell, 141 | heightForChildCellAtIndexPath: heightForChildCell, 142 | scrollViewDidScroll: scrollViewDidScroll 143 | ) 144 | 145 | tableView.dataSource = dataSourceProvider?.tableViewDataSource 146 | tableView.delegate = dataSourceProvider?.tableViewDelegate 147 | tableView.tableFooterView = UIView() 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /Example/Views/CountryTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CountryTableViewCell.swift 3 | // Example 4 | // 5 | // Created by Victor Sigler Lopez on 7/6/18. 6 | // Copyright © 2018 Victor Sigler. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class CountryTableViewCell: UITableViewCell { 12 | 13 | // MARK: - IBOutlets 14 | 15 | @IBOutlet weak var countryImageView: UIImageView! 16 | @IBOutlet weak var contryLabel: UILabel! 17 | 18 | override func awakeFromNib() { 19 | super.awakeFromNib() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Example/Views/MainTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainTableViewController.swift 3 | // Example 4 | // 5 | // Created by Victor Sigler on 8/8/18. 6 | // Copyright © 2018 Victor Sigler. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MainTableViewController: UITableViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | tableView.tableFooterView = UIView(frame: .zero) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ExampleTests/ExampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleTests.swift 3 | // ExampleTests 4 | // 5 | // Created by Victor Sigler Lopez on 7/5/18. 6 | // Copyright © 2018 Victor Sigler. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Example 11 | 12 | class ExampleTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /ExampleTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Victor Sigler 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "AccordionSwift", 7 | platforms: [.iOS(.v10)], 8 | products: [ 9 | .library( 10 | name: "AccordionSwift", 11 | targets: ["AccordionSwift"] 12 | ) 13 | ], 14 | targets: [ 15 | .target( 16 | name: "AccordionSwift", 17 | path: "Source" 18 | ) 19 | ] 20 | ) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Accordion Custom Image 3 |

4 | 5 |

6 | 7 | Pods Version 9 | 10 | 11 | Swift Version 13 | 14 | 15 | License Type 17 | 18 |

19 | 20 | ---------------- 21 | 22 | _An accordion/dropdown menu to integrate in your projects. This library is protocol oriented, type safe and the new version is inspired in [JSQDataSourcesKit](https://github.com/jessesquires/JSQDataSourcesKit) by Jesse Squires_. 23 | 24 | 25 | | | Main Features | 26 | ----------|----------------- 27 | 📱 | Compatible with iPhone / iPad 28 | 🔨 | Fully customizable cells 29 | 🚒 | Supports device rotation 30 | 🔥 | Written completely in Swift 31 | 32 | 33 | ## Requirements 💥 34 | - iOS 10.0+ 35 | - Xcode 10.2+ 36 | 37 | ## Installation 38 | 39 | ### CocoaPods 40 | 41 | [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command: 42 | 43 | ```bash 44 | $ gem install cocoapods 45 | ``` 46 | 47 | > CocoaPods 1.1.0+ is required to build AccordionSwift 2.0.0+. 48 | 49 | To integrate AccordionSwift into your Xcode project using CocoaPods, specify it in your `Podfile`: 50 | 51 | ```ruby 52 | source 'https://github.com/CocoaPods/Specs.git' 53 | platform :ios, '10.0' 54 | use_frameworks! 55 | 56 | target '' do 57 | pod 'AccordionSwift', '~> 2.0.0' 58 | end 59 | ``` 60 | 61 | Then, run the following command: 62 | 63 | ```bash 64 | $ pod install 65 | ``` 66 | 67 | ### Carthage 68 | 69 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate AccordionSwift into your Xcode project using Carthage, specify it in your `Cartfile`: 70 | 71 | ```ogdl 72 | github "Vkt0r/AccordionSwift" ~> 2.0.0 73 | ``` 74 | 75 | ## Usage ✨ 76 | After importing the framework, the library can be used in a `UITableViewController` or a `UIViewController` and offers full customization of the cells and data source: 77 | 78 | ```swift 79 | import UIKit 80 | import AccordionSwift 81 | 82 | class AccordionViewController: UIViewController { 83 | 84 | // MARK: - IBOutlets 85 | 86 | @IBOutlet weak var tableView: UITableView! 87 | 88 | // MARK: - Typealias 89 | 90 | typealias ParentCellModel = Parent 91 | typealias ParentCellConfig = CellViewConfig 92 | typealias ChildCellConfig = CellViewConfig 93 | 94 | // MARK: - Properties 95 | 96 | /// The Data Source Provider with the type of DataSource and the different models for the Parent and Chidl cell. 97 | var dataSourceProvider: DataSourceProvider, ParentCellConfig, ChildCellConfig>? 98 | 99 | // MARK: - UIViewController 100 | 101 | override func viewDidLoad() { 102 | super.viewDidLoad() 103 | configDataSource() 104 | 105 | navigationItem.title = "World Cup 2018" 106 | } 107 | } 108 | ``` 109 | The above example shows how to define a `CellViewConfig` for the parent and child cells respectively and how to define the `Parent` model. 110 | 111 | ```swift 112 | /// Defines a cell config type to handle a UITableViewCell 113 | public protocol CellViewConfigType { 114 | 115 | // MARK: Associated types 116 | 117 | /// The type of elements backing the collection view or table view. 118 | associatedtype Item 119 | 120 | /// The type of views that the configuration produces. 121 | associatedtype Cell: UITableViewCell 122 | 123 | // MARK: Methods 124 | 125 | func reuseIdentiferFor(item: Item?, indexPath: IndexPath) -> String 126 | 127 | @discardableResult 128 | func configure(cell: Cell, item: Item?, tableView: UITableView, indexPath: IndexPath) -> Cell 129 | } 130 | ``` 131 | 132 | Another step is to define the `DataSourceProvider` in charge of handling the data source and the `CellViewConfig` for each cell. 133 | The `DataSourceProvider` exposes the `numberOfExpandedParentCells` attribute which can be used to change the behavior of the accordion to only have a `single` item open at once or to have `multiple` items open at any given time. Take note that the default behavior is to have multiple items open. 134 | 135 | For a more detailed guide please see the [Example](https://github.com/Vkt0r/AccordionSwift/tree/master/Example) project. 136 | 137 | ## Screenshots 138 | screenshot 139 | 140 | #### Example of multiple cells open at a time 141 | screenshot 142 | 143 | #### Example of single cells open at a time 144 | screenshot 145 | 146 | ## TODO 147 | 148 | - [ ] Add unit tests for the library. 149 | - [ ] Add CircleCI integration. 150 | 151 | 152 | ## Feedback 153 | 154 | ## I've found a bug, or have a feature request 155 | 156 | Please raise a [GitHub issue](https://github.com/Vkt0r/AccordionMenu/issues). 😱 157 | 158 | ## Interested in contributing? 159 | 160 | Great! Please launch a [pull request](https://github.com/Vkt0r/AccordionMenu/pulls). 👍 161 | 162 | --------------------------------------- 163 | 164 | License: 165 | ================= 166 | The MIT License. See the [LICENSE file](LICENSE) for more information. 167 | -------------------------------------------------------------------------------- /Source/AccordionSwift.h: -------------------------------------------------------------------------------- 1 | // 2 | // AccordionSwift.h 3 | // AccordionSwift 4 | // 5 | // Created by Victor Sigler Lopez on 7/3/18. 6 | // Copyright © 2018 Victor Sigler. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for AccordionSwift. 12 | FOUNDATION_EXPORT double AccordionSwiftVersionNumber; 13 | 14 | //! Project version string for AccordionSwift. 15 | FOUNDATION_EXPORT const unsigned char AccordionSwiftVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Source/CellViewConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewCellConfigurator.swift 3 | // AccordionSwift 4 | // 5 | // Created by Victor Sigler Lopez on 7/3/18. 6 | // Copyright © 2018 Victor Sigler. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Defines a cell config type to handle a UITableViewCell 12 | public protocol CellViewConfigType { 13 | 14 | // MARK: Associated types 15 | 16 | /// The type of elements backing the collection view or table view. 17 | associatedtype Item 18 | 19 | /// The type of views that the configuration produces. 20 | associatedtype Cell: UITableViewCell 21 | 22 | // MARK: Methods 23 | 24 | func reuseIdentiferFor(item: Item?, indexPath: IndexPath) -> String 25 | 26 | @discardableResult 27 | func configure(cell: Cell, item: Item?, tableView: UITableView, indexPath: IndexPath) -> Cell 28 | } 29 | 30 | extension CellViewConfigType { 31 | 32 | public func tableCellFor(item: Item, tableView: UITableView, indexPath: IndexPath) -> Cell { 33 | let reuseIdentifier = reuseIdentiferFor(item: item, indexPath: indexPath) 34 | let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! Cell 35 | return configure(cell: cell, item: item, tableView: tableView, indexPath: indexPath) 36 | } 37 | } 38 | 39 | public struct CellViewConfig: CellViewConfigType { 40 | 41 | // MARK: Type aliases 42 | 43 | public typealias Cell = CellType 44 | 45 | public typealias CellConfigurator = (Cell, Item?, UITableView, IndexPath) -> Cell 46 | 47 | // MARK: Properties 48 | 49 | /// A unique identifier that describes the purpose of the cells that the config produces. 50 | /// The config dequeues cells from the collection view or table view with this reuse identifier. 51 | /// 52 | /// - Note: Clients are responsible for registering a cell for this identifier with the collection view or table view. 53 | public let reuseIdentifier: String 54 | 55 | /// A closure used to configure the views. 56 | public let cellConfigurator: CellConfigurator 57 | 58 | // MARK: Initialization 59 | 60 | /// Constructs a new reusable view config. 61 | /// 62 | /// - Parameters: 63 | /// - reuseIdentifier: The reuse identifier with which to dequeue reusable views. 64 | /// - cellConfigurator: The closure with which to configure views. 65 | public init(reuseIdentifier: String, cellConfigurator: @escaping CellConfigurator) { 66 | self.reuseIdentifier = reuseIdentifier 67 | self.cellConfigurator = cellConfigurator 68 | } 69 | 70 | public func reuseIdentiferFor(item: Item?, indexPath: IndexPath) -> String { 71 | return reuseIdentifier 72 | } 73 | 74 | public func configure(cell view: Cell, item: Item?, tableView: UITableView, indexPath: IndexPath) -> Cell { 75 | return cellConfigurator(view, item, tableView, indexPath) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Source/DataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataSource.swift 3 | // AccordionSwift 4 | // 5 | // Created by Victor Sigler Lopez on 7/3/18. 6 | // Copyright © 2018 Victor Sigler. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Defines a sectioned data source to be displayed in the UITableView 12 | public struct DataSource { 13 | 14 | // MARK: - Properties 15 | 16 | /// The sections in the data source. 17 | public var sections: [Section] 18 | 19 | // MARK: - Initialization 20 | 21 | /// Constructs a new DataSource. 22 | /// 23 | /// - Parameter sections: The sections for the data source. 24 | public init(_ sections: [Section]) { 25 | self.sections = sections 26 | } 27 | 28 | /// Constructs a new DataSource. 29 | /// 30 | /// - Parameter sections: The sections for the data source. 31 | public init(sections: Section...) { 32 | self.sections = sections 33 | } 34 | 35 | // MARK: - Methods 36 | 37 | /// Inserts the item at the specified index path 38 | /// 39 | /// - Parameters: 40 | /// - item: The item to be inserted. 41 | /// - indexPath: The index path specifying the location for the item. 42 | public mutating func insert(item: Item, at indexPath: IndexPath) { 43 | insert(item: item, atRow: indexPath.row, inSection: indexPath.section) 44 | } 45 | 46 | /// Inserts the item at the specified row and section. 47 | /// 48 | /// - Parameters: 49 | /// - item: The item to insert. 50 | /// - row: The row index of the item. 51 | /// - section: The section index of the item. 52 | public mutating func insert(item: Item, atRow row: Int, inSection section: Int) { 53 | guard section < numberOfSections() else { 54 | return 55 | } 56 | guard row <= numberOfItems(inSection: section) else { 57 | return 58 | } 59 | sections[section].items.insert(item, at: row) 60 | } 61 | 62 | /// Add the item at the specified section. 63 | /// 64 | /// - Parameters: 65 | /// - item: The item to be added. 66 | /// - section: The section location for the item. 67 | public mutating func append(_ item: Item, inSection section: Int) { 68 | guard let items = items(inSection: section) else { 69 | return 70 | } 71 | insert(item: item, atRow: items.endIndex, inSection: section) 72 | } 73 | 74 | /// Removes the item at the specified row and section. 75 | /// 76 | /// - Parameters: 77 | /// - row: The row location of the item. 78 | /// - section: The section location of the item. 79 | /// - Returns: The item removed, otherwise nil if it does not exist. 80 | @discardableResult 81 | public mutating func remove(atRow row: Int, inSection section: Int) -> Item? { 82 | guard item(atRow: row, inSection: section) != nil else { 83 | return nil 84 | } 85 | return sections[section].items.remove(at: row) 86 | } 87 | 88 | /// Removes the item at the specified index path. 89 | /// 90 | /// - Parameter indexPath: The index path specifying the location of the item. 91 | /// - Returns: The item at `indexPath`, otherwise nil if it does not exist. 92 | @discardableResult 93 | public mutating func remove(at indexPath: IndexPath) -> Item? { 94 | return remove(atRow: indexPath.row, inSection: indexPath.section) 95 | } 96 | 97 | // MARK: - Subscripts 98 | 99 | /// The section at the specified index. 100 | /// 101 | /// - Parameter index: The index of a section. 102 | public subscript(index: Int) -> Section { 103 | get { sections[index] } 104 | set { sections[index] = newValue } 105 | } 106 | 107 | /// The item at the specified index path. 108 | /// 109 | /// - Parameter indexPath: The index path of an item. 110 | public subscript(indexPath: IndexPath) -> Item { 111 | get { sections[indexPath.section].items[indexPath.row] } 112 | set { sections[indexPath.section].items[indexPath.row] = newValue } 113 | } 114 | } 115 | 116 | extension DataSource: DataSourceType { 117 | 118 | // MARK: - DataSourceType Methods 119 | 120 | public func numberOfSections() -> Int { 121 | return sections.count 122 | } 123 | 124 | public func numberOfItems(inSection section: Int) -> Int { 125 | guard section < sections.count else { return 0 } 126 | return sections[section].total 127 | } 128 | 129 | public func items(inSection section: Int) -> [Item]? { 130 | guard section < sections.count else { return nil } 131 | return sections[section].items 132 | } 133 | 134 | public func item(atRow row: Int, inSection section: Int) -> Item? { 135 | guard let items = items(inSection: section) else { return nil } 136 | guard row < items.count else { return nil } 137 | return items[row] 138 | } 139 | 140 | public func headerTitle(inSection section: Int) -> String? { 141 | guard section < sections.count else { return nil } 142 | return sections[section].headerTitle 143 | } 144 | 145 | public func footerTitle(inSection section: Int) -> String? { 146 | guard section < sections.count else { return nil } 147 | return sections[section].footerTitle 148 | } 149 | 150 | public func childItem(atRow row: Int, inSection section: Int, parentIndex: Int, currentPos: Int) -> Item.ChildItem? { 151 | guard let items = items(inSection: section) else { return nil } 152 | return items[parentIndex].children[row - currentPos - 1] 153 | } 154 | 155 | public mutating func toggleParentCell(toState state: State, inSection section: Int, atIndex index: Int) { 156 | guard var parents = items(inSection: section) else { return } 157 | 158 | parents[index].state = state 159 | var childrenCount = parents[index].children.count 160 | if state == .collapsed { 161 | childrenCount *= -1 162 | } 163 | 164 | sections[section].total += childrenCount 165 | } 166 | 167 | public func indexOfFirstExpandedParent() -> IndexPath? { 168 | for section in (0.. Int { 182 | var count = 0 183 | for section in (0.. Int { 192 | var count = 0 193 | for section in (0.. 21 | where ParentCellConfig.Item == DataSource.Item, ChildCellConfig.Item == DataSource.Item.ChildItem { 22 | 23 | // MARK: - Typealias 24 | 25 | public typealias DidSelectParentAtIndexPathClosure = (UITableView, IndexPath, DataSource.Item?) -> Void 26 | public typealias DidSelectChildAtIndexPathClosure = (UITableView, IndexPath, DataSource.Item.ChildItem?) -> Void 27 | 28 | public typealias HeightForChildAtIndexPathClosure = (UITableView, IndexPath, DataSource.Item.ChildItem?) -> CGFloat 29 | public typealias HeightForParentAtIndexPathClosure = (UITableView, IndexPath, DataSource.Item?) -> CGFloat 30 | 31 | private typealias ParentCell = IndexPath 32 | 33 | // MARK: - Properties 34 | 35 | /// The data source. 36 | public var dataSource: DataSource 37 | 38 | // The currently expanded parent 39 | private var expandedParent: ParentCell? = nil 40 | 41 | // Defines if accordion can have more than one cell open at a time 42 | private var numberOfExpandedParentCells: NumberOfExpandedParentCells 43 | 44 | /// The parent cell configuration. 45 | private let parentCellConfig: ParentCellConfig 46 | 47 | /// The child cell configuration. 48 | private let childCellConfig: ChildCellConfig 49 | 50 | /// The UITableViewDataSource 51 | private var _tableViewDataSource: TableViewDataSource? 52 | 53 | /// The UITableViewDelegate 54 | private var _tableViewDelegate: TableViewDelegate? 55 | 56 | /// The closure to be called when a Parent cell is selected 57 | private let didSelectParentAtIndexPath: DidSelectParentAtIndexPathClosure? 58 | 59 | /// The closure to be called when a Child cell is selected 60 | private let didSelectChildAtIndexPath: DidSelectChildAtIndexPathClosure? 61 | 62 | /// The closure to define the height of the Parent cell at the specified IndexPath 63 | private let heightForParentCellAtIndexPath: HeightForParentAtIndexPathClosure? 64 | 65 | /// The closure to define the height of the Child cell at the specified IndexPath 66 | private let heightForChildCellAtIndexPath: HeightForChildAtIndexPathClosure? 67 | 68 | /// The closure to be called when scrollView is scrolled 69 | private let scrollViewDidScroll: ScrollViewDidScrollClosure? 70 | 71 | // MARK: - Initialization 72 | 73 | /// Initializes a new data source provider. 74 | /// 75 | /// - Parameters: 76 | /// - dataSource: The data source. 77 | /// - cellConfig: The cell configuration. 78 | public init(dataSource: DataSource, 79 | parentCellConfig: ParentCellConfig, 80 | childCellConfig: ChildCellConfig, 81 | didSelectParentAtIndexPath: DidSelectParentAtIndexPathClosure? = nil, 82 | didSelectChildAtIndexPath: DidSelectChildAtIndexPathClosure? = nil, 83 | heightForParentCellAtIndexPath: HeightForParentAtIndexPathClosure? = nil, 84 | heightForChildCellAtIndexPath: HeightForChildAtIndexPathClosure? = nil, 85 | scrollViewDidScroll: ScrollViewDidScrollClosure? = nil, 86 | numberOfExpandedParentCells: NumberOfExpandedParentCells = .multiple 87 | ) { 88 | self.expandedParent = nil 89 | self.parentCellConfig = parentCellConfig 90 | self.childCellConfig = childCellConfig 91 | self.didSelectParentAtIndexPath = didSelectParentAtIndexPath 92 | self.didSelectChildAtIndexPath = didSelectChildAtIndexPath 93 | self.heightForParentCellAtIndexPath = heightForParentCellAtIndexPath 94 | self.heightForChildCellAtIndexPath = heightForChildCellAtIndexPath 95 | self.scrollViewDidScroll = scrollViewDidScroll 96 | self.numberOfExpandedParentCells = numberOfExpandedParentCells 97 | self.dataSource = dataSource 98 | 99 | let numberOfParentCells = dataSource.numberOfParents() 100 | assert(numberOfParentCells > 0, file: "DataSource has no parent cells") 101 | 102 | if numberOfExpandedParentCells == .single { 103 | assert(dataSource.numberOfExpandedParents() <= 1, file: "More than one expanded parent cell in dataSource") 104 | expandedParent = dataSource.indexOfFirstExpandedParent() 105 | } 106 | } 107 | 108 | // MARK: - Private Methods 109 | 110 | // Update the cells of the table based on the selected parent cell 111 | // 112 | // - Parameters: 113 | // - tableView: The UITableView to update 114 | // - item: The DataSource item that was selected 115 | // - currentPosition: The current position in the data source 116 | // - indexPaths: The last IndexPath of the new cells expanded 117 | // - parentIndex: The index of the parent item selected 118 | private func update(_ tableView: UITableView, _ item: DataSource.Item?, _ currentPosition: Int, _ indexPath: IndexPath, _ parentIndex: Int) { 119 | guard let item = item else { 120 | return 121 | } 122 | 123 | let numberOfChildren = item.children.count 124 | guard numberOfChildren > 0 else { 125 | return 126 | } 127 | 128 | let selectedParentCell: ParentCell = indexPath 129 | 130 | tableView.beginUpdates() 131 | toggle(selectedParentCell, withState: item.state, dataSourceIndex: parentIndex, tableView) 132 | tableView.endUpdates() 133 | 134 | // If the cells were expanded then we verify if they are inside the CGRect 135 | if item.state == .expanded { 136 | let lastCellIndexPath = IndexPath(item: indexPath.item + numberOfChildren, section: indexPath.section) 137 | // Scroll the new cells expanded in case of be outside the UITableView CGRect 138 | scrollCellIfNeeded(atIndexPath: lastCellIndexPath, tableView) 139 | } 140 | } 141 | 142 | // Toggle the state of the selected parent cell between expanded and collapsed 143 | // 144 | // - Parameters: 145 | // - currentState: The current state of the selected parent 146 | // - selectedParentCell: The actual cell selected 147 | private func toggle(_ selectedParentCell: ParentCell, withState currentState: State, dataSourceIndex: Int, _ tableView: UITableView) { 148 | switch (currentState, numberOfExpandedParentCells) { 149 | case (.expanded, _): 150 | // Collapse the parent and it's children 151 | collapse(parent: selectedParentCell, dataSourceIndex: dataSourceIndex, tableView) 152 | expandedParent = nil 153 | case (.collapsed, .single): 154 | // Expand the parent and it's children and collapse the expanded parent 155 | var mutableSelectedParent = selectedParentCell 156 | 157 | if let expandedParent = expandedParent { 158 | collapse(parent: expandedParent, dataSourceIndex: expandedParent.item, tableView) 159 | // Correct the selectedCell's index after collapsing expanded cell 160 | mutableSelectedParent = correctParentCellIndexAfterCollapse(collapsedCell: expandedParent, toBeExpandedParent: selectedParentCell) 161 | } 162 | 163 | expand(parent: mutableSelectedParent, dataSourceIndex: dataSourceIndex, tableView) 164 | expandedParent = mutableSelectedParent 165 | case (.collapsed, .multiple): 166 | // Expand the parent and it's children 167 | expand(parent: selectedParentCell, dataSourceIndex: dataSourceIndex, tableView) 168 | } 169 | } 170 | 171 | // Expand the parent cell and it's children 172 | // 173 | // - Parameters: 174 | // - parent: The actual parent cell to be expanded 175 | // - dataSourceIndex: The index of the parent in the dataSource 176 | // - tableView: The tableView to insert the children cells into 177 | private func expand(parent: ParentCell, dataSourceIndex: Int, _ tableView: UITableView) { 178 | let numberOfChildren = getNumberOfChildren(parent: parent, dataSourceIndex: dataSourceIndex) 179 | 180 | guard numberOfChildren > 0 else { 181 | return 182 | } 183 | 184 | let indexPaths = getIndexes(parent, numberOfChildren) 185 | tableView.insertRows(at: indexPaths, with: .fade) 186 | dataSource.toggleParentCell(toState: .expanded, inSection: parent.section, atIndex: dataSourceIndex) 187 | } 188 | 189 | // Get the number of children a parent cell has 190 | // 191 | // - Parameters: 192 | // - parent: The actual parent cell to be expanded 193 | // 194 | // - Returns: 195 | // - The number of children the parent cell has 196 | private func getNumberOfChildren(parent: ParentCell, dataSourceIndex: Int) -> Int { 197 | return dataSource.item(atRow: dataSourceIndex, inSection: parent.section)?.children.count ?? 0 198 | } 199 | 200 | // Correct the toBeExpanded cell's index after collapsing the expanded cell 201 | // 202 | // - Parameters: 203 | // - collapsedCell: The parent cell that has been collapsed 204 | // - toBeExpandedParent: The parent cell that will be expanded 205 | // 206 | // - Returns: 207 | // - The updated parent cell to be expanded 208 | private func correctParentCellIndexAfterCollapse(collapsedCell expandedParent: ParentCell, toBeExpandedParent: ParentCell) -> ParentCell { 209 | // If toBeExpandedParent index is larger than the currentlyExpandedParent index 210 | // then update the selected parent index to be correct due to the currentlyExpandedParent's children being removed 211 | if toBeExpandedParent.item > expandedParent.item { 212 | let numberChildrenOfExpandedParent = getNumberOfChildren(parent: expandedParent, dataSourceIndex: expandedParent.item) 213 | return IndexPath(item: (toBeExpandedParent.item - numberChildrenOfExpandedParent), section: toBeExpandedParent.section) 214 | } 215 | return toBeExpandedParent 216 | } 217 | 218 | 219 | // Collapse the parent cell and it's children 220 | // 221 | // - Parameters: 222 | // - parent: The actual parent cell to be expanded 223 | // - dataSourceIndex: The index of the parent in the dataSource 224 | // - tableView: The tableView to remove the children cells from 225 | private func collapse(parent: ParentCell, dataSourceIndex: Int, _ tableView: UITableView) { 226 | let numberOfChildren = getNumberOfChildren(parent: parent, dataSourceIndex: dataSourceIndex) 227 | 228 | guard numberOfChildren > 0 else { 229 | return 230 | } 231 | 232 | let indexPaths = getIndexes(parent, numberOfChildren) 233 | tableView.deleteRows(at: indexPaths, with: .fade) 234 | dataSource.toggleParentCell(toState: .collapsed, inSection: parent.section, atIndex: dataSourceIndex) 235 | } 236 | 237 | /// Get a list of index paths of the children of the parent cell 238 | /// 239 | /// - Parameters: 240 | /// - parent: The parent cell 241 | /// - numberOfChildren: The number of children the parent has 242 | private func getIndexes(_ parent: ParentCell, _ numberOfChildren: Int) -> [IndexPath] { 243 | let startPosition: Int = parent.item 244 | return (1...numberOfChildren).map { offset -> IndexPath in 245 | IndexPath(item: startPosition + offset, section: parent.section) 246 | } 247 | } 248 | 249 | /// Scroll the new cells expanded in case of be outside the UITableView CGRect 250 | /// 251 | /// - Parameters: 252 | /// - indexPaths: The last IndexPath of the new cells expanded 253 | /// - tableView: The UITableView to update 254 | private func scrollCellIfNeeded(atIndexPath indexPath: IndexPath, _ tableView: UITableView) { 255 | 256 | let cellRect = tableView.rectForRow(at: indexPath) 257 | 258 | // Scroll to the cell in case of not being visible 259 | if !tableView.bounds.contains(cellRect) { 260 | tableView.scrollToRow(at: indexPath, at: .bottom, animated: true) 261 | } 262 | } 263 | 264 | } 265 | 266 | extension DataSourceProvider { 267 | 268 | // MARK: - UITableViewDataSource 269 | 270 | /// The UITableViewDataSource protocol handler 271 | public var tableViewDataSource: UITableViewDataSource { 272 | if _tableViewDataSource == nil { 273 | _tableViewDataSource = configTableViewDataSource() 274 | } 275 | 276 | return _tableViewDataSource! 277 | } 278 | 279 | /// Config the UITableViewDataSource methods 280 | /// 281 | /// - Returns: An instance of the `TableViewDataSource` 282 | private func configTableViewDataSource() -> TableViewDataSource { 283 | 284 | let dataSource = TableViewDataSource( 285 | numberOfSections: { [unowned self] () -> Int in 286 | return self.dataSource.numberOfSections() 287 | }, 288 | numberOfItemsInSection: { [unowned self] (section) -> Int in 289 | return self.dataSource.numberOfItems(inSection: section) 290 | }) 291 | 292 | dataSource.tableCellForRowAtIndexPath = { [unowned self] (tableView, indexPath) -> UITableViewCell in 293 | 294 | let (parentPosition, isParent, currentPos) = self.dataSource.findParentOfCell(atIndexPath: indexPath) 295 | 296 | guard isParent else { 297 | let item = self.dataSource.childItem(at: indexPath, parentIndex: parentPosition, currentPos: currentPos) 298 | return self.childCellConfig.tableCellFor(item: item!, tableView: tableView, indexPath: indexPath) 299 | } 300 | 301 | let item = self.dataSource.item(at: IndexPath(item: parentPosition, section: indexPath.section))! 302 | return self.parentCellConfig.tableCellFor(item: item, tableView: tableView, indexPath: indexPath) 303 | } 304 | 305 | dataSource.tableTitleForHeaderInSection = { [unowned self] (section) -> String? in 306 | return self.dataSource.headerTitle(inSection: section) 307 | } 308 | 309 | dataSource.tableTitleForFooterInSection = { [unowned self] (section) -> String? in 310 | return self.dataSource.footerTitle(inSection: section) 311 | } 312 | 313 | return dataSource 314 | } 315 | } 316 | 317 | extension DataSourceProvider { 318 | 319 | // MARK: - UITableViewDelegate 320 | 321 | /// The UITableViewDataSource protocol handler 322 | public var tableViewDelegate: UITableViewDelegate { 323 | if _tableViewDelegate == nil { 324 | _tableViewDelegate = configTableViewDelegate() 325 | } 326 | 327 | return _tableViewDelegate! 328 | } 329 | 330 | /// Config the UITableViewDelegate methods 331 | /// 332 | /// - Returns: An instance of the `TableViewDelegate` 333 | private func configTableViewDelegate() -> TableViewDelegate { 334 | 335 | let delegate = TableViewDelegate() 336 | 337 | delegate.didSelectRowAtIndexPath = { [unowned self] (tableView, indexPath) -> Void in 338 | let (parentIndex, isParent, currentPosition) = self.dataSource.findParentOfCell(atIndexPath: indexPath) 339 | let item = self.dataSource.item(atRow: parentIndex, inSection: indexPath.section) 340 | 341 | if isParent { 342 | self.update(tableView, item, currentPosition, indexPath, parentIndex) 343 | self.didSelectParentAtIndexPath?(tableView, indexPath, item) 344 | } else { 345 | let index = indexPath.row - currentPosition - 1 346 | let childItem = index >= 0 ? item?.children[index] : nil 347 | self.didSelectChildAtIndexPath?(tableView, indexPath, childItem) 348 | } 349 | } 350 | 351 | delegate.heightForRowAtIndexPath = { [unowned self] (tableView, indexPath) -> CGFloat in 352 | let (parentIndex, isParent, currentPosition) = self.dataSource.findParentOfCell(atIndexPath: indexPath) 353 | let item = self.dataSource.item(atRow: parentIndex, inSection: indexPath.section) 354 | 355 | if isParent { 356 | return self.heightForParentCellAtIndexPath?(tableView, indexPath, item) ?? 40 357 | } 358 | 359 | let index = indexPath.row - currentPosition - 1 360 | let childItem = index >= 0 ? item?.children[index] : nil 361 | return self.heightForChildCellAtIndexPath?(tableView, indexPath, childItem) ?? 35 362 | } 363 | 364 | delegate.scrollViewDidScrollClosure = { [unowned self] (scrollView) -> Void in 365 | self.scrollViewDidScroll?(scrollView) 366 | } 367 | 368 | return delegate 369 | } 370 | } 371 | 372 | -------------------------------------------------------------------------------- /Source/DataSourceType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataSourceType.swift 3 | // AccordionSwift 4 | // 5 | // Created by Victor Sigler Lopez on 7/3/18. 6 | // Copyright © 2018 Victor Sigler. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Defines a sectioned data source to be displayed in the UITableView 12 | public protocol DataSourceType { 13 | 14 | // MARK: - Associated Type 15 | 16 | /// The type of items in the data source. 17 | associatedtype Item: ParentType 18 | 19 | // MARK: - Typealias 20 | 21 | /// An typealias with the position of the parent cell, if the current cell is parent or not and its current position 22 | typealias ParentResult = (parentPosition: Int, isParent: Bool, currentPos: Int) 23 | 24 | // MARK: - Methods 25 | 26 | /// - Returns: The number of items in the specified section. 27 | func numberOfSections() -> Int 28 | 29 | /// - Parameter section: A section index in the data source. 30 | /// - Returns: The number of items in the specified section. 31 | func numberOfItems(inSection section: Int) -> Int 32 | 33 | /// - Parameter section: A section in the data source. 34 | /// - Returns: The items in the specified section. 35 | func items(inSection section: Int) -> [Item]? 36 | 37 | /// - Parameters: 38 | /// - row: A row index in the data source. 39 | /// - section: A section index in the data source. 40 | /// - Returns: The item specified by the section number and row number, otherwise nil. 41 | func item(atRow row: Int, inSection section: Int) -> Item? 42 | 43 | /// - Parameters: 44 | /// - row: A row index in the data source. 45 | /// - section: A section index in the data source. 46 | /// - parentIndex: The index of the parent cell. 47 | /// - currentPos: The current position in the data source. 48 | /// - Returns: The child item specified by the by the section number, row number, parent index and current position 49 | func childItem(atRow row: Int, inSection section: Int, parentIndex: Int, currentPos: Int) -> Item.ChildItem? 50 | 51 | /// - Parameter section: A section in the data source. 52 | /// - Returns: The header title for the specified section. 53 | func headerTitle(inSection section: Int) -> String? 54 | 55 | /// - Parameter section: A section in the data source. 56 | /// - Returns: The footer title for the specified section. 57 | func footerTitle(inSection section: Int) -> String? 58 | 59 | /// Toggle the state of the parent cell in the data source between (.expanded and .collapsed). 60 | /// 61 | /// - Parameters: 62 | /// - state: The state the parent cell should obtain. 63 | /// - section: The index of the section in which the parent is located. 64 | /// - parentIndex: The index of the parent. 65 | mutating func toggleParentCell(toState state: State, inSection section: Int, atIndex parentIndex: Int) 66 | 67 | /// Get the total number of expanded parents cells in the data source 68 | /// 69 | /// - Returns: The number of parents cells in the expanded state 70 | func numberOfExpandedParents() -> Int 71 | 72 | /// Get the total number of parents cells in the data source 73 | /// 74 | /// - Returns: The number of parents cells 75 | func numberOfParents() -> Int 76 | 77 | /// Get the indexPath of the first expanded cell in the data source 78 | /// 79 | /// - Returns: IndexPath of the first expanded cell 80 | func indexOfFirstExpandedParent() -> IndexPath? 81 | } 82 | 83 | extension DataSourceType { 84 | 85 | /// Get the item at the specified IndexPath 86 | /// 87 | /// - Parameter indexPath: The index path of the cell. 88 | /// - Returns: The item specified by indexPath, otherwise nil. 89 | public func item(at indexPath: IndexPath) -> Item? { 90 | return item(atRow: indexPath.row, inSection: indexPath.section) 91 | } 92 | 93 | public func childItem(at indexPath: IndexPath, parentIndex: Int, currentPos: Int) -> Item.ChildItem? { 94 | return childItem(atRow: indexPath.row, inSection: indexPath.section, parentIndex: parentIndex, currentPos: currentPos) 95 | } 96 | 97 | /// - Parameter indexPath: The index path of the cell. 98 | /// - Returns: A tuple with the position of the parent cell, true if the current row is parent, otherwise false and the current position in the data source. 99 | public func findParentOfCell(atIndexPath indexPath: IndexPath) -> ParentResult { 100 | let row = indexPath.row 101 | guard let items = items(inSection: indexPath.section) else { return (0, true, 0) } 102 | return self.findParentOfCell(atRow: row, itemsInSection: items) 103 | } 104 | 105 | /// - Parameters: 106 | /// - row: A row index in the data source. 107 | /// - items: The items in the specified section. 108 | /// - Returns: A tuple with the position of the parent cell, true if the current row is parent, otherwise false and the current position in the data source. 109 | private func findParentOfCell(atRow row: Int, itemsInSection items: [Item]) -> ParentResult { 110 | 111 | guard row > 0 else { return (0, true, 0) } 112 | 113 | var position = 0 114 | var parent = 0 115 | var item = items[parent] 116 | 117 | repeat { 118 | position += (item.state == .expanded) ? item.children.count + 1 : 1 119 | parent += 1 120 | 121 | // check for the boundaries of the data source 122 | if parent < items.count { 123 | item = items[parent] 124 | } 125 | 126 | } while (position < row) 127 | 128 | // if it is a parent cell then the indexes should be equal 129 | guard position != row else { return (parent, position == row, position) } 130 | item = items[parent - 1] 131 | return (parent - 1, position == row, position - item.children.count - 1) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Source/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Source/Parent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parent.swift 3 | // AccordionSwift 4 | // 5 | // Created by Victor Sigler Lopez on 7/5/18. 6 | // Copyright © 2018 Victor Sigler. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Defines the state of a cell 12 | public enum State { 13 | case collapsed 14 | case expanded 15 | } 16 | 17 | public protocol ParentType { 18 | 19 | // MARK: - Associated Types 20 | 21 | /// The type of item in the ParentType 22 | associatedtype Item 23 | 24 | /// The type of item in the Child 25 | associatedtype ChildItem 26 | 27 | /// The current state of the cell 28 | var state: State { get set } 29 | 30 | /// The item in the parent cell 31 | var item: Item { get } 32 | 33 | /// The children of the cell 34 | var children: [ChildItem] { get } 35 | } 36 | 37 | /// Defines the Parent model for the cells 38 | public class Parent: ParentType { 39 | 40 | // MARK: - Properties 41 | 42 | /// The current state of the cell 43 | public var state: State 44 | 45 | /// The item in the parent cell 46 | public let item: Item 47 | 48 | /// The children of the cell 49 | public let children: [ChildItem] 50 | 51 | // MARK: - Initializer 52 | 53 | /// Construct a new Parent model 54 | /// 55 | /// - Parameters: 56 | /// - state: The state of the cell 57 | /// - item: The item in the cell 58 | /// - children: The children assigned to the cell 59 | public init(state: State = .collapsed, item: Item, children: [ChildItem]) { 60 | self.state = state 61 | self.item = item 62 | self.children = children 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Source/Section.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Section.swift 3 | // AccordionSwift 4 | // 5 | // Created by Victor Sigler Lopez on 7/3/18. 6 | // Copyright © 2018 Victor Sigler. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Defines a section of items 12 | public struct Section { 13 | 14 | // MARK: - Properties 15 | 16 | /// The elements in the section. 17 | public var items: [Item] 18 | 19 | /// The header title for the section. 20 | public let headerTitle: String? 21 | 22 | /// The footer title for the section. 23 | public let footerTitle: String? 24 | 25 | /// The number of elements in the section. 26 | public var count: Int { 27 | return items.count 28 | } 29 | 30 | /// The number of elements in the section for collapsed and expanded 31 | public var total: Int 32 | 33 | // MARK: - Initialization 34 | 35 | /// Constructs a new section. 36 | /// 37 | /// - Parameters: 38 | /// - items: The elements in the section. 39 | /// - headerTitle: The section header title. 40 | /// - footerTitle: The section footer title. 41 | public init(items: Item..., headerTitle: String? = nil, footerTitle: String? = nil) { 42 | self.init(items, headerTitle: headerTitle, footerTitle: footerTitle) 43 | } 44 | 45 | /// Constructs a new section. 46 | /// 47 | /// - Parameters: 48 | /// - items: The elements in the section. 49 | /// - headerTitle: The section header title. 50 | /// - footerTitle: The section footer title. 51 | public init(_ items: [Item], headerTitle: String? = nil, footerTitle: String? = nil) { 52 | self.items = items 53 | self.headerTitle = headerTitle 54 | self.footerTitle = footerTitle 55 | self.total = items.reduce(0) { (numberOfItems, cell) -> Int in 56 | let isExpanded = cell.state == .expanded 57 | return numberOfItems + (isExpanded ? cell.children.count + 1 : 1) 58 | } 59 | } 60 | 61 | // MARK: - Subscript 62 | 63 | /// - Parameter index: The index of the item to return. 64 | /// - Returns: The item at `index`. 65 | public subscript(index: Int) -> Item { 66 | get { return items[index] } 67 | set { items[index] = newValue } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Source/TableViewDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewDataSource.swift 3 | // AccordionSwift 4 | // 5 | // Created by Victor Sigler Lopez on 7/5/18. 6 | // Copyright © 2018 Victor Sigler. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | typealias NumberOfSectionsClosure = () -> Int 12 | typealias NumberOfItemsInSectionClosure = (Int) -> Int 13 | 14 | typealias TableCellForRowAtIndexPathClosure = (UITableView, IndexPath) -> UITableViewCell 15 | typealias TableTitleForHeaderInSectionClosure = (Int) -> String? 16 | typealias TableTitleForFooterInSectionClosure = (Int) -> String? 17 | 18 | /// Defines the methods in the UITableViewDataSource 19 | @objc final class TableViewDataSource: NSObject { 20 | 21 | // MARK: - Properties 22 | 23 | let numberOfSections: NumberOfSectionsClosure 24 | let numberOfItemsInSection: NumberOfItemsInSectionClosure 25 | 26 | var tableCellForRowAtIndexPath: TableCellForRowAtIndexPathClosure? 27 | var tableTitleForHeaderInSection: TableTitleForHeaderInSectionClosure? 28 | var tableTitleForFooterInSection: TableTitleForFooterInSectionClosure? 29 | 30 | // MARK: - Initializer 31 | 32 | /// Construct a new TableViewDataSource 33 | /// 34 | /// - Parameters: 35 | /// - numberOfSections: The number of sections closure in data source 36 | /// - numberOfItemsInSection: The number of items in the section closure in data source 37 | init(numberOfSections: @escaping NumberOfSectionsClosure, 38 | numberOfItemsInSection: @escaping NumberOfItemsInSectionClosure) { 39 | self.numberOfSections = numberOfSections 40 | self.numberOfItemsInSection = numberOfItemsInSection 41 | } 42 | } 43 | 44 | extension TableViewDataSource: UITableViewDataSource { 45 | 46 | // MARK: - UITableViewDataSource 47 | 48 | @objc func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 49 | return numberOfItemsInSection(section) 50 | } 51 | 52 | @objc func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 53 | return tableCellForRowAtIndexPath!(tableView, indexPath) 54 | } 55 | 56 | @objc func numberOfSections(in tableView: UITableView) -> Int { 57 | return numberOfSections() 58 | } 59 | 60 | @objc func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 61 | guard let closure = tableTitleForHeaderInSection else { return nil } 62 | return closure(section) 63 | } 64 | 65 | @objc func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { 66 | guard let closure = tableTitleForFooterInSection else { return nil } 67 | return closure(section) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Source/TableViewDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewDelegate.swift 3 | // AccordionSwift 4 | // 5 | // Created by Victor Sigler Lopez on 7/5/18. 6 | // Copyright © 2018 Victor Sigler. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | typealias DidSelectRowAtIndexPathClosure = (UITableView, IndexPath) -> Void 12 | typealias HeightForRowAtIndexPathClosure = (UITableView, IndexPath) -> CGFloat 13 | public typealias ScrollViewDidScrollClosure = (UIScrollView) -> Void 14 | 15 | @objc final class TableViewDelegate: NSObject { 16 | 17 | // MARK: - Properties 18 | 19 | var didSelectRowAtIndexPath: DidSelectRowAtIndexPathClosure? 20 | var heightForRowAtIndexPath: HeightForRowAtIndexPathClosure? 21 | 22 | var scrollViewDidScrollClosure: ScrollViewDidScrollClosure? 23 | } 24 | 25 | extension TableViewDelegate: UITableViewDelegate { 26 | 27 | @objc func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 28 | didSelectRowAtIndexPath?(tableView, indexPath) 29 | } 30 | 31 | @objc func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 32 | return heightForRowAtIndexPath!(tableView, indexPath) 33 | } 34 | } 35 | 36 | extension TableViewDelegate: UIScrollViewDelegate { 37 | @objc func scrollViewDidScroll(_ scrollView: UIScrollView) { 38 | scrollViewDidScrollClosure?(scrollView) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /repo-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vkt0r/AccordionSwift/503f4046ce9ade11c4ae48be59ff2e2a23644d73/repo-logo.png --------------------------------------------------------------------------------