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
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];
}
}
there is UITreeView example at github UITreeView
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.
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 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 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)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With