NSOutlineView 实现文件管理器层级视图
效果图
Data Model
JZiCloudFileSystemItem.h
@interface JZiCloudFileSystemItem : NSObject
@property (nonatomic,strong) NSString *relativePath;
@property (nonatomic,weak) JZiCloudFileSystemItem *parent;
@property (nonatomic,strong) NSMutableArray *children;
@property (nonatomic,strong) NSMutableArray *childrenFolderOnly;
@property (nonatomic,strong) NSMutableArray *childrenMDOnly;
+ (JZiCloudFileSystemItem *)rootItem;
- (NSInteger)numberOfChildren;// Returns -1 for leaf nodes
- (JZiCloudFileSystemItem *)childAtIndex:(NSUInteger)n; // Invalid to call on leaf nodes
// 当前 JZiCloudFileSystemItem 文件夹内的 Markdown 数量
- (NSInteger)numberOfChildrenMD;
// 当前 JZiCloudFileSystemItem 文件夹内的 文件夹 数量
- (NSInteger)numberOfChildrenFolder;// Returns -1 for leaf nodes
- (JZiCloudFileSystemItem *)childFolderAtIndex:(NSUInteger)n; // Invalid to call on leaf nodes
- (NSString *)fullPath;
- (NSString *)relativePath;
- (void)refresh;
@end
JZiCloudFileSystemItem.m
@implementation JZiCloudFileSystemItem
@synthesize relativePath,parent,children,childrenFolderOnly,childrenMDOnly;
static JZiCloudFileSystemItem *rootItem = nil;
static NSMutableArray *leafNode = nil;
+ (void)initialize {
if (self == [JZiCloudFileSystemItem class])
{
leafNode = [[NSMutableArray alloc] init];
}
}
- (id)initWithPath:(NSString *)path parent:(JZiCloudFileSystemItem *)parentItem {
self = [super init];
if (self) {
relativePath = [[path lastPathComponent] copy];
parent = parentItem;
}
return self;
}
- (void)refresh
{
rootItem = nil;
}
+ (JZiCloudFileSystemItem *)rootItem {
if (rootItem == nil)
{
NSString *icloudRoot = [[[JZiCloudStorageManager sharedManager] ubiquitousDocumentsURL] path];
rootItem = [[JZiCloudFileSystemItem alloc] initWithPath:icloudRoot parent:nil];
}
return rootItem;
}
// Creates, caches, and returns the array of children
// Loads children incrementally
- (NSArray *)children {
if (children == nil)
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *fullPath = [self fullPath];
BOOL isDir, valid;
valid = [fileManager fileExistsAtPath:fullPath isDirectory:&isDir];
if (valid && isDir) {
NSArray *array = [fileManager contentsOfDirectoryAtURL:[NSURL fileURLWithPath:fullPath isDirectory:YES]
includingPropertiesForKeys:[NSArray arrayWithObject:NSURLNameKey]
options:NSDirectoryEnumerationSkipsHiddenFiles
error:nil];
NSUInteger numChildren, i;
numChildren = [array count];
children = [[NSMutableArray alloc] initWithCapacity:numChildren];
for (i = 0; i < numChildren; i++)
{
JZiCloudFileSystemItem *newChild = [[JZiCloudFileSystemItem alloc]
initWithPath:[[array objectAtIndex:i] path] parent:self];
[children addObject:newChild];
}
}
else {
children = leafNode;
}
}
return children;
}
- (NSArray *)childrenFolderOnly
{
if (childrenFolderOnly == nil)
{
childrenFolderOnly = [[NSMutableArray alloc] initWithCapacity:[[self children] count]];
for (NSUInteger i = 0; i < [[self children] count]; i++)
{
NSString *path = [[[self children] objectAtIndex:i] fullPath];
NSURL *url = [NSURL fileURLWithPath:path];
BOOL isDirectory;
BOOL fileExistsAtPath = [[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory:&isDirectory];
if (isDirectory && fileExistsAtPath)
{
[childrenFolderOnly addObject:[[self children] objectAtIndex:i]];
}
}
}
return childrenFolderOnly;
}
- (NSArray *)childrenMDOnly
{
if (childrenMDOnly == nil)
{
childrenMDOnly = [[NSMutableArray alloc] initWithCapacity:[[self children] count]];
for (NSUInteger i = 0; i < [[self children] count]; i++)
{
NSString *path = [[[self children] objectAtIndex:i] fullPath];
NSURL *url = [NSURL fileURLWithPath:path];
BOOL isDirectory;
BOOL fileExistsAtPath = [[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory:&isDirectory];
BOOL isMarkdown = ([[url pathExtension] compare: @"md"] == NSOrderedSame);
if (fileExistsAtPath & isMarkdown)
{
[childrenMDOnly addObject:[[self children] objectAtIndex:i]];
}
}
}
return childrenMDOnly;
}
- (NSString *)relativePath {
return relativePath;
}
- (NSString *)fullPath {
// If no parent, return our own relative path
if (parent == nil)
{
return [[[JZiCloudStorageManager sharedManager] ubiquitousDocumentsURL] path];
}
// recurse up the hierarchy, prepending each parent’s path
return [[parent fullPath] stringByAppendingPathComponent:relativePath];
}
- (JZiCloudFileSystemItem *)childAtIndex:(NSUInteger)n {
return [[self children] objectAtIndex:n];
}
- (JZiCloudFileSystemItem *)childFolderAtIndex:(NSUInteger)n
{
return [[self childrenFolderOnly] objectAtIndex:n];
}
- (NSInteger)numberOfChildren
{
NSArray *tmp = [self children];
return (tmp == leafNode) ? (-1) : [tmp count];
}
- (NSInteger)numberOfChildrenMD
{
NSArray *tmp = [self childrenMDOnly];
return (tmp == leafNode) ? (-1) : [tmp count];
}
- (NSInteger)numberOfChildrenFolder
{
NSArray *tmp = [self childrenFolderOnly];
return (tmp == leafNode) ? (-1) : [tmp count];
}
@end
NSOutlineViewDataSource
#pragma mark - NSOutlineViewDataSource
- (NSInteger)outlineView:(NSOutlineView *)outlineView
numberOfChildrenOfItem:(id)item
{
return (item == nil) ? 1 : [item numberOfChildrenFolder];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView
isItemExpandable:(id)item
{
return (item == nil) ? YES : ([item numberOfChildrenFolder] != 0);
}
- (id)outlineView:(NSOutlineView *)outlineView
child:(NSInteger)index
ofItem:(id)item
{
return (item == nil) ? [JZiCloudFileSystemItem rootItem] : [(JZiCloudFileSystemItem *)item childFolderAtIndex:index];
}
- (id)outlineView:(NSOutlineView *)outlineView
objectValueForTableColumn:(NSTableColumn *)tableColumn
byItem:(id)item
{
return (item == nil) ? @"/" : [item relativePath];
}
NSOutlineViewDelegate
- (NSView *)outlineView:(NSOutlineView *)outlineView
viewForTableColumn:(NSTableColumn *)tableColumn
item:(id)item
{
if (NO)
{}
else
{
JZFolderOutlineCellView *view = [self.outlineView makeViewWithIdentifier:@"DataCell" owner:self];
view.textField.stringValue = [item relativePath];
view.childCountButton.title = [NSString stringWithFormat:@"%ld",(long)[item numberOfChildrenMD]];
return view;
}
}
- (CGFloat)outlineView:(NSOutlineView *)outlineView
heightOfRowByItem:(id)item
{
return 30.0f;
}
Storyboard
View Based, Identifier = @“DataCell”