PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Three20UI/Sources/TTTableViewController.m

https://github.com/GetMoPix/three20
Objective C | 968 lines | 656 code | 198 blank | 114 comment | 149 complexity | 2ffd8f4ed25c74e00254fc772046cc7c MD5 | raw file
  1. //
  2. // Copyright 2009-2011 Facebook
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. //
  16. #import "Three20UI/TTTableViewController.h"
  17. // UI
  18. #import "Three20UI/TTNavigator.h"
  19. #import "Three20UI/TTActivityLabel.h"
  20. #import "Three20UI/TTErrorView.h"
  21. #import "Three20UI/TTListDataSource.h"
  22. #import "Three20UI/TTTableView.h"
  23. #import "Three20UI/TTTableViewDelegate.h"
  24. #import "Three20UI/TTTableViewVarHeightDelegate.h"
  25. #import "Three20UI/UIViewAdditions.h"
  26. #import "Three20UI/UITableViewAdditions.h"
  27. // UINavigator
  28. #import "Three20UINavigator/TTURLObject.h"
  29. // UICommon
  30. #import "Three20UICommon/TTGlobalUICommon.h"
  31. #import "Three20UICommon/UIViewControllerAdditions.h"
  32. // Style
  33. #import "Three20Style/TTGlobalStyle.h"
  34. #import "Three20Style/TTDefaultStyleSheet.h"
  35. // Core
  36. #import "Three20Core/TTCorePreprocessorMacros.h"
  37. #import "Three20Core/TTGlobalCoreLocale.h"
  38. #import "Three20Core/TTGlobalCoreRects.h"
  39. #import "Three20Core/TTDebug.h"
  40. #import "Three20Core/TTDebugFlags.h"
  41. ///////////////////////////////////////////////////////////////////////////////////////////////////
  42. ///////////////////////////////////////////////////////////////////////////////////////////////////
  43. ///////////////////////////////////////////////////////////////////////////////////////////////////
  44. @implementation TTTableViewController
  45. @synthesize tableView = _tableView;
  46. @synthesize tableBannerView = _tableBannerView;
  47. @synthesize tableOverlayView = _tableOverlayView;
  48. @synthesize loadingView = _loadingView;
  49. @synthesize errorView = _errorView;
  50. @synthesize emptyView = _emptyView;
  51. @synthesize menuView = _menuView;
  52. @synthesize tableViewStyle = _tableViewStyle;
  53. @synthesize variableHeightRows = _variableHeightRows;
  54. @synthesize showTableShadows = _showTableShadows;
  55. @synthesize clearsSelectionOnViewWillAppear = _clearsSelectionOnViewWillAppear;
  56. @synthesize dataSource = _dataSource;
  57. ///////////////////////////////////////////////////////////////////////////////////////////////////
  58. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
  59. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  60. if (self) {
  61. _lastInterfaceOrientation = self.interfaceOrientation;
  62. _tableViewStyle = UITableViewStylePlain;
  63. _clearsSelectionOnViewWillAppear = YES;
  64. }
  65. return self;
  66. }
  67. ///////////////////////////////////////////////////////////////////////////////////////////////////
  68. - (id)initWithStyle:(UITableViewStyle)style {
  69. self = [self initWithNibName:nil bundle:nil];
  70. if (self) {
  71. _tableViewStyle = style;
  72. }
  73. return self;
  74. }
  75. ///////////////////////////////////////////////////////////////////////////////////////////////////
  76. - (void)dealloc {
  77. _tableView.delegate = nil;
  78. _tableView.dataSource = nil;
  79. TT_RELEASE_SAFELY(_tableDelegate);
  80. TT_RELEASE_SAFELY(_dataSource);
  81. TT_RELEASE_SAFELY(_tableView);
  82. TT_RELEASE_SAFELY(_loadingView);
  83. TT_RELEASE_SAFELY(_errorView);
  84. TT_RELEASE_SAFELY(_emptyView);
  85. TT_RELEASE_SAFELY(_tableOverlayView);
  86. TT_RELEASE_SAFELY(_tableBannerView);
  87. [super dealloc];
  88. }
  89. ///////////////////////////////////////////////////////////////////////////////////////////////////
  90. ///////////////////////////////////////////////////////////////////////////////////////////////////
  91. #pragma mark -
  92. #pragma mark Private
  93. ///////////////////////////////////////////////////////////////////////////////////////////////////
  94. - (void)createInterstitialModel {
  95. self.dataSource = [[[TTTableViewInterstitialDataSource alloc] init] autorelease];
  96. }
  97. ///////////////////////////////////////////////////////////////////////////////////////////////////
  98. - (NSString*)defaultTitleForLoading {
  99. return TTLocalizedString(@"Loading...", @"");
  100. }
  101. ///////////////////////////////////////////////////////////////////////////////////////////////////
  102. - (void)updateTableDelegate {
  103. if (!_tableView.delegate) {
  104. [_tableDelegate release];
  105. _tableDelegate = [[self createDelegate] retain];
  106. // You need to set it to nil before changing it or it won't have any effect
  107. _tableView.delegate = nil;
  108. _tableView.delegate = _tableDelegate;
  109. }
  110. }
  111. ///////////////////////////////////////////////////////////////////////////////////////////////////
  112. - (void)addToOverlayView:(UIView*)view {
  113. if (!_tableOverlayView) {
  114. CGRect frame = [self rectForOverlayView];
  115. _tableOverlayView = [[UIView alloc] initWithFrame:frame];
  116. _tableOverlayView.autoresizesSubviews = YES;
  117. _tableOverlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth
  118. | UIViewAutoresizingFlexibleHeight;
  119. NSInteger tableIndex = [_tableView.superview.subviews indexOfObject:_tableView];
  120. if (tableIndex != NSNotFound) {
  121. [_tableView.superview addSubview:_tableOverlayView];
  122. }
  123. }
  124. view.frame = _tableOverlayView.bounds;
  125. view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  126. [_tableOverlayView addSubview:view];
  127. }
  128. ///////////////////////////////////////////////////////////////////////////////////////////////////
  129. - (void)resetOverlayView {
  130. if (_tableOverlayView && !_tableOverlayView.subviews.count) {
  131. [_tableOverlayView removeFromSuperview];
  132. TT_RELEASE_SAFELY(_tableOverlayView);
  133. }
  134. }
  135. ///////////////////////////////////////////////////////////////////////////////////////////////////
  136. - (void)addSubviewOverTableView:(UIView*)view {
  137. NSInteger tableIndex = [_tableView.superview.subviews
  138. indexOfObject:_tableView];
  139. if (NSNotFound != tableIndex) {
  140. [_tableView.superview addSubview:view];
  141. }
  142. }
  143. ///////////////////////////////////////////////////////////////////////////////////////////////////
  144. - (void)layoutOverlayView {
  145. if (_tableOverlayView) {
  146. _tableOverlayView.frame = [self rectForOverlayView];
  147. }
  148. }
  149. ///////////////////////////////////////////////////////////////////////////////////////////////////
  150. - (void)layoutBannerView {
  151. if (_tableBannerView) {
  152. _tableBannerView.frame = [self rectForBannerView];
  153. }
  154. }
  155. ///////////////////////////////////////////////////////////////////////////////////////////////////
  156. - (void)fadeOutView:(UIView*)view {
  157. [view retain];
  158. [UIView beginAnimations:nil context:view];
  159. [UIView setAnimationDuration:TT_TRANSITION_DURATION];
  160. [UIView setAnimationDelegate:self];
  161. [UIView setAnimationDidStopSelector:@selector(fadingOutViewDidStop:finished:context:)];
  162. view.alpha = 0;
  163. [UIView commitAnimations];
  164. }
  165. ///////////////////////////////////////////////////////////////////////////////////////////////////
  166. - (void)fadingOutViewDidStop:(NSString*)animationID finished:(NSNumber*)finished
  167. context:(void*)context {
  168. UIView* view = (UIView*)context;
  169. [view removeFromSuperview];
  170. [view release];
  171. }
  172. ///////////////////////////////////////////////////////////////////////////////////////////////////
  173. - (void)hideMenuAnimationDidStop:(NSString*)animationID finished:(NSNumber*)finished
  174. context:(void*)context {
  175. UIView* menuView = (UIView*)context;
  176. [menuView removeFromSuperview];
  177. }
  178. ///////////////////////////////////////////////////////////////////////////////////////////////////
  179. ///////////////////////////////////////////////////////////////////////////////////////////////////
  180. #pragma mark -
  181. #pragma mark UIViewController
  182. ///////////////////////////////////////////////////////////////////////////////////////////////////
  183. - (void)loadView {
  184. [super loadView];
  185. [self tableView];
  186. // If this view was unloaded and is now being reloaded, and it was previously
  187. // showing a table banner, then redisplay that banner now.
  188. if (_tableBannerView) {
  189. UIView* savedTableBannerView = [_tableBannerView retain];
  190. [self setTableBannerView:nil animated:NO];
  191. [self setTableBannerView:savedTableBannerView animated:NO];
  192. [savedTableBannerView release];
  193. }
  194. }
  195. ///////////////////////////////////////////////////////////////////////////////////////////////////
  196. - (void)viewDidUnload {
  197. [super viewDidUnload];
  198. _tableView.delegate = nil;
  199. _tableView.dataSource = nil;
  200. TT_RELEASE_SAFELY(_tableDelegate);
  201. TT_RELEASE_SAFELY(_tableView);
  202. [_tableOverlayView removeFromSuperview];
  203. TT_RELEASE_SAFELY(_tableOverlayView);
  204. [_loadingView removeFromSuperview];
  205. TT_RELEASE_SAFELY(_loadingView);
  206. [_errorView removeFromSuperview];
  207. TT_RELEASE_SAFELY(_errorView);
  208. [_emptyView removeFromSuperview];
  209. TT_RELEASE_SAFELY(_emptyView);
  210. [_menuView removeFromSuperview];
  211. TT_RELEASE_SAFELY(_menuView);
  212. [_menuCell removeFromSuperview];
  213. TT_RELEASE_SAFELY(_menuCell);
  214. // Do not release _tableBannerView, because we have no way to recreate it on demand if
  215. // this view gets reloaded.
  216. }
  217. ///////////////////////////////////////////////////////////////////////////////////////////////////
  218. - (void)viewWillAppear:(BOOL)animated {
  219. [super viewWillAppear:animated];
  220. if (_lastInterfaceOrientation != self.interfaceOrientation) {
  221. _lastInterfaceOrientation = self.interfaceOrientation;
  222. [_tableView reloadData];
  223. } else if ([_tableView isKindOfClass:[TTTableView class]]) {
  224. TTTableView* tableView = (TTTableView*)_tableView;
  225. tableView.highlightedLabel = nil;
  226. tableView.showShadows = _showTableShadows;
  227. }
  228. if (_clearsSelectionOnViewWillAppear) {
  229. [_tableView deselectRowAtIndexPath:[_tableView indexPathForSelectedRow] animated:animated];
  230. }
  231. }
  232. ///////////////////////////////////////////////////////////////////////////////////////////////////
  233. - (void)viewDidAppear:(BOOL)animated {
  234. [super viewDidAppear:animated];
  235. if (_flags.isShowingModel) {
  236. [_tableView flashScrollIndicators];
  237. }
  238. }
  239. ///////////////////////////////////////////////////////////////////////////////////////////////////
  240. - (void)viewWillDisappear:(BOOL)animated {
  241. [super viewWillDisappear:animated];
  242. [self hideMenu:YES];
  243. }
  244. ///////////////////////////////////////////////////////////////////////////////////////////////////
  245. - (void)setEditing:(BOOL)editing animated:(BOOL)animated {
  246. [super setEditing:editing animated:animated];
  247. [self.tableView setEditing:editing animated:animated];
  248. }
  249. ///////////////////////////////////////////////////////////////////////////////////////////////////
  250. ///////////////////////////////////////////////////////////////////////////////////////////////////
  251. #pragma mark -
  252. #pragma mark UTViewController (TTCategory)
  253. ///////////////////////////////////////////////////////////////////////////////////////////////////
  254. - (BOOL)persistView:(NSMutableDictionary*)state {
  255. CGFloat scrollY = _tableView.contentOffset.y;
  256. [state setObject:[NSNumber numberWithFloat:scrollY] forKey:@"scrollOffsetY"];
  257. return [super persistView:state];
  258. }
  259. ///////////////////////////////////////////////////////////////////////////////////////////////////
  260. - (void)restoreView:(NSDictionary*)state {
  261. CGFloat scrollY = [[state objectForKey:@"scrollOffsetY"] floatValue];
  262. if (scrollY) {
  263. CGFloat maxY = _tableView.contentSize.height - _tableView.height;
  264. if (scrollY <= maxY) {
  265. _tableView.contentOffset = CGPointMake(0, scrollY);
  266. } else {
  267. _tableView.contentOffset = CGPointMake(0, maxY);
  268. }
  269. }
  270. }
  271. ///////////////////////////////////////////////////////////////////////////////////////////////////
  272. ///////////////////////////////////////////////////////////////////////////////////////////////////
  273. #pragma mark -
  274. #pragma mark TTViewController
  275. ///////////////////////////////////////////////////////////////////////////////////////////////////
  276. - (void)keyboardDidAppear:(BOOL)animated withBounds:(CGRect)bounds {
  277. [super keyboardDidAppear:animated withBounds:bounds];
  278. self.tableView.frame = TTRectContract(self.tableView.frame, 0, bounds.size.height);
  279. [self.tableView scrollFirstResponderIntoView];
  280. [self layoutOverlayView];
  281. [self layoutBannerView];
  282. }
  283. ///////////////////////////////////////////////////////////////////////////////////////////////////
  284. - (void)keyboardWillDisappear:(BOOL)animated withBounds:(CGRect)bounds {
  285. [super keyboardWillDisappear:animated withBounds:bounds];
  286. // If we do this when there is currently no table view, we can get into a weird loop where the
  287. // table view gets doubly-initialized. self.tableView will try to initialize it; this will call
  288. // self.view, which will call -loadView, which often calls self.tableView, which initializes it.
  289. if (_tableView) {
  290. CGRect previousFrame = self.tableView.frame;
  291. self.tableView.frame = TTRectContract(self.tableView.frame, 0, -bounds.size.height);
  292. // There's any number of edge cases wherein a table view controller will get this callback but
  293. // it shouldn't resize itself -- e.g. when a controller has the keyboard up, and then drills
  294. // down into this controller. This is a sanity check to avoid situations where the table
  295. // extends way off the bottom of the screen and becomes unusable.
  296. if (self.tableView.height > self.view.bounds.size.height) {
  297. self.tableView.frame = previousFrame;
  298. }
  299. }
  300. }
  301. ///////////////////////////////////////////////////////////////////////////////////////////////////
  302. - (void)keyboardDidDisappear:(BOOL)animated withBounds:(CGRect)bounds {
  303. [super keyboardDidDisappear:animated withBounds:bounds];
  304. [self layoutOverlayView];
  305. [self layoutBannerView];
  306. }
  307. ///////////////////////////////////////////////////////////////////////////////////////////////////
  308. ///////////////////////////////////////////////////////////////////////////////////////////////////
  309. #pragma mark -
  310. #pragma mark TTModelViewController
  311. ///////////////////////////////////////////////////////////////////////////////////////////////////
  312. - (void)beginUpdates {
  313. [super beginUpdates];
  314. [_tableView beginUpdates];
  315. }
  316. ///////////////////////////////////////////////////////////////////////////////////////////////////
  317. - (void)endUpdates {
  318. [super endUpdates];
  319. [_tableView endUpdates];
  320. }
  321. ///////////////////////////////////////////////////////////////////////////////////////////////////
  322. - (BOOL)canShowModel {
  323. if ([_dataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) {
  324. NSInteger numberOfSections = [_dataSource numberOfSectionsInTableView:_tableView];
  325. if (!numberOfSections) {
  326. return NO;
  327. } else if (numberOfSections == 1) {
  328. NSInteger numberOfRows = [_dataSource tableView:_tableView numberOfRowsInSection:0];
  329. return numberOfRows > 0;
  330. } else {
  331. return YES;
  332. }
  333. } else {
  334. NSInteger numberOfRows = [_dataSource tableView:_tableView numberOfRowsInSection:0];
  335. return numberOfRows > 0;
  336. }
  337. }
  338. ///////////////////////////////////////////////////////////////////////////////////////////////////
  339. - (void)didLoadModel:(BOOL)firstTime {
  340. [super didLoadModel:firstTime];
  341. [_dataSource tableViewDidLoadModel:_tableView];
  342. }
  343. ///////////////////////////////////////////////////////////////////////////////////////////////////
  344. - (void)didShowModel:(BOOL)firstTime {
  345. [super didShowModel:firstTime];
  346. if (![self isViewAppearing] && firstTime) {
  347. [_tableView flashScrollIndicators];
  348. }
  349. }
  350. ///////////////////////////////////////////////////////////////////////////////////////////////////
  351. - (void)showModel:(BOOL)show {
  352. [self hideMenu:YES];
  353. if (show) {
  354. [self updateTableDelegate];
  355. _tableView.dataSource = _dataSource;
  356. } else {
  357. _tableView.dataSource = nil;
  358. }
  359. [_tableView reloadData];
  360. }
  361. ///////////////////////////////////////////////////////////////////////////////////////////////////
  362. - (void)showLoading:(BOOL)show {
  363. if (show) {
  364. if (!self.model.isLoaded || ![self canShowModel]) {
  365. NSString* title = _dataSource
  366. ? [_dataSource titleForLoading:NO]
  367. : [self defaultTitleForLoading];
  368. if (title.length) {
  369. TTActivityLabel* label =
  370. [[[TTActivityLabel alloc] initWithStyle:TTActivityLabelStyleWhiteBox]
  371. autorelease];
  372. label.text = title;
  373. label.backgroundColor = _tableView.backgroundColor;
  374. self.loadingView = label;
  375. }
  376. }
  377. } else {
  378. self.loadingView = nil;
  379. }
  380. }
  381. ///////////////////////////////////////////////////////////////////////////////////////////////////
  382. - (void)showError:(BOOL)show {
  383. if (show) {
  384. if (!self.model.isLoaded || ![self canShowModel]) {
  385. NSString* title = [_dataSource titleForError:_modelError];
  386. NSString* subtitle = [_dataSource subtitleForError:_modelError];
  387. UIImage* image = [_dataSource imageForError:_modelError];
  388. if (title.length || subtitle.length || image) {
  389. TTErrorView* errorView = [[[TTErrorView alloc] initWithTitle:title
  390. subtitle:subtitle
  391. image:image] autorelease];
  392. errorView.backgroundColor = _tableView.backgroundColor;
  393. self.errorView = errorView;
  394. } else {
  395. self.errorView = nil;
  396. }
  397. _tableView.dataSource = nil;
  398. [_tableView reloadData];
  399. }
  400. } else {
  401. self.errorView = nil;
  402. }
  403. }
  404. ///////////////////////////////////////////////////////////////////////////////////////////////////
  405. - (void)showEmpty:(BOOL)show {
  406. if (show) {
  407. NSString* title = [_dataSource titleForEmpty];
  408. NSString* subtitle = [_dataSource subtitleForEmpty];
  409. UIImage* image = [_dataSource imageForEmpty];
  410. if (title.length || subtitle.length || image) {
  411. TTErrorView* errorView = [[[TTErrorView alloc] initWithTitle:title
  412. subtitle:subtitle
  413. image:image] autorelease];
  414. errorView.backgroundColor = _tableView.backgroundColor;
  415. self.emptyView = errorView;
  416. } else {
  417. self.emptyView = nil;
  418. }
  419. _tableView.dataSource = nil;
  420. [_tableView reloadData];
  421. } else {
  422. self.emptyView = nil;
  423. }
  424. }
  425. ///////////////////////////////////////////////////////////////////////////////////////////////////
  426. ///////////////////////////////////////////////////////////////////////////////////////////////////
  427. #pragma mark -
  428. #pragma mark TTModelDelegate
  429. ///////////////////////////////////////////////////////////////////////////////////////////////////
  430. - (void)model:(id<TTModel>)model didUpdateObject:(id)object atIndexPath:(NSIndexPath*)indexPath {
  431. if (model == _model) {
  432. if (_flags.isShowingModel) {
  433. if ([_dataSource respondsToSelector:@selector(tableView:willUpdateObject:atIndexPath:)]) {
  434. NSIndexPath* newIndexPath = [_dataSource tableView:_tableView willUpdateObject:object
  435. atIndexPath:indexPath];
  436. if (newIndexPath) {
  437. if (newIndexPath.length == 1) {
  438. TTDCONDITIONLOG(TTDFLAG_TABLEVIEWMODIFICATIONS,
  439. @"UPDATING SECTION AT %@", newIndexPath);
  440. NSInteger sectionIndex = [newIndexPath indexAtPosition:0];
  441. [_tableView reloadSections:[NSIndexSet indexSetWithIndex:sectionIndex]
  442. withRowAnimation:UITableViewRowAnimationTop];
  443. } else if (newIndexPath.length == 2) {
  444. TTDCONDITIONLOG(TTDFLAG_TABLEVIEWMODIFICATIONS, @"UPDATING ROW AT %@", newIndexPath);
  445. [_tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
  446. withRowAnimation:UITableViewRowAnimationTop];
  447. }
  448. [self invalidateView];
  449. } else {
  450. [_tableView reloadData];
  451. }
  452. }
  453. } else {
  454. [self refresh];
  455. }
  456. }
  457. }
  458. ///////////////////////////////////////////////////////////////////////////////////////////////////
  459. - (void)model:(id<TTModel>)model didInsertObject:(id)object atIndexPath:(NSIndexPath*)indexPath {
  460. if (model == _model) {
  461. if (_flags.isShowingModel) {
  462. if ([_dataSource respondsToSelector:@selector(tableView:willInsertObject:atIndexPath:)]) {
  463. NSIndexPath* newIndexPath = [_dataSource tableView:_tableView willInsertObject:object
  464. atIndexPath:indexPath];
  465. if (newIndexPath) {
  466. if (newIndexPath.length == 1) {
  467. TTDCONDITIONLOG(TTDFLAG_TABLEVIEWMODIFICATIONS,
  468. @"INSERTING SECTION AT %@", newIndexPath);
  469. NSInteger sectionIndex = [newIndexPath indexAtPosition:0];
  470. [_tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
  471. withRowAnimation:UITableViewRowAnimationTop];
  472. } else if (newIndexPath.length == 2) {
  473. TTDCONDITIONLOG(TTDFLAG_TABLEVIEWMODIFICATIONS, @"INSERTING ROW AT %@", newIndexPath);
  474. [_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
  475. withRowAnimation:UITableViewRowAnimationTop];
  476. }
  477. [self invalidateView];
  478. } else {
  479. [_tableView reloadData];
  480. }
  481. }
  482. } else {
  483. [self refresh];
  484. }
  485. }
  486. }
  487. ///////////////////////////////////////////////////////////////////////////////////////////////////
  488. - (void)model:(id<TTModel>)model didDeleteObject:(id)object atIndexPath:(NSIndexPath*)indexPath {
  489. if (model == _model) {
  490. if (_flags.isShowingModel) {
  491. if ([_dataSource respondsToSelector:@selector(tableView:willRemoveObject:atIndexPath:)]) {
  492. NSIndexPath* newIndexPath = [_dataSource tableView:_tableView willRemoveObject:object
  493. atIndexPath:indexPath];
  494. if (newIndexPath) {
  495. if (newIndexPath.length == 1) {
  496. TTDCONDITIONLOG(TTDFLAG_TABLEVIEWMODIFICATIONS,
  497. @"DELETING SECTION AT %@", newIndexPath);
  498. NSInteger sectionIndex = [newIndexPath indexAtPosition:0];
  499. [_tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
  500. withRowAnimation:UITableViewRowAnimationLeft];
  501. } else if (newIndexPath.length == 2) {
  502. TTDCONDITIONLOG(TTDFLAG_TABLEVIEWMODIFICATIONS, @"DELETING ROW AT %@", newIndexPath);
  503. [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
  504. withRowAnimation:UITableViewRowAnimationLeft];
  505. }
  506. [self invalidateView];
  507. } else {
  508. [_tableView reloadData];
  509. }
  510. }
  511. } else {
  512. [self refresh];
  513. }
  514. }
  515. }
  516. ///////////////////////////////////////////////////////////////////////////////////////////////////
  517. ///////////////////////////////////////////////////////////////////////////////////////////////////
  518. #pragma mark -
  519. #pragma mark Public
  520. ///////////////////////////////////////////////////////////////////////////////////////////////////
  521. - (UITableView*)tableView {
  522. if (nil == _tableView) {
  523. _tableView = [[TTTableView alloc] initWithFrame:self.view.bounds style:_tableViewStyle];
  524. _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth
  525. | UIViewAutoresizingFlexibleHeight;
  526. UIColor* separatorColor = _tableViewStyle == UITableViewStyleGrouped
  527. ? TTSTYLEVAR(tableGroupedCellSeparatorColor)
  528. : TTSTYLEVAR(tablePlainCellSeparatorColor);
  529. if (separatorColor) {
  530. _tableView.separatorColor = separatorColor;
  531. }
  532. _tableView.separatorStyle = _tableViewStyle == UITableViewStyleGrouped
  533. ? TTSTYLEVAR(tableGroupedCellSeparatorStyle)
  534. : TTSTYLEVAR(tablePlainCellSeparatorStyle);
  535. UIColor* backgroundColor = _tableViewStyle == UITableViewStyleGrouped
  536. ? TTSTYLEVAR(tableGroupedBackgroundColor)
  537. : TTSTYLEVAR(tablePlainBackgroundColor);
  538. if (backgroundColor) {
  539. _tableView.backgroundColor = backgroundColor;
  540. self.view.backgroundColor = backgroundColor;
  541. }
  542. [self.view addSubview:_tableView];
  543. }
  544. return _tableView;
  545. }
  546. ///////////////////////////////////////////////////////////////////////////////////////////////////
  547. - (void)setTableView:(UITableView*)tableView {
  548. if (tableView != _tableView) {
  549. [_tableView release];
  550. _tableView = [tableView retain];
  551. if (!_tableView) {
  552. self.tableBannerView = nil;
  553. self.tableOverlayView = nil;
  554. }
  555. }
  556. }
  557. ///////////////////////////////////////////////////////////////////////////////////////////////////
  558. - (void)setTableBannerView:(UIView*)tableBannerView {
  559. [self setTableBannerView:tableBannerView animated:YES];
  560. }
  561. ///////////////////////////////////////////////////////////////////////////////////////////////////
  562. - (void)setTableBannerView:(UIView*)tableBannerView animated:(BOOL)animated {
  563. if (tableBannerView != _tableBannerView) {
  564. if (_tableBannerView) {
  565. if (animated) {
  566. [self fadeOutView:_tableBannerView];
  567. } else {
  568. [_tableBannerView removeFromSuperview];
  569. }
  570. }
  571. [_tableBannerView release];
  572. _tableBannerView = [tableBannerView retain];
  573. if (_tableBannerView) {
  574. self.tableView.contentInset = UIEdgeInsetsMake(0, 0, TTSTYLEVAR(tableBannerViewHeight), 0);
  575. self.tableView.scrollIndicatorInsets = self.tableView.contentInset;
  576. _tableBannerView.frame = [self rectForBannerView];
  577. _tableBannerView.autoresizingMask = (UIViewAutoresizingFlexibleWidth
  578. | UIViewAutoresizingFlexibleTopMargin);
  579. [self addSubviewOverTableView:_tableBannerView];
  580. if (animated) {
  581. _tableBannerView.top += TTSTYLEVAR(tableBannerViewHeight);
  582. [UIView beginAnimations:nil context:nil];
  583. [UIView setAnimationDuration:TT_TRANSITION_DURATION];
  584. [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
  585. _tableBannerView.top -= TTSTYLEVAR(tableBannerViewHeight);
  586. [UIView commitAnimations];
  587. }
  588. } else {
  589. self.tableView.contentInset = UIEdgeInsetsZero;
  590. self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
  591. }
  592. }
  593. }
  594. ///////////////////////////////////////////////////////////////////////////////////////////////////
  595. - (void)setTableOverlayView:(UIView*)tableOverlayView animated:(BOOL)animated {
  596. if (tableOverlayView != _tableOverlayView) {
  597. if (_tableOverlayView) {
  598. if (animated) {
  599. [self fadeOutView:_tableOverlayView];
  600. } else {
  601. [_tableOverlayView removeFromSuperview];
  602. }
  603. }
  604. [_tableOverlayView release];
  605. _tableOverlayView = [tableOverlayView retain];
  606. if (_tableOverlayView) {
  607. _tableOverlayView.frame = [self rectForOverlayView];
  608. [self addToOverlayView:_tableOverlayView];
  609. }
  610. // XXXjoe There seem to be cases where this gets left disable - must investigate
  611. //_tableView.scrollEnabled = !_tableOverlayView;
  612. }
  613. }
  614. ///////////////////////////////////////////////////////////////////////////////////////////////////
  615. - (void)setDataSource:(id<TTTableViewDataSource>)dataSource {
  616. if (dataSource != _dataSource) {
  617. [_dataSource release];
  618. _dataSource = [dataSource retain];
  619. _tableView.dataSource = nil;
  620. self.model = dataSource.model;
  621. }
  622. }
  623. ///////////////////////////////////////////////////////////////////////////////////////////////////
  624. - (void)setVariableHeightRows:(BOOL)variableHeightRows {
  625. if (variableHeightRows != _variableHeightRows) {
  626. _variableHeightRows = variableHeightRows;
  627. // Force the delegate to be re-created so that it supports the right kind of row measurement
  628. _tableView.delegate = nil;
  629. }
  630. }
  631. ///////////////////////////////////////////////////////////////////////////////////////////////////
  632. - (void)setLoadingView:(UIView*)view {
  633. if (view != _loadingView) {
  634. if (_loadingView) {
  635. [_loadingView removeFromSuperview];
  636. TT_RELEASE_SAFELY(_loadingView);
  637. }
  638. _loadingView = [view retain];
  639. if (_loadingView) {
  640. [self addToOverlayView:_loadingView];
  641. } else {
  642. [self resetOverlayView];
  643. }
  644. }
  645. }
  646. ///////////////////////////////////////////////////////////////////////////////////////////////////
  647. - (void)setErrorView:(UIView*)view {
  648. if (view != _errorView) {
  649. if (_errorView) {
  650. [_errorView removeFromSuperview];
  651. TT_RELEASE_SAFELY(_errorView);
  652. }
  653. _errorView = [view retain];
  654. if (_errorView) {
  655. [self addToOverlayView:_errorView];
  656. } else {
  657. [self resetOverlayView];
  658. }
  659. }
  660. }
  661. ///////////////////////////////////////////////////////////////////////////////////////////////////
  662. - (void)setEmptyView:(UIView*)view {
  663. if (view != _emptyView) {
  664. if (_emptyView) {
  665. [_emptyView removeFromSuperview];
  666. TT_RELEASE_SAFELY(_emptyView);
  667. }
  668. _emptyView = [view retain];
  669. if (_emptyView) {
  670. [self addToOverlayView:_emptyView];
  671. } else {
  672. [self resetOverlayView];
  673. }
  674. }
  675. }
  676. ///////////////////////////////////////////////////////////////////////////////////////////////////
  677. - (id<UITableViewDelegate>)createDelegate {
  678. if (_variableHeightRows) {
  679. return [[[TTTableViewVarHeightDelegate alloc] initWithController:self] autorelease];
  680. } else {
  681. return [[[TTTableViewDelegate alloc] initWithController:self] autorelease];
  682. }
  683. }
  684. ///////////////////////////////////////////////////////////////////////////////////////////////////
  685. - (void)showMenu:(UIView*)view forCell:(UITableViewCell*)cell animated:(BOOL)animated {
  686. [self hideMenu:YES];
  687. _menuView = [view retain];
  688. _menuCell = [cell retain];
  689. // Insert the cell below all content subviews
  690. [_menuCell.contentView insertSubview:_menuView atIndex:0];
  691. if (animated) {
  692. [UIView beginAnimations:nil context:nil];
  693. [UIView setAnimationDuration:TT_FAST_TRANSITION_DURATION];
  694. [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
  695. }
  696. // Move each content subview down, revealing the menu
  697. for (UIView* subview in _menuCell.contentView.subviews) {
  698. if (subview != _menuView) {
  699. subview.left -= _menuCell.contentView.width;
  700. }
  701. }
  702. if (animated) {
  703. [UIView commitAnimations];
  704. }
  705. }
  706. ///////////////////////////////////////////////////////////////////////////////////////////////////
  707. - (void)hideMenu:(BOOL)animated {
  708. if (_menuView) {
  709. if (animated) {
  710. [UIView beginAnimations:nil context:_menuView];
  711. [UIView setAnimationDuration:TT_FAST_TRANSITION_DURATION];
  712. [UIView setAnimationDelegate:self];
  713. [UIView setAnimationDidStopSelector:@selector(hideMenuAnimationDidStop:finished:context:)];
  714. }
  715. for (UIView* view in _menuCell.contentView.subviews) {
  716. if (view != _menuView) {
  717. view.left += _menuCell.contentView.width;
  718. }
  719. }
  720. if (animated) {
  721. [UIView commitAnimations];
  722. } else {
  723. [_menuView removeFromSuperview];
  724. }
  725. TT_RELEASE_SAFELY(_menuView);
  726. TT_RELEASE_SAFELY(_menuCell);
  727. }
  728. }
  729. ///////////////////////////////////////////////////////////////////////////////////////////////////
  730. - (void)didSelectObject:(id)object atIndexPath:(NSIndexPath*)indexPath {
  731. if ([object respondsToSelector:@selector(URLValue)]) {
  732. NSString* URL = [object URLValue];
  733. if (URL) {
  734. TTOpenURLFromView(URL, self.view);
  735. }
  736. }
  737. }
  738. ///////////////////////////////////////////////////////////////////////////////////////////////////
  739. - (BOOL)shouldOpenURL:(NSString*)URL {
  740. return YES;
  741. }
  742. ///////////////////////////////////////////////////////////////////////////////////////////////////
  743. - (void)didBeginDragging {
  744. [self hideMenu:YES];
  745. }
  746. ///////////////////////////////////////////////////////////////////////////////////////////////////
  747. - (void)didEndDragging {
  748. }
  749. ///////////////////////////////////////////////////////////////////////////////////////////////////
  750. - (CGRect)rectForOverlayView {
  751. return [_tableView frameWithKeyboardSubtracted:0];
  752. }
  753. ///////////////////////////////////////////////////////////////////////////////////////////////////
  754. - (CGRect)rectForBannerView {
  755. CGRect tableFrame = [_tableView frameWithKeyboardSubtracted:0];
  756. const CGFloat bannerViewHeight = TTSTYLEVAR(tableBannerViewHeight);
  757. return CGRectMake(tableFrame.origin.x,
  758. (tableFrame.origin.y + tableFrame.size.height) - bannerViewHeight,
  759. tableFrame.size.width, bannerViewHeight);
  760. }
  761. ///////////////////////////////////////////////////////////////////////////////////////////////////
  762. - (void)invalidateModel {
  763. [super invalidateModel];
  764. // Renew the tableView delegate when the model is refreshed.
  765. // Otherwise the delegate will be retained the model.
  766. // You need to set it to nil before changing it or it won't have any effect
  767. _tableView.delegate = nil;
  768. [self updateTableDelegate];
  769. }
  770. @end