Giter Site home page Giter Site logo

vkt0r / accordionswift Goto Github PK

View Code? Open in Web Editor NEW
170.0 11.0 31.0 4.88 MB

The best way of implement an accordion menu using an UITableView in Swift

License: MIT License

Swift 96.60% Objective-C 0.97% Ruby 2.44%
swift accordion xcode cocoapods tableview

accordionswift's Introduction

Accordion Custom Image

Pods Version Swift Version License Type


An accordion/dropdown menu to integrate in your projects. This library is protocol oriented, type safe and the new version is inspired in JSQDataSourcesKit by Jesse Squires.

Main Features
๐Ÿ“ฑ Compatible with iPhone / iPad
๐Ÿ”จ Fully customizable cells
๐Ÿš’ Supports device rotation
๐Ÿ”ฅ Written completely in Swift

Requirements ๐Ÿ’ฅ

  • iOS 10.0+
  • Xcode 10.2+

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:

$ gem install cocoapods

CocoaPods 1.1.0+ is required to build AccordionSwift 2.0.0+.

To integrate AccordionSwift into your Xcode project using CocoaPods, specify it in your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!

target '<Your Target Name>' do
    pod 'AccordionSwift', '~> 2.0.0'
end

Then, run the following command:

$ pod install

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:

github "Vkt0r/AccordionSwift" ~> 2.0.0

Usage โœจ

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:

import UIKit
import AccordionSwift

class AccordionViewController: UIViewController {
    
    // MARK: - IBOutlets
    
    @IBOutlet weak var tableView: UITableView!
    
    // MARK: - Typealias
    
    typealias ParentCellModel = Parent<GroupCellModel, CountryCellModel>
    typealias ParentCellConfig = CellViewConfig<ParentCellModel, UITableViewCell>
    typealias ChildCellConfig = CellViewConfig<CountryCellModel, CountryTableViewCell>
    
    // MARK: - Properties
    
    /// The Data Source Provider with the type of DataSource and the different models for the Parent and Chidl cell.
    var dataSourceProvider: DataSourceProvider<DataSource<ParentCellModel>, ParentCellConfig, ChildCellConfig>?
    
    // MARK: - UIViewController
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configDataSource()
        
        navigationItem.title = "World Cup 2018"
    }
}

The above example shows how to define a CellViewConfig for the parent and child cells respectively and how to define the Parent model.

/// Defines a cell config type to handle a UITableViewCell
public protocol CellViewConfigType {
    
    // MARK: Associated types
    
    /// The type of elements backing the collection view or table view.
    associatedtype Item
    
    /// The type of views that the configuration produces.
    associatedtype Cell: UITableViewCell
    
    // MARK: Methods
    
    func reuseIdentiferFor(item: Item?, indexPath: IndexPath) -> String
    
    @discardableResult
    func configure(cell: Cell, item: Item?, tableView: UITableView, indexPath: IndexPath) -> Cell
}

Another step is to define the DataSourceProvider in charge of handling the data source and the CellViewConfig for each cell. 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.

For a more detailed guide please see the Example project.

Screenshots

screenshot

Example of multiple cells open at a time

screenshot

Example of single cells open at a time

screenshot

TODO

  • Add unit tests for the library.
  • Add CircleCI integration.

Feedback

I've found a bug, or have a feature request

Please raise a GitHub issue. ๐Ÿ˜ฑ

Interested in contributing?

Great! Please launch a pull request. ๐Ÿ‘


License:

The MIT License. See the LICENSE file for more information.

accordionswift's People

Contributors

devmjun avatar dewalddejager avatar kwood22 avatar twodollarsesq avatar vkt0r avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

accordionswift's Issues

Allow only one Parent Cell to be expanded at a time

This feature was requested in a previous version of the library. See Issue #2.

Issue Request

The framework should be customizable in such a way that a user of the framework should be able to configure a property and then the accordion will allow only one Parent Cell to be expanded at any time. This is very similar to many HTML/JS/CSS accordions.

