【问题标题】:how to create Expandable Table view like Tree Structure in ios如何在 ios 中创建像树结构这样的可扩展表视图
【发布时间】:2015-11-20 02:25:16
【问题描述】:

大家好,我需要一个可扩展的表格视图来存储我的数据, 是否可以在 tableView 中创建这样的内容。在我的数据中,每个人都有不同的孩子,下面是我的数据

-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

帮帮我提前谢谢

【问题讨论】:

    标签: ios objective-c swift uitableview expandablelistview


    【解决方案1】:

    试试这个:-

      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];
       }
    
     }
    

    【讨论】:

      【解决方案2】:

      在 github UITreeView 有 UITreeView 示例

      【讨论】:

      • 非常感谢@Varun,UITreeView 是完美的选择
      【解决方案3】:

      我使用SwiftListTreeDataSource 以类似列表的方式可视化分层数据结构(树)。它与 UI 无关,就像视图模型一样,因此您可以将其与 UITableView/UICollectionView/NSTableView 甚至具有搜索功能的 SwiftUI 一起使用。

      【讨论】:

        【解决方案4】:

        这个问题很老,但现在没有任何第三方库更容易做到。从 iOS 14 开始,您可以在 UICollectionView 上使用 UICollectionViewDiffableDataSource。你也可以使用 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 有一个OutlineGroup 类型,可以很容易地创建一个树形结构。但是,这种方法不能延迟加载。

        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(延迟加载)

        SwiftUI 还有一个DisclosureGroup 视图,允许我们手动创建可扩展的部分,因此很容易创建我们自己的延迟加载列表。

        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)
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多