Repository: azonov/ExpandableTable Branch: master Commit: b174053436ed Files: 36 Total size: 84.2 KB Directory structure: gitextract_apmunmq8/ ├── .gitignore ├── AZExpandable.podspec ├── Example/ │ ├── ExpandableCell/ │ │ ├── Supporting Files/ │ │ │ ├── AppDelegate.swift │ │ │ ├── Assets.xcassets/ │ │ │ │ └── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ ├── Base.lproj/ │ │ │ │ └── LaunchScreen.storyboard │ │ │ └── Info.plist │ │ ├── View Controller/ │ │ │ ├── BasicDemoViewController.swift │ │ │ ├── CellFactory.swift │ │ │ ├── ComplexDemoViewController.swift │ │ │ ├── LoadMoreDemoViewController.swift │ │ │ └── PickerItemsController.swift │ │ ├── ViewModels/ │ │ │ ├── TableViewModel.swift │ │ │ └── TableViewModelFactory.swift │ │ └── Views/ │ │ ├── Base.lproj/ │ │ │ └── Demo.storyboard │ │ ├── CenteredLabelCell.swift │ │ └── CenteredLabelCell.xib │ ├── ExpandableCell.xcodeproj/ │ │ ├── project.pbxproj │ │ └── project.xcworkspace/ │ │ └── contents.xcworkspacedata │ ├── ExpandableCell.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── IDEWorkspaceChecks.plist │ └── Podfile ├── Gemfile ├── LICENSE ├── README.md ├── Source/ │ ├── ExpandableCell-Bridging-Header.h │ ├── ExpandableTable/ │ │ ├── Cells/ │ │ │ ├── ActivityIndicatorCell.swift │ │ │ ├── DatePickerCell.swift │ │ │ ├── PickerCell.swift │ │ │ └── UIView+Autolayout.swift │ │ ├── ExpandableCellInfo.swift │ │ ├── ExpandableTable.swift │ │ ├── IndexPath+SyntaxSugar.swift │ │ └── PagingTable.swift │ └── Proxy/ │ ├── ExpandableProxy.h │ └── ExpandableProxy.m └── _config.yml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Mac OS X .DS_Store # Xcode ## Build generated build/ DerivedData ## Various settings *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata ## Other *.xccheckout *.moved-aside *.xcuserstate *.xcscmblueprint ## Obj-C/Swift specific *.hmap *.ipa ## Playgrounds timeline.xctimeline playground.xcworkspace # Swift Package Manager .build/ # Carthage Carthage/Build ExpandableCell.xcworkspace/contents.xcworkspacedata Pods/Manifest.lock Pods/Target Support Files/AZExpandable/AZExpandable-dummy.m Pods/Target Support Files/AZExpandable/AZExpandable-prefix.pch Pods/Target Support Files/AZExpandable/AZExpandable-umbrella.h Pods/Target Support Files/AZExpandable/AZExpandable.modulemap Pods/Target Support Files/AZExpandable/AZExpandable.xcconfig Pods/Target Support Files/AZExpandable/Info.plist Pods/Target Support Files/Pods-ExpandableCell/Info.plist Pods/Target Support Files/Pods-ExpandableCell/Pods-ExpandableCell-acknowledgements.markdown Pods/Target Support Files/Pods-ExpandableCell/Pods-ExpandableCell-acknowledgements.plist Pods/Target Support Files/Pods-ExpandableCell/Pods-ExpandableCell-dummy.m Pods/Target Support Files/Pods-ExpandableCell/Pods-ExpandableCell-frameworks.sh Pods/Target Support Files/Pods-ExpandableCell/Pods-ExpandableCell-resources.sh Pods/Target Support Files/Pods-ExpandableCell/Pods-ExpandableCell-umbrella.h Pods/Target Support Files/Pods-ExpandableCell/Pods-ExpandableCell.debug.xcconfig Pods/Target Support Files/Pods-ExpandableCell/Pods-ExpandableCell.modulemap Pods/Target Support Files/Pods-ExpandableCell/Pods-ExpandableCell.release.xcconfig ================================================ FILE: AZExpandable.podspec ================================================ Pod::Spec.new do |s| s.name = 'AZExpandable' s.version = '1.1.0' s.summary = 'AZExpandable is a lightweight proxy for UITableView to expand cells.' s.platform = :ios s.ios.deployment_target = '9.0' s.swift_version = '5.0' s.homepage = 'https://github.com/azonov/expandableTable.git' s.license = { :type => 'MIT', :file => 'LICENSE' } s.author = { 'Andrey Zonov' => 'andryzonov@gmail.com' } s.source = { :git => 'https://github.com/azonov/expandableTable.git', :tag => s.version.to_s } s.source_files = 'Source/**/*' end ================================================ FILE: Example/ExpandableCell/Supporting Files/AppDelegate.swift ================================================ // // AppDelegate.swift // ExpandableCell // // Created by Andrey Zonov on 18/10/2017. // Copyright © 2017 Andrey Zonov. All rights reserved. // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { return true } } ================================================ FILE: Example/ExpandableCell/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: Example/ExpandableCell/Supporting Files/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: Example/ExpandableCell/Supporting Files/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Demo UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: Example/ExpandableCell/View Controller/BasicDemoViewController.swift ================================================ // // BasicDemoViewController.swift // ExpandableCell // // Created by Andrey Zonov on 19/12/2017. // Copyright © 2017 Andrey Zonov. All rights reserved. // import Foundation import AZExpandable class BasicDemoViewController: UIViewController { // MARK: IBOutlet's @IBOutlet private weak var tableView: UITableView! // MARK: Private Properties private var expandableTable: ExpandableTable! private var expandedCell: ExpandedCellInfo? // MARK: Lifecycle override func viewDidLoad() { super.viewDidLoad() CellFactory.registerCells(for: tableView) expandableTable = ExpandableTable(with: tableView, infoProvider: self) } private func toggleItem(at indexPath: IndexPath) { guard expandedCell?.indexPath != indexPath else { expandableTable.unexpandCell() expandedCell = nil return } // Construct your cell here let cellType: ExpandedCellInfo.CellType = .custom { [weak self] indexPath -> (UITableViewCell) in let centeredCell = self?.tableView.dequeueReusableCell(withIdentifier: "CenteredLabelCell", for: indexPath) as! CenteredLabelCell centeredCell.configure(with: "Hint Inside your custom cell") centeredCell.backgroundColor = .lightGray return centeredCell } let cell = ExpandedCellInfo(for: indexPath, cellType: cellType) expandedCell = cell expandableTable.expandCell(cell) } } // MARK: - UITableViewDataSource extension BasicDemoViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 100 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "CenteredLabelCell", for: indexPath) as! CenteredLabelCell cell.configure(with: "Tap to reveal hint for \(indexPath.row) row") return cell } } // MARK: - UITableViewDelegate extension BasicDemoViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { toggleItem(at: indexPath) } } ================================================ FILE: Example/ExpandableCell/View Controller/CellFactory.swift ================================================ // // CellFactory.swift // ExpandableCell // // Created by Andrey Zonov on 08/11/2017. // Copyright © 2017 Andrey Zonov. All rights reserved. // import UIKit struct CellFactory { // MARK: Private private static let cellIdentifier = "CenteredLabelCell" // MARK: Public enum CellFactoryError: Error { case unsupportedType case wrongIdentifier } // MARK: Public static func cell(for cellModel: TableViewModel.Section.Cell, in tableView: UITableView, indexPath: IndexPath, cellAction: @escaping (() -> ())) throws -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? CenteredLabelCell else { throw CellFactoryError.wrongIdentifier } cell.configure(with: cellModel.title) return cell } static func registerCells(for tableView: UITableView) { let cellIdentifiers = [cellIdentifier] for cellIdentifier in cellIdentifiers { let nib = UINib(nibName: cellIdentifier, bundle: .main) tableView.register(nib, forCellReuseIdentifier: cellIdentifier) } } } ================================================ FILE: Example/ExpandableCell/View Controller/ComplexDemoViewController.swift ================================================ // // ComplexDemoViewController.swift // ExpandableCell // // Created by Andrey Zonov on 18/10/2017. // Copyright © 2017 Andrey Zonov. All rights reserved. // import UIKit import AZExpandable class ComplexDemoViewController: UIViewController { // MARK: IBOutlet's @IBOutlet private weak var tableView: UITableView! // MARK: Private Properties private var expandableTable: ExpandableTable! private var expandedCell: ExpandedCellInfo? private var tableViewModel = TableViewModelFactory.staticExpandableTable private var pickerController: PickerItemsController? // MARK: Lifecycle override func viewDidLoad() { super.viewDidLoad() CellFactory.registerCells(for: tableView) expandableTable = ExpandableTable(with: tableView, infoProvider: self) // DataSource and Delegate for PickerView, closure is about value changing pickerController = PickerItemsController { title in if let indexPath = self.expandedCell?.indexPath { var viewModel = self.tableViewModel.cell(for: indexPath) viewModel.title = title self.tableViewModel.replace(cell: viewModel, at: indexPath) self.tableView.reloadRows(at: [indexPath], with: .automatic) } } } private func toggleItem(at indexPath: IndexPath) { guard expandedCell?.indexPath != indexPath else { expandableTable.unexpandCell() expandedCell = nil return } let cellViewModel = tableViewModel.sections[indexPath.section].cells[indexPath.row] let cellType: ExpandedCellInfo.CellType switch cellViewModel.expandingType { case .date: cellType = .datePicker { datePicker in datePicker.minimumDate = Date() datePicker.addTarget(self.pickerController, action: #selector(PickerItemsController.datePickerDidChangeValue), for: .valueChanged) } case .picker: cellType = .picker { picker in picker.dataSource = self.pickerController picker.delegate = self.pickerController } case .custom: cellType = .custom { [weak self] indexPath -> (UITableViewCell) in let centeredCell = self?.tableView.dequeueReusableCell(withIdentifier: "CenteredLabelCell", for: indexPath) as! CenteredLabelCell centeredCell.configure(with: "Hint Inside your custom cell") centeredCell.backgroundColor = .lightGray return centeredCell } } let cell = ExpandedCellInfo(for: indexPath, cellType: cellType) expandedCell = cell expandableTable.expandCell(cell) } } // MARK: - UITableViewDataSource extension ComplexDemoViewController: UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { return tableViewModel.sections.count } func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { let section = tableViewModel.sections[section] return section.title } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return tableViewModel.sections[section].cells.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cellModel = tableViewModel.sections[indexPath.section].cells[indexPath.row] do { return try CellFactory.cell(for: cellModel, in: tableView, indexPath: indexPath) { [weak self] in self?.toggleItem(at: indexPath) } } catch { assertionFailure("handle \(error)") return UITableViewCell(style: .default, reuseIdentifier: "default") } } } // MARK: - UITableViewDelegate extension ComplexDemoViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { toggleItem(at: indexPath) } } ================================================ FILE: Example/ExpandableCell/View Controller/LoadMoreDemoViewController.swift ================================================ // // LoadMoreDemoViewController.swift // ExpandableCell // // Created by Andrey Zonov on 11/17/18. // Copyright © 2018 Andrey Zonov. All rights reserved. // import Foundation import AZExpandable class LoadMoreDemoViewController: UIViewController { // MARK: IBOutlet's @IBOutlet private weak var tableView: UITableView! // MARK: Private Properties private var pagingTable: PagingTable! private var rowsCount = 20 // MARK: Lifecycle override func viewDidLoad() { super.viewDidLoad() CellFactory.registerCells(for: tableView) pagingTable = PagingTable(tableView: tableView, infoProvider: self) { [weak self] paging in DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: { self?.rowsCount += 20 self?.tableView.reloadData() }) } } @IBAction func paginationValueChanged(_ sender: UISwitch) { if sender.isOn { try? pagingTable.addPagingIndicator() } else { try? pagingTable.removePagingIndicator() } } } // MARK: - UITableViewDataSource extension LoadMoreDemoViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return rowsCount } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "CenteredLabelCell", for: indexPath) as! CenteredLabelCell cell.configure(with: "\(indexPath.row) row") return cell } } // MARK: - UITableViewDelegate extension LoadMoreDemoViewController: UITableViewDelegate {} ================================================ FILE: Example/ExpandableCell/View Controller/PickerItemsController.swift ================================================ // // PickerItemsController.swift // ExpandableCell // // Created by Andrey Zonov on 18/12/2017. // Copyright © 2017 Andrey Zonov. All rights reserved. // import UIKit class PickerItemsController: NSObject, UIPickerViewDelegate, UIPickerViewDataSource { private var updateClosure: (String)->() init(updateClosure: @escaping (String)->()) { self.updateClosure = updateClosure } func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return 10 } func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return "row \(row) component \(component)" } func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { if let title = self.pickerView(pickerView, titleForRow: row, forComponent: component) { updateClosure(title) } } @objc func datePickerDidChangeValue(_ datePicker: UIDatePicker) { let title = DateFormatter.localizedString(from: datePicker.date, dateStyle: .medium, timeStyle: .medium) updateClosure(title) } } ================================================ FILE: Example/ExpandableCell/ViewModels/TableViewModel.swift ================================================ // // TableViewModel.swift // ExpandableCell // // Created by Andrey Zonov on 27/10/2017. // Copyright © 2017 Andrey Zonov. All rights reserved. // import Foundation enum ExpandingType { case date case picker case custom } struct TableViewModel { struct Section { struct Cell { var title: String var expandingType: ExpandingType } let title: String? var cells: [Cell] } static var expandable = TableViewModel(sections: []) var sections: [Section] } extension TableViewModel { func cell(for indexPath: IndexPath) -> TableViewModel.Section.Cell { return sections[indexPath.section].cells[indexPath.row] } mutating func replace(cell: TableViewModel.Section.Cell, at indexPath: IndexPath) { sections[indexPath.section].cells[indexPath.row] = cell } } ================================================ FILE: Example/ExpandableCell/ViewModels/TableViewModelFactory.swift ================================================ // // TableViewModelFactory.swift // ExpandableCell // // Created by Andrey Zonov on 08/11/2017. // Copyright © 2017 Andrey Zonov. All rights reserved. // import Foundation struct TableViewModelFactory { static var staticExpandableTable: TableViewModel { let helpCell1 = TableViewModel.Section.Cell(title: "Tap to reveal help1", expandingType: .custom) let helpCell2 = TableViewModel.Section.Cell(title: "Tap to reveal help2", expandingType: .custom) let helpCell3 = TableViewModel.Section.Cell(title: "Tap to reveal help3", expandingType: .custom) let dateCell1 = TableViewModel.Section.Cell(title: "Date 1", expandingType: .date) let dateCell2 = TableViewModel.Section.Cell(title: "Date 2", expandingType: .date) let dateCell3 = TableViewModel.Section.Cell(title: "Date 3", expandingType: .date) let pickerCell1 = TableViewModel.Section.Cell(title: "Picker 1", expandingType: .picker) let pickerCell2 = TableViewModel.Section.Cell(title: "Picker 2", expandingType: .picker) let pickerCell3 = TableViewModel.Section.Cell(title: "Picker 3", expandingType: .picker) let sectionHelp = TableViewModel.Section(title: "Help cells section", cells: [helpCell1, helpCell2, helpCell3]) let sectionDate = TableViewModel.Section(title: "Date cells section", cells: [dateCell1, dateCell2, dateCell3]) let sectionPicker = TableViewModel.Section(title: "Picker cells section", cells: [pickerCell1, pickerCell2, pickerCell3]) return TableViewModel(sections: [sectionHelp, sectionDate, sectionPicker]) } } ================================================ FILE: Example/ExpandableCell/Views/Base.lproj/Demo.storyboard ================================================ ================================================ FILE: Example/ExpandableCell/Views/CenteredLabelCell.swift ================================================ // // CenteredLabelCell.swift // ExpandableCell // // Created by Andrey Zonov on 08/11/2017. // Copyright © 2017 Andrey Zonov. All rights reserved. // import UIKit class CenteredLabelCell: UITableViewCell { // MARK: Private Outlets @IBOutlet private var label: UILabel! // MARK: Lifecycle override func awakeFromNib() { super.awakeFromNib() selectionStyle = .none } override func prepareForReuse() { super.prepareForReuse() label.text = "" backgroundColor = .white } // MARK: Public func configure(with title: String) { label.text = title } } ================================================ FILE: Example/ExpandableCell/Views/CenteredLabelCell.xib ================================================ ================================================ FILE: Example/ExpandableCell.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 48; objects = { /* Begin PBXBuildFile section */ 544BBBECEC7CBA6DC12582BA /* Pods_ExpandableCell.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9CAEC0521AA1804F5AD4A219 /* Pods_ExpandableCell.framework */; }; 5759872B1FA39FE200239B5E /* TableViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5759872A1FA39FE200239B5E /* TableViewModel.swift */; }; 579732361F971A43006DB071 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 579732351F971A43006DB071 /* AppDelegate.swift */; }; 579732381F971A43006DB071 /* ComplexDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 579732371F971A43006DB071 /* ComplexDemoViewController.swift */; }; 5797323B1F971A43006DB071 /* Demo.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 579732391F971A43006DB071 /* Demo.storyboard */; }; 5797323D1F971A43006DB071 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5797323C1F971A43006DB071 /* Assets.xcassets */; }; 579732401F971A43006DB071 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5797323E1F971A43006DB071 /* LaunchScreen.storyboard */; }; 57FF07FF1FB2CBC2008C0958 /* TableViewModelFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57FF07FE1FB2CBC2008C0958 /* TableViewModelFactory.swift */; }; 57FF08071FB2D957008C0958 /* CenteredLabelCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 57FF08061FB2D957008C0958 /* CenteredLabelCell.xib */; }; 57FF08091FB2D9BB008C0958 /* CenteredLabelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57FF08081FB2D9BB008C0958 /* CenteredLabelCell.swift */; }; 57FF080D1FB2DB73008C0958 /* CellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57FF080C1FB2DB73008C0958 /* CellFactory.swift */; }; FB3A0DBB1FE82D2A00D5C0A6 /* PickerItemsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB3A0DBA1FE82D2A00D5C0A6 /* PickerItemsController.swift */; }; FBAA601B219F0E24004CC2FA /* LoadMoreDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBAA601A219F0E24004CC2FA /* LoadMoreDemoViewController.swift */; }; FBDF2A041FE8FCC600889B28 /* BasicDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBDF2A031FE8FCC600889B28 /* BasicDemoViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 5759872A1FA39FE200239B5E /* TableViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewModel.swift; sourceTree = ""; }; 579732321F971A43006DB071 /* ExpandableCell.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExpandableCell.app; sourceTree = BUILT_PRODUCTS_DIR; }; 579732351F971A43006DB071 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 579732371F971A43006DB071 /* ComplexDemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComplexDemoViewController.swift; sourceTree = ""; }; 5797323A1F971A43006DB071 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Demo.storyboard; sourceTree = ""; }; 5797323C1F971A43006DB071 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 5797323F1F971A43006DB071 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 579732411F971A43006DB071 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 57FF07FE1FB2CBC2008C0958 /* TableViewModelFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewModelFactory.swift; sourceTree = ""; }; 57FF08061FB2D957008C0958 /* CenteredLabelCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CenteredLabelCell.xib; sourceTree = ""; }; 57FF08081FB2D9BB008C0958 /* CenteredLabelCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenteredLabelCell.swift; sourceTree = ""; }; 57FF080C1FB2DB73008C0958 /* CellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellFactory.swift; sourceTree = ""; }; 5B9DB0A27CB6D3B14B4FA504 /* Pods-ExpandableCell.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpandableCell.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ExpandableCell/Pods-ExpandableCell.debug.xcconfig"; sourceTree = ""; }; 8A984765D95495A0BB3DFE38 /* Pods-ExpandableCell.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ExpandableCell.release.xcconfig"; path = "Pods/Target Support Files/Pods-ExpandableCell/Pods-ExpandableCell.release.xcconfig"; sourceTree = ""; }; 9CAEC0521AA1804F5AD4A219 /* Pods_ExpandableCell.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ExpandableCell.framework; sourceTree = BUILT_PRODUCTS_DIR; }; FB3A0DBA1FE82D2A00D5C0A6 /* PickerItemsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerItemsController.swift; sourceTree = ""; }; FBAA601A219F0E24004CC2FA /* LoadMoreDemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadMoreDemoViewController.swift; sourceTree = ""; }; FBDF2A031FE8FCC600889B28 /* BasicDemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicDemoViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 5797322F1F971A43006DB071 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 544BBBECEC7CBA6DC12582BA /* Pods_ExpandableCell.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 0FDE01C8E17946A046C6AC32 /* Frameworks */ = { isa = PBXGroup; children = ( 9CAEC0521AA1804F5AD4A219 /* Pods_ExpandableCell.framework */, ); name = Frameworks; sourceTree = ""; }; 2CD19F1321C5BD94F03DDF41 /* Pods */ = { isa = PBXGroup; children = ( 5B9DB0A27CB6D3B14B4FA504 /* Pods-ExpandableCell.debug.xcconfig */, 8A984765D95495A0BB3DFE38 /* Pods-ExpandableCell.release.xcconfig */, ); name = Pods; sourceTree = ""; }; 579732291F971A43006DB071 = { isa = PBXGroup; children = ( 579732341F971A43006DB071 /* ExpandableCell */, 579732331F971A43006DB071 /* Products */, 2CD19F1321C5BD94F03DDF41 /* Pods */, 0FDE01C8E17946A046C6AC32 /* Frameworks */, ); sourceTree = ""; }; 579732331F971A43006DB071 /* Products */ = { isa = PBXGroup; children = ( 579732321F971A43006DB071 /* ExpandableCell.app */, ); name = Products; sourceTree = ""; }; 579732341F971A43006DB071 /* ExpandableCell */ = { isa = PBXGroup; children = ( 57FF08031FB2CEDF008C0958 /* ViewModels */, 57FF08021FB2CED3008C0958 /* Views */, 57FF08011FB2CEB8008C0958 /* View Controller */, 57FF08001FB2CE51008C0958 /* Supporting Files */, ); path = ExpandableCell; sourceTree = ""; }; 57FF08001FB2CE51008C0958 /* Supporting Files */ = { isa = PBXGroup; children = ( 5797323C1F971A43006DB071 /* Assets.xcassets */, 5797323E1F971A43006DB071 /* LaunchScreen.storyboard */, 579732411F971A43006DB071 /* Info.plist */, 579732351F971A43006DB071 /* AppDelegate.swift */, ); path = "Supporting Files"; sourceTree = ""; }; 57FF08011FB2CEB8008C0958 /* View Controller */ = { isa = PBXGroup; children = ( FBDF2A031FE8FCC600889B28 /* BasicDemoViewController.swift */, 579732371F971A43006DB071 /* ComplexDemoViewController.swift */, FBAA601A219F0E24004CC2FA /* LoadMoreDemoViewController.swift */, FB3A0DBA1FE82D2A00D5C0A6 /* PickerItemsController.swift */, 57FF080C1FB2DB73008C0958 /* CellFactory.swift */, ); path = "View Controller"; sourceTree = ""; }; 57FF08021FB2CED3008C0958 /* Views */ = { isa = PBXGroup; children = ( 579732391F971A43006DB071 /* Demo.storyboard */, 57FF08081FB2D9BB008C0958 /* CenteredLabelCell.swift */, 57FF08061FB2D957008C0958 /* CenteredLabelCell.xib */, ); path = Views; sourceTree = ""; }; 57FF08031FB2CEDF008C0958 /* ViewModels */ = { isa = PBXGroup; children = ( 5759872A1FA39FE200239B5E /* TableViewModel.swift */, 57FF07FE1FB2CBC2008C0958 /* TableViewModelFactory.swift */, ); path = ViewModels; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 579732311F971A43006DB071 /* ExpandableCell */ = { isa = PBXNativeTarget; buildConfigurationList = 579732441F971A43006DB071 /* Build configuration list for PBXNativeTarget "ExpandableCell" */; buildPhases = ( 3B9DABDA8FCD5AA9B2C3ADF1 /* [CP] Check Pods Manifest.lock */, 5797322E1F971A43006DB071 /* Sources */, 5797322F1F971A43006DB071 /* Frameworks */, 579732301F971A43006DB071 /* Resources */, C68FC4EE3E073A697ABC5066 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = ExpandableCell; productName = ExpandableCell; productReference = 579732321F971A43006DB071 /* ExpandableCell.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 5797322A1F971A43006DB071 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0900; LastUpgradeCheck = 1010; ORGANIZATIONNAME = "Andrey Zonov"; TargetAttributes = { 579732311F971A43006DB071 = { CreatedOnToolsVersion = 9.0.1; LastSwiftMigration = 1030; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 5797322D1F971A43006DB071 /* Build configuration list for PBXProject "ExpandableCell" */; compatibilityVersion = "Xcode 8.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 579732291F971A43006DB071; productRefGroup = 579732331F971A43006DB071 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 579732311F971A43006DB071 /* ExpandableCell */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 579732301F971A43006DB071 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 579732401F971A43006DB071 /* LaunchScreen.storyboard in Resources */, 5797323D1F971A43006DB071 /* Assets.xcassets in Resources */, 5797323B1F971A43006DB071 /* Demo.storyboard in Resources */, 57FF08071FB2D957008C0958 /* CenteredLabelCell.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 3B9DABDA8FCD5AA9B2C3ADF1 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-ExpandableCell-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; C68FC4EE3E073A697ABC5066 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-ExpandableCell/Pods-ExpandableCell-frameworks.sh", "${BUILT_PRODUCTS_DIR}/AZExpandable/AZExpandable.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AZExpandable.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ExpandableCell/Pods-ExpandableCell-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 5797322E1F971A43006DB071 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 5759872B1FA39FE200239B5E /* TableViewModel.swift in Sources */, 57FF08091FB2D9BB008C0958 /* CenteredLabelCell.swift in Sources */, 579732381F971A43006DB071 /* ComplexDemoViewController.swift in Sources */, 57FF07FF1FB2CBC2008C0958 /* TableViewModelFactory.swift in Sources */, FB3A0DBB1FE82D2A00D5C0A6 /* PickerItemsController.swift in Sources */, 57FF080D1FB2DB73008C0958 /* CellFactory.swift in Sources */, FBAA601B219F0E24004CC2FA /* LoadMoreDemoViewController.swift in Sources */, FBDF2A041FE8FCC600889B28 /* BasicDemoViewController.swift in Sources */, 579732361F971A43006DB071 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 579732391F971A43006DB071 /* Demo.storyboard */ = { isa = PBXVariantGroup; children = ( 5797323A1F971A43006DB071 /* Base */, ); name = Demo.storyboard; sourceTree = ""; }; 5797323E1F971A43006DB071 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 5797323F1F971A43006DB071 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 579732421F971A43006DB071 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.2; }; name = Debug; }; 579732431F971A43006DB071 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 4.2; VALIDATE_PRODUCT = YES; }; name = Release; }; 579732451F971A43006DB071 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 5B9DB0A27CB6D3B14B4FA504 /* Pods-ExpandableCell.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = "ExpandableCell/Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.azonov.ExpandableCell; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 579732461F971A43006DB071 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 8A984765D95495A0BB3DFE38 /* Pods-ExpandableCell.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = "ExpandableCell/Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.azonov.ExpandableCell; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 5797322D1F971A43006DB071 /* Build configuration list for PBXProject "ExpandableCell" */ = { isa = XCConfigurationList; buildConfigurations = ( 579732421F971A43006DB071 /* Debug */, 579732431F971A43006DB071 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 579732441F971A43006DB071 /* Build configuration list for PBXNativeTarget "ExpandableCell" */ = { isa = XCConfigurationList; buildConfigurations = ( 579732451F971A43006DB071 /* Debug */, 579732461F971A43006DB071 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 5797322A1F971A43006DB071 /* Project object */; } ================================================ FILE: Example/ExpandableCell.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Example/ExpandableCell.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: Example/ExpandableCell.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: Example/Podfile ================================================ use_frameworks! target 'ExpandableCell' do pod 'AZExpandable', :path => '../' end ================================================ FILE: Gemfile ================================================ # frozen_string_literal: true source "https://rubygems.org" git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } gem "cocoapods" ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017 Andrey Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ ![AZExpandable](https://raw.githubusercontent.com/azonov/expandableTable/master/logo_long.png) **AZExpandable** is a lightweight proxy for UITableView to expand cells. It incapsulates native NSProxy mechanism inside and gives swifty api outside.