Possible Solutions

  1. Before a parent is expanded, collapse all parent cells in the accordion
  2. Keep track of the index of the expanded parent cell and when a different parent cell is expanded, toggle the previous parent cell

Questions

  1. Should the framework allow for no parents to be expanded as well?
  2. If so, should the user be able to specify the initial state?

Reloading on dynamic data.

I'm not sure if this is something I may have been missed, but is there a more efficient way to reload data when new items are added/removed without having to recreate a DataSourceProvider?

By the way, thank you for creating repo!

Out of bounds error

Hello!

So I'm using this in a static manner to show 3 parent cells, each of which have 1 child. Therefore there should be a total of 3 cells.
However, if I check the number of items on the dataSource it reports 4.
I'm getting an out of bounds error in scrollCellIfNeeded as it thinks the last indexPath does not exist, which it doesn't, so crashes out on me. This only occurs if I try to select the bottom parent when the middle one is open and the top is slightly off the screen.

I couldn't actually see why it thought there 4 items, not 3, however, fixing this specific bug is relatively easy.

Would a PR to resolve the issue be best or can you think of why this issue would be occurring? I think it's best to ensure the indexPath to scroll to is actually in the range of the table, rather than attempting to do so without checking.

Getting index..

Assume i expand Parent cell.So,how can i get the selected child cell index and parent cell index in code?

If you have anytime,can you post an example with realworld data,like below

[Smith Family]
->[Adem Smith]
->[Jason Smith]
[Luca Family]
->[Steven Luca]
->[James Luca]
->[Adem Luca]
->[Dean Luca]

Problem######

1.If i select luca,and steven,i want to get index of luca and james.
2.And setting initial data is not meet for my requirement which mean child will be dynamic
self.setInitialDataSource(numberOfRowParents: (Dynamic Parent Count), numberOfRowChildPerParent: (Dynamic Child Count for each Parent))

I am asking you this because the data will be come from the server and i need to fetch data from the model.
Any Help

dataSource

private func setInitialDataSource(numberOfRowParents parents: Int, numberOfRowChildPerParent childs: Int) {

    // Set the total of cells initially.
    self.total = parents

    let data = [Parent](count: parents, repeatedValue: Parent(state: .Collapsed, childs: [String](), title: ""))

    **dataSource = data.enumerate().map({ (index: Int, element: Parent) -> Parent in**

        var newElement = element

        newElement.title = "Item \(index)"

        // generate the random number between 0...childs
        let random = Int(arc4random_uniform(UInt32(childs + 1))) + 1

        // create the array for each cell
        newElement.childs = (0..<random).enumerate().map {"Subitem \($0.index)"}

        return newElement
    })
}

This is what i don't understand the most I guess, specifically the dataSource = data.enumerate()...

What I'm trying to do is implement it into my app, making the third row of my tableview expand with about 5-6 cells drop down once expanded.

Crashed when calling configDataSource() method if sever don't have data.

In my scenario, I am calling some list API first so, in that case, if I call configDataSource() method before getting data from the server so it's crashed because I have no data to load table view, once I got list data from the server so I can reload table view but if I don't have data so how can load table view with empty data?
Please help me....
Thanks.

Missing items when expanded

I realised that when one or more of the cells are set to be expanded, the items will be missing.

simulator screen shot 12 mar 2017 10 17 13 pm

let item1 = Parent(state: .collapsed, childs: ["SubItem 1", "SubItem 2", "SubItem 3"], title: "Item 1")
        let item2 = Parent(state: .collapsed, childs: ["SubItem 1", "SubItem 2"], title: "Item 2")
        let item3 = Parent(state: .expanded, childs: ["SubItem 1", "SubItem 2", "SubItem 3"], title: "Item 3")
        let item4 = Parent(state: .expanded, childs: ["SubItem 1", "SubItem 2"], title: "Item 4")
        let item5 = Parent(state: .expanded, childs: ["SubItem 1", "SubItem 2"], title: "Item 5")
        
        dataSource = [item1, item2, item3, item4, item5]
        numberOfCellsExpanded = .several
        total = dataSource.count

Can't form Range with end < start

Hi Vkt0r,

