RATreeView+TableViewDelegate.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. //The MIT License (MIT)
  2. //
  3. //Copyright (c) 2014 Rafał Augustyniak
  4. //
  5. //Permission is hereby granted, free of charge, to any person obtaining a copy of
  6. //this software and associated documentation files (the "Software"), to deal in
  7. //the Software without restriction, including without limitation the rights to
  8. //use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  9. //the Software, and to permit persons to whom the Software is furnished to do so,
  10. //subject to the following conditions:
  11. //
  12. //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  14. //FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  15. //COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  16. //IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  17. //CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  18. //
  19. #import <QuartzCore/QuartzCore.h>
  20. #import "RATreeView+TableViewDelegate.h"
  21. #import "RATreeView_ClassExtension.h"
  22. #import "RATreeView+Private.h"
  23. #import "RATreeView.h"
  24. #import "RATreeNodeCollectionController.h"
  25. #import "RATreeNode.h"
  26. @implementation RATreeView (TableViewDelegate)
  27. #pragma mark - Configuring Rows for the Table View
  28. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  29. {
  30. if ([self.delegate respondsToSelector:@selector(treeView:heightForRowForItem:)]) {
  31. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  32. return [self.delegate treeView:self heightForRowForItem:treeNode.item];
  33. }
  34. return self.tableView.rowHeight;
  35. }
  36. - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
  37. {
  38. if ([self.delegate respondsToSelector:@selector(treeView:estimatedHeightForRowForItem:)]) {
  39. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  40. return [self.delegate treeView:self estimatedHeightForRowForItem:treeNode.item];
  41. }
  42. return UITableViewAutomaticDimension;
  43. }
  44. - (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
  45. {
  46. if ([self.delegate respondsToSelector:@selector(treeView:indentationLevelForRowForItem:)]) {
  47. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  48. return [self.delegate treeView:self indentationLevelForRowForItem:treeNode.item];
  49. }
  50. return 0;
  51. }
  52. - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
  53. {
  54. if ([self.delegate respondsToSelector:@selector(treeView:willDisplayCell:forItem:)]) {
  55. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  56. [self.delegate treeView:self willDisplayCell:cell forItem:treeNode.item];
  57. }
  58. }
  59. #pragma mark - Managing Accessory Views
  60. - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
  61. {
  62. if ([self.delegate respondsToSelector:@selector(treeView:accessoryButtonTappedForRowForItem:)]) {
  63. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  64. [self.delegate treeView:self accessoryButtonTappedForRowForItem:treeNode.item];
  65. }
  66. }
  67. #pragma mark - Managing Selection
  68. - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
  69. {
  70. if ([self.delegate respondsToSelector:@selector(treeView:willSelectRowForItem:)]) {
  71. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  72. id item = [self.delegate treeView:self willSelectRowForItem:treeNode.item];
  73. if (item) {
  74. NSIndexPath *newIndexPath = [self indexPathForItem:item];
  75. return (newIndexPath.row == NSNotFound) ? indexPath : newIndexPath;
  76. } else {
  77. return nil;
  78. }
  79. }
  80. return indexPath;
  81. }
  82. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  83. {
  84. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  85. if ([self.delegate respondsToSelector:@selector(treeView:didSelectRowForItem:)]) {
  86. [self.delegate treeView:self didSelectRowForItem:treeNode.item];
  87. }
  88. if (treeNode.expanded) {
  89. if ([self.delegate respondsToSelector:@selector(treeView:shouldCollapaseRowForItem:)]) {
  90. if ([self.delegate treeView:self shouldCollapaseRowForItem:treeNode.item]) {
  91. [self collapseCellForTreeNode:treeNode informDelegate:YES];
  92. }
  93. } else {
  94. [self collapseCellForTreeNode:treeNode informDelegate:YES];
  95. }
  96. } else {
  97. if ([self.delegate respondsToSelector:@selector(treeView:shouldExpandRowForItem:)]) {
  98. if ([self.delegate treeView:self shouldExpandRowForItem:treeNode.item]) {
  99. [self expandCellForTreeNode:treeNode informDelegate:YES];
  100. }
  101. } else {
  102. [self expandCellForTreeNode:treeNode informDelegate:YES];
  103. }
  104. }
  105. }
  106. - (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath
  107. {
  108. if ([self.delegate respondsToSelector:@selector(treeView:willDeselectRowForItem:)]) {
  109. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  110. id item = [self.delegate treeView:self willDeselectRowForItem:treeNode.item];
  111. NSIndexPath *delegateIndexPath = [self indexPathForItem:item];
  112. return delegateIndexPath.row == NSNotFound ? indexPath : delegateIndexPath;
  113. } else {
  114. return indexPath;
  115. }
  116. }
  117. - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
  118. {
  119. if ([self.delegate respondsToSelector:@selector(treeView:didDeselectRowForItem:)]) {
  120. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  121. [self.delegate treeView:self didDeselectRowForItem:treeNode.item];
  122. }
  123. }
  124. - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
  125. {
  126. if ([self.delegate respondsToSelector:@selector(treeView:editingStyleForRowForItem:)]) {
  127. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  128. return [self.delegate treeView:self editingStyleForRowForItem:treeNode.item];
  129. }
  130. return UITableViewCellEditingStyleDelete;
  131. }
  132. - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
  133. {
  134. if ([self.delegate respondsToSelector:@selector(treeView:titleForDeleteConfirmationButtonForRowForItem:)]) {
  135. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  136. return [self.delegate treeView:self titleForDeleteConfirmationButtonForRowForItem:treeNode.item];
  137. }
  138. return @"Delete";
  139. }
  140. - (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
  141. {
  142. if ([self.delegate respondsToSelector:@selector(treeView:shouldIndentWhileEditingRowForItem:)]) {
  143. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  144. return [self.delegate treeView:self shouldIndentWhileEditingRowForItem:treeNode.item];
  145. }
  146. return YES;
  147. }
  148. #pragma mark - Editing Table Rows
  149. - (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
  150. {
  151. if ([self.delegate respondsToSelector:@selector(treeView:willBeginEditingRowForItem:)]) {
  152. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  153. [self.delegate treeView:self willBeginEditingRowForItem:treeNode.item];
  154. }
  155. }
  156. - (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath
  157. {
  158. if ([self.delegate respondsToSelector:@selector(treeView:didEndEditingRowForItem:)]) {
  159. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  160. [self.delegate treeView:self didEndEditingRowForItem:treeNode.item];
  161. }
  162. }
  163. - (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
  164. {
  165. if ([self.delegate respondsToSelector:@selector(treeView:editActionsForItem:)]) {
  166. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  167. return [self.delegate treeView:self editActionsForItem:treeNode.item];
  168. }
  169. return nil;
  170. }
  171. #pragma mark - Tracking the Removal of Views
  172. - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
  173. {
  174. if ([self.delegate respondsToSelector:@selector(treeView:didEndDisplayingCell:forItem:)]) {
  175. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  176. [self.delegate treeView:self didEndDisplayingCell:cell forItem:treeNode.item];
  177. }
  178. }
  179. #pragma mark - Copying and Pasting Row Content
  180. - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath
  181. {
  182. if ([self.delegate respondsToSelector:@selector(treeView:shouldShowMenuForRowForItem:)]) {
  183. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  184. return [self.delegate treeView:self shouldShowMenuForRowForItem:treeNode.item];
  185. }
  186. return NO;
  187. }
  188. - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
  189. {
  190. if ([self.delegate respondsToSelector:@selector(treeView:canPerformAction:forRowForItem:withSender:)]) {
  191. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  192. return [self.delegate treeView:self canPerformAction:action forRowForItem:treeNode.item withSender:sender];
  193. }
  194. return NO;
  195. }
  196. - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
  197. {
  198. if ([self.delegate respondsToSelector:@selector(treeView:performAction:forRowForItem:withSender:)]) {
  199. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  200. return [self.delegate treeView:self performAction:action forRowForItem:treeNode.item withSender:sender];
  201. }
  202. }
  203. #pragma mark - Managing Table View Highlighting
  204. - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath
  205. {
  206. if ([self.delegate respondsToSelector:@selector(treeView:shouldHighlightRowForItem:)]) {
  207. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  208. return [self.delegate treeView:self shouldHighlightRowForItem:treeNode.item];
  209. }
  210. return YES;
  211. }
  212. - (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath
  213. {
  214. if ([self.delegate respondsToSelector:@selector(treeView:didHighlightRowForItem:)]) {
  215. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  216. [self.delegate treeView:self didHighlightRowForItem:treeNode.item];
  217. }
  218. }
  219. - (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath
  220. {
  221. if ([self.delegate respondsToSelector:@selector(treeView:didUnhighlightRowForItem:)]) {
  222. RATreeNode *treeNode = [self treeNodeForIndexPath:indexPath];
  223. [self.delegate treeView:self didUnhighlightRowForItem:treeNode.item];
  224. }
  225. }
  226. #pragma mark - Private Helpers
  227. - (void)collapseCellForTreeNode:(RATreeNode *)treeNode informDelegate:(BOOL)informDelegate
  228. {
  229. if (informDelegate) {
  230. if ([self.delegate respondsToSelector:@selector(treeView:willCollapseRowForItem:)]) {
  231. [self.delegate treeView:self willCollapseRowForItem:treeNode.item];
  232. }
  233. }
  234. [CATransaction begin];
  235. [CATransaction setCompletionBlock:^{
  236. if ([self.delegate respondsToSelector:@selector(treeView:didCollapseRowForItem:)] &&
  237. informDelegate) {
  238. dispatch_async(dispatch_get_main_queue(), ^{
  239. //Content size of the UITableView isn't updates when completion block of the CATransaction is called. To make it possible for the user of the RATreeView to get a correct content size in the implementation of the 'treeView:didCollapseRowForItem' RATreeView calls this method in the next run loop.
  240. [self.delegate treeView:self didCollapseRowForItem:treeNode.item];
  241. });
  242. }
  243. }];
  244. [self collapseCellForTreeNode:treeNode];
  245. [CATransaction commit];
  246. }
  247. - (void)expandCellForTreeNode:(RATreeNode *)treeNode informDelegate:(BOOL)informDelegate
  248. {
  249. if (informDelegate) {
  250. if ([self.delegate respondsToSelector:@selector(treeView:willExpandRowForItem:)]) {
  251. [self.delegate treeView:self willExpandRowForItem:treeNode.item];
  252. }
  253. }
  254. [CATransaction begin];
  255. [CATransaction setCompletionBlock:^{
  256. if ([self.delegate respondsToSelector:@selector(treeView:didExpandRowForItem:)] &&
  257. informDelegate) {
  258. dispatch_async(dispatch_get_main_queue(), ^{
  259. //Content size of the UITableView isn't updates when completion block of the CATransaction is called. To make it possible for the user of the RATreeView to get a correct content size in the implementation of the 'treeView:didExpandRowForItem' RATreeView calls this method in the next run loop.
  260. [self.delegate treeView:self didExpandRowForItem:treeNode.item];
  261. });
  262. }
  263. }];
  264. [self expandCellForTreeNode:treeNode];
  265. [CATransaction commit];
  266. }
  267. @end