General advantages: **No Subclassing**, **No Swizzling**, **Easy to intagrate** - [Requirements](#requirements) - [Communication](#communication) - [Installation](#installation) - [Usage](#usage) - [License](#license) ## Example ![Alt Text](https://media.giphy.com/media/l0HU17JvuEjnkLskg/giphy.gif) ## Requirements - iOS 8.0+ - Xcode 9.2+ - Swift 4.0+ ## Communication - If you'd like to **ask a general question**, use [Twitter](http://twitter.com/avzonov). - If you **found a bug**, open an issue. - If you **have a feature request**, open an issue. - If you **want to contribute**, submit a pull request. ## Installation ### CocoaPods [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command: ```bash $ gem install cocoapods ``` > CocoaPods 1.1+ is required to build AZExpandable. To integrate AZExpandable into your Xcode project using CocoaPods, specify it in your `Podfile`: ```ruby source 'https://github.com/CocoaPods/Specs.git' use_frameworks! target '' do pod 'AZExpandable' end ``` Then, run the following command: ```bash $ pod install ``` ## Usage ```swift private var expandableTable: ExpandableTable!// Expanding Table Proxy override func viewDidLoad() { super.viewDidLoad() // infoProvider - UITableViewDelegate & UITableViewDataSource expandableTable = ExpandableTable(with: tableView, infoProvider: self) } func expandCell(at indexPath: IndexPath) { let cellClosure: CellClosure = { (IndexPath) -> (UITableViewCell) in //Your custom expanding cell return self.tableView.dequeueReusableCell(withIdentifier: "Identifier", for: indexPath) } expandableTable.expandCell(ExpandedCellInfo(for: indexPath, cellType: .custom(cellClosure))) } func unexpandCell() { expandableTable.unexpandCell() } ``` ## License AZExpandable is released under the MIT license. [See LICENSE](https://github.com/azonov/expandableTable/blob/master/LICENSE) for details. ================================================ FILE: Source/ExpandableCell-Bridging-Header.h ================================================ // // Use this file to import your target's public headers that you would like to expose to Swift. // #import "ExpandableProxy.h" ================================================ FILE: Source/ExpandableTable/Cells/ActivityIndicatorCell.swift ================================================ // // ActivityIndicatorCell.swift // AZExpandable // // Created by Andrey Zonov on 11/17/18. // import UIKit class ActivityIndicatorCell: UITableViewCell { // MARK: Public Properties lazy var activityIndicator: UIActivityIndicatorView = { let frame = CGRect(origin: .zero, size: CGSize(width: 50, height: 50)) let activityIndicator = UIActivityIndicatorView(frame: frame) activityIndicator.color = .black activityIndicator.hidesWhenStopped = false return activityIndicator }() // MARK: Lifecycle init() { super.init(style: .default, reuseIdentifier: String(describing: type(of: self))) addSubview(activityIndicator) activityIndicator.makeEdgesEqualToSuperView() activityIndicator.startAnimating() heightAnchor.constraint(equalToConstant: 44).isActive = true separatorInset = UIEdgeInsets(top: 0, left: CGFloat.infinity, bottom: 0, right: 0) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } ================================================ FILE: Source/ExpandableTable/Cells/DatePickerCell.swift ================================================ // // DatePickerCell.swift // AZExpandable // // Created by Andrey Zonov on 20/10/2017. // import UIKit class DatePickerCell: UITableViewCell { // MARK: Public Properties weak var datePicker: UIDatePicker! // MARK: Lifecycle init() { let datePicker = UIDatePicker(frame: .zero) self.datePicker = datePicker super.init(style: .default, reuseIdentifier: String(describing: type(of: self))) addSubview(datePicker) datePicker.makeEdgesEqualToSuperView() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } ================================================ FILE: Source/ExpandableTable/Cells/PickerCell.swift ================================================ // // PickerCell.swift // Pods-ExpandableCell // // Created by Andrey Zonov on 27/10/2017. // import UIKit class PickerCell: UITableViewCell { // MARK: Public Properties weak var picker: UIPickerView! // MARK: Lifecycle init() { let picker = UIPickerView(frame: .zero) self.picker = picker super.init(style: .default, reuseIdentifier: String(describing: type(of: self))) addSubview(picker) picker.makeEdgesEqualToSuperView() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } ================================================ FILE: Source/ExpandableTable/Cells/UIView+Autolayout.swift ================================================ // // UIView+Autolayout.swift // AZExpandable // // Created by Andrey Zonov on 11/16/18. // import UIKit extension UIView { func makeEdgesEqualToSuperView() { guard let superView = superview else { return } translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ topAnchor.constraint(equalTo: superView.topAnchor), bottomAnchor.constraint(equalTo: superView.bottomAnchor), leftAnchor.constraint(equalTo: superView.leftAnchor), rightAnchor.constraint(equalTo: superView.rightAnchor)]) } } ================================================ FILE: Source/ExpandableTable/ExpandableCellInfo.swift ================================================ // // ExpandableCellInfo.swift // Pods // // Created by Andrey Zonov on 20/10/2017. // import UIKit public typealias CellClosure = ((IndexPath) -> (UITableViewCell)) public typealias DatePickerSetupClosure = ((UIDatePicker) -> ()) public typealias PickerSetupClosure = ((UIPickerView) -> ()) public struct ExpandedCellInfo: Equatable { // MARK: Private Data Structures public enum CellType { case datePicker(DatePickerSetupClosure?) case picker(PickerSetupClosure) case custom(CellClosure) } // MARK: Public Properties public var indexPath: IndexPath public var cellType: CellType // MARK: Lifecycle public init(for indexPath: IndexPath, cellType: CellType) { self.indexPath = indexPath self.cellType = cellType } // MARK: Public public static func ==(lhs: ExpandedCellInfo, rhs: ExpandedCellInfo) -> Bool { return lhs.indexPath == rhs.indexPath } func isHigherInSameSection(than hint: ExpandedCellInfo) -> Bool { return hint.indexPath.section == indexPath.section && hint.indexPath.row > indexPath.row } func computedIndexPath(from indexPath: IndexPath) -> IndexPath { var computedIndexPath = indexPath if indexPath.section == self.indexPath.section && indexPath.row > self.indexPath.row { computedIndexPath.decrementRow() } return computedIndexPath } } ================================================ FILE: Source/ExpandableTable/ExpandableTable.swift ================================================ // // ExpandableTable.swift // ExpandableCell // // Created by Andrey Zonov on 18/10/2017. // Copyright © 2017 Andrey Zonov. All rights reserved. // import Foundation public typealias TableInfoProvider = UITableViewDelegate & UITableViewDataSource public class ExpandableTable: NSObject { // MARK: Private Properties private var proxy: ExpandableProxy? private var expandedCell: ExpandedCellInfo? private weak var tableView: UITableView! private weak var infoProvider: TableInfoProvider! // MARK: Lifecycle public init(with tableView: UITableView, infoProvider: TableInfoProvider) { self.tableView = tableView self.infoProvider = infoProvider super.init() proxy = ExpandableProxy(tableDelegate: infoProvider, proxyDelegate: self) tableView.dataSource = proxy tableView.delegate = proxy } // MARK: Public public func expandCell(_ cell: ExpandedCellInfo) { var resultCell = cell resultCell.indexPath.incrementRow() guard expandedCell != resultCell else { return } if expandedCell != nil { unexpandCell() } expandedCell = resultCell tableView.beginUpdates() tableView?.insertRows(at: [resultCell.indexPath], with: .middle) tableView.endUpdates() } public func unexpandCell() { guard let indexPath = expandedCell?.indexPath else { return } expandedCell = nil tableView.beginUpdates() tableView?.deleteRows(at: [indexPath], with: .middle) tableView.endUpdates() } } // MARK: - UITableViewDataSource extension ExpandableTable: UITableViewDataSource { public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let rows = infoProvider.tableView(tableView ,numberOfRowsInSection: section) guard let expandedCell = expandedCell else { return rows } return expandedCell.indexPath.section == section ? rows + 1 : rows } public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard indexPath != expandedCell?.indexPath else { switch expandedCell!.cellType { case .custom(let cellClosure): return cellClosure(indexPath) case .datePicker(let setupClosure): let cell = DatePickerCell() setupClosure?(cell.datePicker) return cell case .picker(let setupClosure): let cell = PickerCell() setupClosure(cell.picker) return cell } } let computedIndexPath = expandedCell?.computedIndexPath(from: indexPath) ?? indexPath return infoProvider.tableView(tableView, cellForRowAt: computedIndexPath) } } // MARK: - UITableViewDelegate extension ExpandableTable: UITableViewDelegate { public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { guard indexPath != expandedCell?.indexPath else { return UITableView.automaticDimension } let computedIndexPath = expandedCell?.computedIndexPath(from: indexPath) ?? indexPath return infoProvider.tableView?(tableView, heightForRowAt: computedIndexPath) ?? UITableView.automaticDimension } public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if indexPath != expandedCell?.indexPath { let computedIndexPath = expandedCell?.computedIndexPath(from: indexPath) ?? indexPath infoProvider.tableView?(tableView, didSelectRowAt: computedIndexPath) } } } ================================================ FILE: Source/ExpandableTable/IndexPath+SyntaxSugar.swift ================================================ // // IndexPath+SyntaxSugar.swift // Pods // // Created by Andrey Zonov on 20/10/2017. // import Foundation extension IndexPath { mutating func incrementRow() { row += 1 } mutating func decrementRow() { row -= 1 } } ================================================ FILE: Source/ExpandableTable/PagingTable.swift ================================================ // // PagingTable.swift // AZExpandable // // Created by Andrey Zonov on 18/11/2018. // import UIKit public typealias NextPageClosure = (PagingTable) -> () public class PagingTable: NSObject { public enum PagingError: Error { case described(string: String) } // MARK: Private Properties private var proxy: ExpandableProxy? private var loadMoreIndexPath: IndexPath? private weak var tableView: UITableView! private weak var infoProvider: TableInfoProvider! private var nextPageClosure: NextPageClosure // MARK: Lifecycle public init(tableView: UITableView, infoProvider: TableInfoProvider, nextPage: @escaping NextPageClosure) { self.tableView = tableView self.infoProvider = infoProvider self.nextPageClosure = nextPage super.init() proxy = ExpandableProxy(tableDelegate: infoProvider, proxyDelegate: self) tableView.dataSource = proxy tableView.delegate = proxy } public func addPagingIndicator() throws { try makePagingIndicatorVisible(true) } public func removePagingIndicator() throws { try makePagingIndicatorVisible(false) } private func makePagingIndicatorVisible(_ isVisible: Bool) throws { let sectionsCount = tableView.numberOfSections guard sectionsCount > 0 else { throw PagingError.described(string: "numberOfSections > 0") } let rowsCount = tableView.numberOfRows(inSection: sectionsCount - 1) guard rowsCount > 0 else { throw PagingError.described(string: "rowsCount > 0") } var indexPath = IndexPath(item: rowsCount - 1, section: sectionsCount - 1) loadMoreIndexPath = isVisible ? indexPath : nil tableView.beginUpdates() if isVisible { indexPath.incrementRow() tableView?.insertRows(at: [indexPath], with: .top) } else { tableView?.deleteRows(at: [indexPath], with: .bottom) } tableView.endUpdates() } } // MARK: - UITableViewDataSource extension PagingTable: UITableViewDataSource { public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let rows = infoProvider.tableView(tableView ,numberOfRowsInSection: section) if loadMoreIndexPath?.section == section { loadMoreIndexPath?.row = rows return rows + 1 } else { return rows } } public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard loadMoreIndexPath != indexPath else { return ActivityIndicatorCell() } return infoProvider.tableView(tableView, cellForRowAt: indexPath) } } // MARK: - UITableViewDelegate extension PagingTable: UITableViewDelegate { public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { guard loadMoreIndexPath != indexPath else { nextPageClosure(self) return } infoProvider.tableView?(tableView, willDisplay: cell, forRowAt: indexPath) } public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { guard loadMoreIndexPath != indexPath else { return UITableView.automaticDimension } return infoProvider.tableView?(tableView, heightForRowAt: indexPath) ?? UITableView.automaticDimension } public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard loadMoreIndexPath != indexPath else { return } infoProvider.tableView?(tableView, didSelectRowAt: indexPath) } } ================================================ FILE: Source/Proxy/ExpandableProxy.h ================================================ // // ExpandableProxy.h // ExpandableCell // // Created by Andrey Zonov on 18/10/2017. // Copyright © 2017 Andrey Zonov. All rights reserved. // #import #import @interface ExpandableProxy : NSProxy - (instancetype)initWithTableDelegate:(id )tableDelegate proxyDelegate:(id )proxyDelegate; @end ================================================ FILE: Source/Proxy/ExpandableProxy.m ================================================ // // ExpandableProxy.m // ExpandableCell // // Created by Andrey Zonov on 18/10/2017. // Copyright © 2017 Andrey Zonov. All rights reserved. // #import "ExpandableProxy.h" @interface ExpandableProxy() @property (nonatomic, weak) id proxyDelegate; @property (nonatomic, weak) id tableDelegate; @end @implementation ExpandableProxy - (instancetype)initWithTableDelegate:(id )tableDelegate proxyDelegate:(id )proxyDelegate { self.tableDelegate = tableDelegate; self.proxyDelegate = proxyDelegate; return self; } - (id)forwardingTargetForSelector:(SEL)aSelector { if ([self.proxyDelegate respondsToSelector:aSelector]) { return self.proxyDelegate; } else if ([self.tableDelegate respondsToSelector:aSelector]) { return self.tableDelegate; } return nil; } - (BOOL)respondsToSelector:(SEL)aSelector { if ([self.proxyDelegate respondsToSelector:aSelector]) { return YES; } else if ([self.tableDelegate respondsToSelector:aSelector]) { return YES; } return NO; } - (void)forwardInvocation:(NSInvocation *)invocation { [invocation invokeWithTarget:self.tableDelegate]; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { return [self.tableDelegate methodSignatureForSelector:sel]; } @end ================================================ FILE: _config.yml ================================================ theme: jekyll-theme-architect