First of all thank you for this code. Just one issue so far.. In case child is empty, I get an error:
"Can't form Range with end < start"

I'm using it for generating a category menu. Some items have subcategories, some don't.

Any idea how I can change the code?

Build Archive Error: Command PhaseScriptExecution failed with a nonzero exit code

Xcode Version 14.3 (14E222b)

Pod: platform :ios, '12.0'

Log;

PhaseScriptExecution [CP]\ Embed\ Pods\ Frameworks /Users/onderada/Library/Developer/Xcode/DerivedData/timeware-flcdnawruutlqubvzxgiwmzmsszt/Build/Intermediates.noindex/ArchiveIntermediates/timewareRelease/IntermediateBuildFilesPath/timeware.build/Release-iphoneos/timeware.build/Script-366EFECB66AE6C7F9CE51422.sh (in target 'timeware' from project 'timeware')
cd /Volumes/disk2/globmeios/idenfitios
/bin/sh -c /Users/onderada/Library/Developer/Xcode/DerivedData/timeware-flcdnawruutlqubvzxgiwmzmsszt/Build/Intermediates.noindex/ArchiveIntermediates/timewareRelease/IntermediateBuildFilesPath/timeware.build/Release-iphoneos/timeware.build/Script-366EFECB66AE6C7F9CE51422.sh

mkdir -p /Users/onderada/Library/Developer/Xcode/DerivedData/timeware-flcdnawruutlqubvzxgiwmzmsszt/Build/Intermediates.noindex/ArchiveIntermediates/timewareRelease/BuildProductsPath/Release-iphoneos/timeware.app/Frameworks
Symlinked...
rsync --delete -av --filter P .*.?????? --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "../../../IntermediateBuildFilesPath/UninstalledProducts/iphoneos/AccordionSwift.framework" "/Users/onderada/Library/Developer/Xcode/DerivedData/timeware-flcdnawruutlqubvzxgiwmzmsszt/Build/Intermediates.noindex/ArchiveIntermediates/timewareRelease/InstallationBuildProductsLocation/Applications/timeware.app/Frameworks"
building file list ... rsync: link_stat "/Volumes/disk2/globmeios/idenfitios/../../../IntermediateBuildFilesPath/UninstalledProducts/iphoneos/AccordionSwift.framework" failed: No such file or directory (2)
done

sent 29 bytes received 20 bytes 98.00 bytes/sec
total size is 0 speedup is 0.00
rsync error: some files could not be transferred (code 23) at /AppleInternal/Library/BuildRoots/9e200cfa-7d96-11ed-886f-a23c4f261b56/Library/Caches/com.apple.xbs/Sources/rsync/rsync/main.c(996) [sender=2.6.9]
Command PhaseScriptExecution failed with a nonzero exit code

Drop down cells in drop down cell

Hi, I have this strange problem I can't figure out on my own... the problem is that I want to use dropdown cells in drop down cells like

DropDown
DropDown
-------Final cell
DropDown
-------Final cell
DropDown

I tried to add SubHeader Items into the class of cell but I failed, can you point me the right way? Also I tried to attach indicators of whether the cell is expanded or collapsed, but it either worked for every cell or didn't roll back when chose other cell.. Thanks for any response, wish best coding!

Crashed when expanded cell last cell at scrollCellIfNeeded() method in DataSourceProvider.swift

When expanding cells one after one from first to the last cell. At the last cell Crashed.
if we have three cells when trying to expand at 3 cell that indexPath { 0 : 0, 1: 4}

private func scrollCellIfNeeded(atIndexPath indexPath: IndexPath, _ tableView: UITableView) {

    let cellRect = tableView.rectForRow(at: indexPath)
    
    // Scroll to the cell in case of not being visible
    if !tableView.bounds.contains(cellRect) {
        tableView.scrollToRow(at: indexPath, at: .bottom, animated: true) // Crashed here
    }
} 

private func update(_ tableView: UITableView, _ item: DataSource.Item?, _ currentPosition: Int, _ indexPath: IndexPath, _ parentIndex: Int) {

}

due to instance numberOfChildren is an increased value by one

