123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631 |
- //The MIT License (MIT)
- //
- //Copyright (c) 2014 Rafał Augustyniak
- //
- //Permission is hereby granted, free of charge, to any person obtaining a copy of
- //this software and associated documentation files (the "Software"), to deal in
- //the Software without restriction, including without limitation the rights to
- //use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- //the Software, and to permit persons to whom the Software is furnished to do so,
- //subject to the following conditions:
- //
- //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- //FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- //COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- //IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- //CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- //
- #import "RATreeView.h"
- #import "RATreeView_ClassExtension.h"
- #import "RATreeView+Enums.h"
- #import "RATreeView+Private.h"
- #import "RABatchChanges.h"
- #import "RATreeNodeCollectionController.h"
- #import "RATreeNode.h"
- #import "RATableView.h"
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wincomplete-implementation"
- @implementation RATreeView
- #pragma clang diagnostic pop
- #pragma mark Initializing a TreeView Object
- - (id)init
- {
- return [self initWithFrame:CGRectMake(0, 0, 100, 100) style:RATreeViewStylePlain];
- }
- - (id)initWithFrame:(CGRect)frame
- {
- return [self initWithFrame:frame style:RATreeViewStylePlain];
- }
- - (id)initWithFrame:(CGRect)frame style:(RATreeViewStyle)style
- {
- self = [super initWithFrame:frame];
- if (self) {
- CGRect innerFrame = CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds));
- [self commonInitWithFrame:innerFrame style:style];
- }
- return self;
- }
- - (id)initWithCoder:(NSCoder *)aDecoder
- {
- self = [super initWithCoder:aDecoder];
- if (self) {
- CGRect innerFrame = CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds));
- [self commonInitWithFrame:innerFrame style:RATreeViewStylePlain];
- }
- return self;
- }
- - (void)commonInitWithFrame:(CGRect)frame style:(RATreeViewStyle)style
- {
- UITableViewStyle tableViewStyle = [RATreeView tableViewStyleForTreeViewStyle:style];
- RATableView *tableView = [[RATableView alloc] initWithFrame:frame style:tableViewStyle];
- tableView.tableViewDelegate = (id<UITableViewDelegate>)self;
- tableView.dataSource = (id<UITableViewDataSource>)self;
- tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- tableView.backgroundColor = [UIColor clearColor];
- [self addSubview:tableView];
- [self setTableView:tableView];
- self.expandsChildRowsWhenRowExpands = NO;
- self.collapsesChildRowsWhenRowCollapses = NO;
- self.rowsExpandingAnimation = RATreeViewRowAnimationTop;
- self.rowsCollapsingAnimation = RATreeViewRowAnimationBottom;
- }
- - (void)awakeFromNib
- {
- [super awakeFromNib];
- self.tableView.backgroundColor = [UIColor clearColor];
- }
- #pragma mark Scroll View
- - (UIScrollView *)scrollView
- {
- return self.tableView;
- }
- #pragma mark Configuring a Tree View
- - (NSInteger)numberOfRows
- {
- return [self.tableView numberOfRowsInSection:0];
- }
- - (RATreeViewStyle)style
- {
- UITableViewStyle tableViewStyle = self.tableView.style;
- return [RATreeView treeViewStyleForTableViewStyle:tableViewStyle];
- }
- #if TARGET_OS_IOS
- - (RATreeViewCellSeparatorStyle)separatorStyle
- {
- RATreeViewCellSeparatorStyle style = [RATreeView treeViewCellSeparatorStyleForTableViewSeparatorStyle:self.tableView.separatorStyle];
- return style;
- }
- - (void)setSeparatorStyle:(RATreeViewCellSeparatorStyle)separatorStyle
- {
- UITableViewCellSeparatorStyle tableViewSeparatorStyle = [RATreeView tableViewCellSeparatorStyleForTreeViewCellSeparatorStyle:separatorStyle];
- self.tableView.separatorStyle = tableViewSeparatorStyle;
- }
- - (UIColor *)separatorColor
- {
- return self.tableView.separatorColor;
- }
- - (void)setSeparatorColor:(UIColor *)separatorColor
- {
- self.tableView.separatorColor = separatorColor;
- }
- #endif
- - (CGFloat)rowHeight
- {
- return self.tableView.rowHeight;
- }
- - (void)setRowHeight:(CGFloat)rowHeight
- {
- self.tableView.rowHeight = rowHeight;
- }
- - (CGFloat)estimatedRowHeight
- {
- if ([self.tableView respondsToSelector:@selector(estimatedRowHeight)]) {
- return self.tableView.estimatedRowHeight;
- } else {
- return 0;
- }
- }
- - (void)setEstimatedRowHeight:(CGFloat)estimatedRowHeight
- {
- if ([self.tableView respondsToSelector:@selector(estimatedRowHeight)]) {
- self.tableView.estimatedRowHeight = estimatedRowHeight;
- }
- }
- - (void)setEstimatedSectionHeaderHeight:(CGFloat)estimatedSectionHeaderHeight {
- if ([self.tableView respondsToSelector:@selector(estimatedSectionHeaderHeight)]) {
- self.tableView.estimatedSectionHeaderHeight = estimatedSectionHeaderHeight;
- }
- }
- - (void)setEstimatedSectionFooterHeight:(CGFloat)estimatedSectionFooterHeight {
- if ([self.tableView respondsToSelector:@selector(estimatedSectionFooterHeight)]) {
- self.tableView.estimatedSectionFooterHeight = estimatedSectionFooterHeight;
- }
- }
- - (UIEdgeInsets)separatorInset
- {
- if ([self.tableView respondsToSelector:@selector(separatorInset)]) {
- return self.tableView.separatorInset;
- } else {
- return UIEdgeInsetsZero;
- }
- }
- - (void)setSeparatorInset:(UIEdgeInsets)separatorInset
- {
- if ([self.tableView respondsToSelector:@selector(separatorInset)]) {
- self.tableView.separatorInset = separatorInset;
- }
- }
- #if TARGET_OS_IOS
- - (UIVisualEffect *)separatorEffect
- {
- if ([self.tableView respondsToSelector:@selector(separatorEffect)]) {
- return self.tableView.separatorEffect;
- } else {
- return nil;
- }
- }
- - (void)setSeparatorEffect:(UIVisualEffect *)separatorEffect
- {
- if ([self.tableView respondsToSelector:@selector(separatorEffect)]) {
- self.tableView.separatorEffect = separatorEffect;
- }
- }
- #endif
- - (BOOL)cellLayoutMarginsFollowReadableWidth
- {
- if ([self.tableView respondsToSelector:@selector(cellLayoutMarginsFollowReadableWidth)]) {
- return self.tableView.cellLayoutMarginsFollowReadableWidth;
- } else {
- return NO;
- }
- }
- - (void)setCellLayoutMarginsFollowReadableWidth:(BOOL)cellLayoutMarginsFollowReadableWidth
- {
- if ([self.tableView respondsToSelector:@selector(cellLayoutMarginsFollowReadableWidth)]) {
- self.tableView.cellLayoutMarginsFollowReadableWidth = cellLayoutMarginsFollowReadableWidth;
- }
- }
- - (UIView *)backgroundView
- {
- return self.tableView.backgroundView;
- }
- - (void)setBackgroundView:(UIView *)backgroundView
- {
- self.tableView.backgroundView = backgroundView;
- }
- #pragma mark Expanding and Collapsing Rows
- - (void)expandRowForItem:(id)item
- {
- [self expandRowForItem:item withRowAnimation:self.rowsExpandingAnimation];
- }
- - (void)expandRowForItem:(id)item withRowAnimation:(RATreeViewRowAnimation)animation
- {
- [self expandRowForItem:item expandChildren:NO withRowAnimation:animation];
- }
- - (void)expandRowForItem:(id)item expandChildren:(BOOL)expandChildren withRowAnimation:(RATreeViewRowAnimation)animation
- {
- NSIndexPath *indexPath = [self indexPathForItem:item];
- RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
- if (!treeNode || treeNode.expanded) {
- return;
- }
- [self expandCellForTreeNode:treeNode expandChildren:expandChildren withRowAnimation:animation];
- }
- - (void)collapseRowForItem:(id)item
- {
- [self collapseRowForItem:item withRowAnimation:self.rowsCollapsingAnimation];
- }
- - (void)collapseRowForItem:(id)item withRowAnimation:(RATreeViewRowAnimation)animation
- {
- [self collapseRowForItem:item collapseChildren:NO withRowAnimation:animation];
- }
- - (void)collapseRowForItem:(id)item collapseChildren:(BOOL)collapseChildren withRowAnimation:(RATreeViewRowAnimation)animation
- {
- NSIndexPath *indexPath = [self indexPathForItem:item];
- RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
- if (!treeNode) {
- return;
- }
- [self collapseCellForTreeNode:treeNode collapseChildren:collapseChildren withRowAnimation:animation];
- }
- #pragma mark - Changing tree's structure
- - (void)beginUpdates
- {
- [self.tableView beginUpdates];
- [self.batchChanges beginUpdates];
- }
- - (void)endUpdates
- {
- [self.batchChanges endUpdates];
- [self.tableView endUpdates];
- }
- - (void)insertItemsAtIndexes:(NSIndexSet *)indexes inParent:(id)parent withAnimation:(RATreeViewRowAnimation)animation
- {
- // if (parent && ![self isCellForItemExpanded:parent]) {
- // return;
- // }
- __weak __typeof(self) weakSelf = self;
- [indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
- [weakSelf insertItemAtIndex:idx inParent:parent withAnimation:animation];
- }];
- }
- - (void)deleteItemsAtIndexes:(NSIndexSet *)indexes inParent:(id)parent withAnimation:(RATreeViewRowAnimation)animation
- {
- if (parent && ![self isCellForItemExpanded:parent]) {
- return;
- }
- __weak __typeof(self) weakSelf = self;
- [indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
- [weakSelf removeItemAtIndex:idx inParent:parent withAnimation:animation];
- }];
- }
- #pragma mark - Creating Table View Cells
- - (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier
- {
- [self.tableView registerNib:nib forCellReuseIdentifier:identifier];
- }
- - (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier
- {
- [self.tableView registerClass:cellClass forCellReuseIdentifier:identifier];
- }
- - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
- {
- return [self.tableView dequeueReusableCellWithIdentifier:identifier];
- }
- #pragma mark - Accessing Header and Footer Views
- - (void)registerNib:(UINib *)nib forHeaderFooterViewReuseIdentifier:(NSString *)identifier
- {
- [self.tableView registerNib:nib forHeaderFooterViewReuseIdentifier:identifier];
- }
- - (void)registerClass:(Class)aClass forHeaderFooterViewReuseIdentifier:(NSString *)identifier
- {
- [self.tableView registerClass:aClass forHeaderFooterViewReuseIdentifier:identifier];
- }
- - (id)dequeueReusableHeaderFooterViewWithIdentifier:(NSString *)identifier
- {
- return [self.tableView dequeueReusableHeaderFooterViewWithIdentifier:identifier];
- }
- - (UIView *)treeHeaderView
- {
- return self.tableView.tableHeaderView;
- }
- - (void)setTreeHeaderView:(UIView *)treeHeaderView
- {
- self.tableView.tableHeaderView = treeHeaderView;
- }
- - (UIView *)treeFooterView
- {
- return self.tableView.tableFooterView;
- }
- - (void)setTreeFooterView:(UIView *)treeFooterView
- {
- self.tableView.tableFooterView = treeFooterView;
- }
- #pragma mark - Working with Expandability
- - (BOOL)isCellForItemExpanded:(id)item
- {
- NSIndexPath *indexPath = [self indexPathForItem:item];
- return [self treeNodeForIndexPath:indexPath].expanded;
- }
- - (BOOL)isCellExpanded:(UITableViewCell *)cell
- {
- id item = [self itemForCell:cell];
- return [self isCellForItemExpanded:item];
- }
- #pragma mark - Working with Indentation
- - (NSInteger)levelForCellForItem:(id)item
- {
- return [self.treeNodeCollectionController levelForItem:item];
- }
- - (NSInteger)levelForCell:(UITableViewCell *)cell
- {
- id item = [self itemForCell:cell];
- return [self levelForCellForItem:item];
- }
- #pragma mark - Getting the Parent for an Item
- - (id)parentForItem:(id)item
- {
- return [self.treeNodeCollectionController parentForItem:item];
- }
- #pragma mark - Accessing Cells
- - (UITableViewCell *)cellForItem:(id)item
- {
- NSIndexPath *indexPath = [self indexPathForItem:item];
- return [self.tableView cellForRowAtIndexPath:indexPath];
- }
- - (NSArray *)visibleCells
- {
- return [self.tableView visibleCells];
- }
- - (id)itemForCell:(UITableViewCell *)cell
- {
- NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
- return [self treeNodeForIndexPath:indexPath].item;
- }
- - (id)itemForRowAtPoint:(CGPoint)point
- {
- NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:point];
- return !indexPath ? nil : [self treeNodeForIndexPath:indexPath].item;
- }
- - (id)itemsForRowsInRect:(CGRect)rect
- {
- NSArray *indexPaths = [self.tableView indexPathsForRowsInRect:rect];
- return [self itemsForIndexPaths:indexPaths];
- }
- - (NSArray *)itemsForVisibleRows
- {
- NSArray *indexPaths = [self.tableView indexPathsForVisibleRows];
- return [self itemsForIndexPaths:indexPaths];
- }
- #pragma mark - Scrolling the TreeView
- - (void)scrollToRowForItem:(id)item atScrollPosition:(RATreeViewScrollPosition)scrollPosition animated:(BOOL)animated
- {
- NSIndexPath *indexPath = [self indexPathForItem:item];
- UITableViewScrollPosition tableViewScrollPosition = [RATreeView tableViewScrollPositionForTreeViewScrollPosition:scrollPosition];
- [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:tableViewScrollPosition animated:animated];
- }
- - (void)scrollToNearestSelectedRowAtScrollPosition:(RATreeViewScrollPosition)scrollPosition animated:(BOOL)animated
- {
- UITableViewScrollPosition tableViewScrollPosition = [RATreeView tableViewScrollPositionForTreeViewScrollPosition:scrollPosition];
- [self.tableView scrollToNearestSelectedRowAtScrollPosition:tableViewScrollPosition animated:animated];
- }
- #pragma mark - Managing Selections
- - (id)itemForSelectedRow
- {
- NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
- return [self treeNodeForIndexPath:indexPath].item;
- }
- - (NSArray *)itemsForSelectedRows
- {
- NSArray *selectedRows = [self.tableView indexPathsForSelectedRows];
- return [self itemsForIndexPaths:selectedRows];
- }
- - (void)selectRowForItem:(id)item animated:(BOOL)animated scrollPosition:(RATreeViewScrollPosition)scrollPosition
- {
- if ([self isCellForItemExpanded:[self parentForItem:item]]) {
- NSIndexPath *indexPath = [self indexPathForItem:item];
- UITableViewScrollPosition tableViewScrollPosition = [RATreeView tableViewScrollPositionForTreeViewScrollPosition:scrollPosition];
- [self.tableView selectRowAtIndexPath:indexPath animated:animated scrollPosition:tableViewScrollPosition];
- }
- }
- - (void)deselectRowForItem:(id)item animated:(BOOL)animated
- {
- if ([self isCellForItemExpanded:[self parentForItem:item]]) {
- NSIndexPath *indexPath = [self indexPathForItem:item];
- [self.tableView deselectRowAtIndexPath:indexPath animated:animated];
- }
- }
- - (BOOL)allowsSelection
- {
- return self.tableView.allowsSelection;
- }
- - (void)setAllowsSelection:(BOOL)allowsSelection
- {
- self.tableView.allowsSelection = allowsSelection;
- }
- - (BOOL)allowsMultipleSelection
- {
- return self.tableView.allowsMultipleSelection;
- }
- - (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection
- {
- self.tableView.allowsMultipleSelection = allowsMultipleSelection;
- }
- - (BOOL)allowsSelectionDuringEditing
- {
- return self.tableView.allowsSelectionDuringEditing;
- }
- - (void)setAllowsSelectionDuringEditing:(BOOL)allowsSelectionDuringEditing
- {
- self.tableView.allowsSelectionDuringEditing = allowsSelectionDuringEditing;
- }
- - (BOOL)allowsMultipleSelectionDuringEditing
- {
- return self.tableView.allowsMultipleSelectionDuringEditing;
- }
- - (void)setAllowsMultipleSelectionDuringEditing:(BOOL)allowsMultipleSelectionDuringEditing
- {
- self.tableView.allowsMultipleSelectionDuringEditing = allowsMultipleSelectionDuringEditing;
- }
- #pragma mark - Managing the Editing of Tree Cells
- - (BOOL)isEditing
- {
- return self.tableView.isEditing;
- }
- - (void)setEditing:(BOOL)editing
- {
- self.tableView.editing = editing;
- }
- - (void)setEditing:(BOOL)editing animated:(BOOL)animated
- {
- [self.tableView setEditing:editing animated:animated];
- }
- #pragma mark - Reloading the Tree View
- - (void)reloadData
- {
- [self setupTreeStructure];
- [self.tableView reloadData];
- }
- - (void)reloadRowsForItems:(NSArray *)items withRowAnimation:(RATreeViewRowAnimation)animation
- {
- NSMutableArray *indexes = [NSMutableArray array];
- UITableViewRowAnimation tableViewRowAnimation = [RATreeView tableViewRowAnimationForTreeViewRowAnimation:animation];
- for (id item in items) {
- NSIndexPath *indexPath = [self indexPathForItem:item];
- [indexes addObject:indexPath];
- }
- [self.tableView reloadRowsAtIndexPaths:indexes withRowAnimation:tableViewRowAnimation];
- }
- - (void)reloadRows
- {
- NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:0];
- [self.tableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationNone];
- }
- #pragma mark - UIScrollView's properties
- - (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
- {
- [self.tableView setContentOffset:contentOffset animated:animated];
- }
- - (void)scrollRectToVisible:(CGRect)visible animated:(BOOL)animated
- {
- [self.tableView scrollRectToVisible:visible animated:animated];
- }
- - (void)setZoomScale:(CGFloat)zoomScale animated:(BOOL)animated
- {
- [self.tableView setZoomScale:zoomScale animated:animated];
- }
- - (void)flashScrollIndicators
- {
- [self.tableView flashScrollIndicators];
- }
- - (void)zoomToRect:(CGRect)rect animated:(BOOL)animated
- {
- [self.tableView zoomToRect:rect animated:animated];
- }
- #pragma mark -
- - (NSArray *)itemsForIndexPaths:(NSArray *)indexPaths
- {
- if (!indexPaths) {
- return nil;
- }
- NSMutableArray *items = [NSMutableArray array];
- for (NSIndexPath *indexPath in indexPaths) {
- [items addObject:[self treeNodeForIndexPath:indexPath].item];
- }
- return [items copy];
- }
- @end
|