Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to create Expandable Table view like Tree Structure in ios

hi every one i need a expandable table view for my data, Is it Possible to create like this in tableView.In my Data each one having different Childs,below is my data

-A1
    -A1.1
        -A1.1.1
            A1.1.1.1
+B1
+C1
+D1

----------------------
+A1
-B1
    +B1.1
+C1
+D1
-----------------------
+A1
+B1
+C1
-D1
    +D1.1
    -D1.2
        +D1.2.1
        +D1.2.2
    +D1.3

Help me thanks in advance

like image 697
Test Avatar asked Aug 26 '15 07:08

Test


3 Answers

try this :-

  NSMutableIndexSet *expandedSections;
  @property (strong, nonatomic) NSIndexPath *expandedIndexPath;



 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
   {

    if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) {
        return 100;
    }
    else
    {
    return 30;
    }

}


  - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  {

  [tableView deselectRowAtIndexPath:indexPath animated:YES];
  if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) {
      [tableView beginUpdates];
      self.expandedIndexPath = nil;
      [tableView endUpdates];
  }
  else{
      self.expandedIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];

      [tableView beginUpdates];

     if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame)     {
        self.expandedIndexPath = indexPath;
    } else {
        self.expandedIndexPath = nil;
    }

    [tableView endUpdates];
   }

 }
like image 107
Ram Vinay Yadav Avatar answered Oct 22 '22 13:10

Ram Vinay Yadav


there is UITreeView example at github UITreeView

like image 44
Varun Naharia Avatar answered Oct 22 '22 13:10

Varun Naharia


This question is old but now it's easier to do it without any third party library. Since iOS 14, you can use UICollectionViewDiffableDataSource on a UICollectionView. You can also use SwiftUI.

UIKit

class ViewController: UIViewController {
    
    enum Section { // We have one section
        case main
    }

    let directory = URL(fileURLWithPath: "/") // The directory we want to browse
    
    var dataSource: UICollectionViewDiffableDataSource<Section, URL>! // The data source
    
    var collectionView: UICollectionView! // The collection view
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Create the collection view with a list layout so it looks like a table view
        collectionView = UICollectionView(frame: view.frame, collectionViewLayout: UICollectionViewCompositionalLayout { section, layoutEnvironment in
            let config = UICollectionLayoutListConfiguration(appearance: .plain)
            return NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvironment)
        })
        
        collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(collectionView)
        
        
        // Here is the code to create a cell. Replace `URL` by your own data type managed by your app
        let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, URL> { (cell, indexPath, url) in
                var content = cell.defaultContentConfiguration()
                content.text = url.lastPathComponent
                var isDir: ObjCBool = false
                if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir) && isDir.boolValue {
                    cell.accessories = [.outlineDisclosure(options: .init(style: .header))] // Add this to expandable cells
                }
                cell.contentConfiguration = content
        }
        
        // Create a data source. We pass our `Section` type that we created and `URL` since we are working with files here
        dataSource = UICollectionViewDiffableDataSource<Section, URL>(collectionView: collectionView, cellProvider: { collectionView, indexPath, url in
            // Create a cell with the block created above
            return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: url)
        })
        
        // Only expand directories
        dataSource.sectionSnapshotHandlers.shouldExpandItem = {
            var isDir: ObjCBool = false
            if FileManager.default.fileExists(atPath: $0.path, isDirectory: &isDir) {
                return isDir.boolValue
            } else {
                return false
            }
        }
        
        // Only collapse directories
        dataSource.sectionSnapshotHandlers.shouldCollapseItem = {
            var isDir: ObjCBool = false
            if FileManager.default.fileExists(atPath: $0.path, isDirectory: &isDir) {
                return isDir.boolValue
            } else {
                return false
            }
        }
        
        // When a directory will be expanded, fill the directory with its files
        dataSource.sectionSnapshotHandlers.willExpandItem = { [weak self] url in
            guard let self = self else {
                return
            }
            
            var snapshot = self.dataSource.snapshot(for: .main)
            snapshot.append((try? FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [])) ?? [], to: url)
            self.dataSource.apply(snapshot, to: .main, animatingDifferences: true, completion: nil)
        }

        // When a directory is collapsed, clear its content to free memory
        dataSource.sectionSnapshotHandlers.willCollapseItem = { [weak self] url in
            guard let self = self else {
                return
            }
            
            var snapshot = self.dataSource.snapshot(for: .main)
            var items = [URL]()
            for item in snapshot.items { // Delete all files that are in the collapsed directory
                if item.resolvingSymlinksInPath().path.hasPrefix(url.resolvingSymlinksInPath().path) && item.resolvingSymlinksInPath() != url.resolvingSymlinksInPath() {
                    items.append(item)
                }
            }
            snapshot.delete(items)
            self.dataSource.apply(snapshot, to: .main, animatingDifferences: true, completion: nil)
        }
        
        // Load the directory
        loadDirectory()
    }
    
    // Fill the collection view with the content of the directory
    func loadDirectory() {
        var snapshot = NSDiffableDataSourceSectionSnapshot<URL>()
        snapshot.append((try? FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil, options: [])) ?? [])
        dataSource.apply(snapshot, to: .main, animatingDifferences: true, completion: nil)
    }
}