Parent cells require children?

I wanted to be able to detect when the user selects a parent cell that doesn't have any children. In order to check for that, I had to make Parent.childs public, FYI.

Edit: it looks like handling taps on parent cells works now, thanks for the update! But it also seems that child cells aren't optional. My use case is a "Settings" row in a navigation that doesn't have any child cells.

Get child value

Hi,
I have a problem and question with this library. I want to get value of child how can i do it?

Thanks,
Vafa

How to select a default parent and child cell

Hello everyone,

I'm looking for a way to select a parent-child cell by default automatically after the tableView was loaded.

How can I do the equivalent of this:
self.tableView.selectRow(at: IndexPath.init(row: 2, section: 0), animated: false, scrollPosition: UITableViewScrollPosition.none)
using your library ?

Thanks in advance !

update dataSource and tableView

Is there a way to update dataSource and tableView? For example when i click button, I want to add new parent with childs, but I get errors or table delete some rows when I try to do this.

Library is complex to use

Hi Vkt0r,

I see that you say this is the best way to implement accordion. Anyway, your codes in this library and in the sample project, I find it hard to understand. Is it possible for you to make a very simple sample project that teaches how to add data properly? I will try my best to make one in the future if by any chance I get to understand your codes. I found another Accordion library and its so easy to use and implement, however it has bugs with regards to cells being reused (trying to fix them by myself by the way).

So again, thanks for this, but I'm just requesting for a quite easier to read sample project.

Glenn

Accordion Swift Crashes "Library not loaded".

Just clone the project and run

Its crashes on loading the app on device

dyld: Library not loaded: @rpath/AccordionSwift.framework/AccordionSwift
Referenced from: /var/containers/Bundle/Application/B3A15C0F-880B-44C8-8EF9-62871F5AE81B/Example.app/Example
Reason: image not found

Image not found, whats the image here missing?

Customize Cells

Hi
Thanks you for your great library. I want to use image in my parent cell what can I do for it? how can I customize it?
Thanks,
Vafa

Expanding last group doesn't show offscreen child rows

@Vkt0r I found a tiny UX situation using the latest Example project:

  1. Tap "UITableViewController"
  2. Scroll all the way to the end
  3. Tap "Group H" to expand it

It seems that nothing happens, but the child rows actually do get displayed, they're just off-screen. It would be nice to auto-scroll down so they're visible.

Cell without children?

Is there a way to have a cell without children?
How can I achieve that first three cells have children, and last two don't?

Cells not being expanded after a Parent cell with no Childs

The cells not being expanded after a Parent cell with no Childs is tapped, the following are steps to reproduce the issue:

  1. Tap and expand Group B, then collapse it back down (works fine!)
  2. Tap Group A (the empty group)
  3. Tap Group B again (nothing happens)

Index out of Bounds

I am setting the data for both the ParentCell and the ChildCell in the cellForRowAtIndexPath function, but on swiping the table up and down after expanding a couple of cells the app crashes due to the "Index out of range" error.

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let (parent, isParentCell, actualPosition) = self.findParent(indexPath.row)

    if !isParentCell {
        var cell = tableView.dequeueReusableCellWithIdentifier("ChildCell", forIndexPath: indexPath) as! customTrackCell  
        cell.label4.text = "Child Cell"
        return cell
    }
    else {
        var cell = tableView.dequeueReusableCellWithIdentifier("ParentCell", forIndexPath: indexPath) as! customTrackCell

        cell.label1.text = "\(udf1[indexPath.row])"
        cell.label2.text = "\(ordId[indexPath.row])"
        cell.label3.text = "\(status[indexPath.row])"
        return cell
    }
}

I feel this is happening due to the change of indexPath every time when the cells get expanded or collapsed. So is there any way where i can separate the indexPath for both the ParentCell and the ChildCell so that the change in the indexPath of one type does not affect the other.

item backgrounds

Can anyone think why it would work perfectly on the simulator, but on IOS 9 ipad 2 all of the cells have a white background?
thanks in advance,

Add new row

how can i add new row in any section.
i have textfield in each row.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.