SwiftUI

SwiftUI has an OutlineGroup type that can make it very easy to make a tree structure. However it's not possible to lazy load with this method.

struct TreeView: View {
    
    // We create a type that has a name and an array for children.
    // These children must have the same type of the parent.
    // The array must be optional. If the array is `nil`, the item will not be expandable.
    // The type must also conform to `Identifiable`.
    struct Item: Identifiable {
        var id = UUID()
        
        var name: String
        
        var children: [Item]?
    }
    
    // Here we create our structure
    let items = [
        Item(name: "Food", children: [
            Item(name: "Fruits", children: [
                Item(name: "🍎"),
                Item(name: "πŸ“"),
                Item(name: "πŸ₯"), 
                Item(name: "πŸ‹")
            ])
        ]),
        
        Item(name: "Objects", children: [
            Item(name: "πŸ–₯"),
            Item(name: "πŸ’»"),
            Item(name: "⌚️"),
            Item(name: "πŸ“±")
        ]),
    ]
    
    // Here we create a `List` containing an `OutlineGroup` initialized with our data and the path to find children
    var body: some View {
        List {
            OutlineGroup(items, children: \.children) { item in
                Text(item.name)
            }
        }.listStyle(.plain)
    }
}

SwiftUI (Lazy Loading)

SwiftUI also has a DisclosureGroup view that allows us to make expandable sections manually, so it's easy to create our own lazy loading list.

struct TreeView_LazyLoading: View {
    
    // We create a view that contains a list of files inside a directory
    struct DirectoryList: View {
        
        var directory: URL
        
        func isDirectory(_ item: URL) -> Bool {
            var isDir: ObjCBool = false
            return FileManager.default.fileExists(atPath: item.path, isDirectory: &isDir) && isDir.boolValue
        }
        
        var body: some View {
            
            // This will only be called when the view appears, so we can lazy load content
            ForEach((try? FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil, options: [])) ?? [], id: \.self) { url in
                
                if isDirectory(url) {
                    // If it's a directory, show a disclosure group with another `DirectoryList` view initialized with the url
                    DisclosureGroup {
                        DirectoryList(directory: url)
                    } label: {
                        Text(url.lastPathComponent)
                    }
                } else {
                    // If not, just show the file name
                    Text(url.lastPathComponent)
                }
            }
        }
    }
    
    // The directory we want to browse
    let directory = URL(fileURLWithPath: "/")
    
    var body: some View {
        List { // A list with the content of `directory`
            DirectoryList(directory: directory)
        }.listStyle(.plain)
    }
}
like image 1
Emma LabbΓ© Avatar answered Oct 22 '22 13:10

Emma LabbΓ©