/src/Three20UI/Sources/TTTableViewController.m
Objective C | 968 lines | 656 code | 198 blank | 114 comment | 149 complexity | 2ffd8f4ed25c74e00254fc772046cc7c MD5 | raw file
- //
- // Copyright 2009-2011 Facebook
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- //
- #import "Three20UI/TTTableViewController.h"
- // UI
- #import "Three20UI/TTNavigator.h"
- #import "Three20UI/TTActivityLabel.h"
- #import "Three20UI/TTErrorView.h"
- #import "Three20UI/TTListDataSource.h"
- #import "Three20UI/TTTableView.h"
- #import "Three20UI/TTTableViewDelegate.h"
- #import "Three20UI/TTTableViewVarHeightDelegate.h"
- #import "Three20UI/UIViewAdditions.h"
- #import "Three20UI/UITableViewAdditions.h"
- // UINavigator
- #import "Three20UINavigator/TTURLObject.h"
- // UICommon
- #import "Three20UICommon/TTGlobalUICommon.h"
- #import "Three20UICommon/UIViewControllerAdditions.h"
- // Style
- #import "Three20Style/TTGlobalStyle.h"
- #import "Three20Style/TTDefaultStyleSheet.h"
- // Core
- #import "Three20Core/TTCorePreprocessorMacros.h"
- #import "Three20Core/TTGlobalCoreLocale.h"
- #import "Three20Core/TTGlobalCoreRects.h"
- #import "Three20Core/TTDebug.h"
- #import "Three20Core/TTDebugFlags.h"
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- @implementation TTTableViewController
- @synthesize tableView = _tableView;
- @synthesize tableBannerView = _tableBannerView;
- @synthesize tableOverlayView = _tableOverlayView;
- @synthesize loadingView = _loadingView;
- @synthesize errorView = _errorView;
- @synthesize emptyView = _emptyView;
- @synthesize menuView = _menuView;
- @synthesize tableViewStyle = _tableViewStyle;
- @synthesize variableHeightRows = _variableHeightRows;
- @synthesize showTableShadows = _showTableShadows;
- @synthesize clearsSelectionOnViewWillAppear = _clearsSelectionOnViewWillAppear;
- @synthesize dataSource = _dataSource;
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
- self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
- if (self) {
- _lastInterfaceOrientation = self.interfaceOrientation;
- _tableViewStyle = UITableViewStylePlain;
- _clearsSelectionOnViewWillAppear = YES;
- }
- return self;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (id)initWithStyle:(UITableViewStyle)style {
- self = [self initWithNibName:nil bundle:nil];
- if (self) {
- _tableViewStyle = style;
- }
- return self;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)dealloc {
- _tableView.delegate = nil;
- _tableView.dataSource = nil;
- TT_RELEASE_SAFELY(_tableDelegate);
- TT_RELEASE_SAFELY(_dataSource);
- TT_RELEASE_SAFELY(_tableView);
- TT_RELEASE_SAFELY(_loadingView);
- TT_RELEASE_SAFELY(_errorView);
- TT_RELEASE_SAFELY(_emptyView);
- TT_RELEASE_SAFELY(_tableOverlayView);
- TT_RELEASE_SAFELY(_tableBannerView);
- [super dealloc];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- #pragma mark Private
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)createInterstitialModel {
- self.dataSource = [[[TTTableViewInterstitialDataSource alloc] init] autorelease];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (NSString*)defaultTitleForLoading {
- return TTLocalizedString(@"Loading...", @"");
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)updateTableDelegate {
- if (!_tableView.delegate) {
- [_tableDelegate release];
- _tableDelegate = [[self createDelegate] retain];
- // You need to set it to nil before changing it or it won't have any effect
- _tableView.delegate = nil;
- _tableView.delegate = _tableDelegate;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)addToOverlayView:(UIView*)view {
- if (!_tableOverlayView) {
- CGRect frame = [self rectForOverlayView];
- _tableOverlayView = [[UIView alloc] initWithFrame:frame];
- _tableOverlayView.autoresizesSubviews = YES;
- _tableOverlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth
- | UIViewAutoresizingFlexibleHeight;
- NSInteger tableIndex = [_tableView.superview.subviews indexOfObject:_tableView];
- if (tableIndex != NSNotFound) {
- [_tableView.superview addSubview:_tableOverlayView];
- }
- }
- view.frame = _tableOverlayView.bounds;
- view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- [_tableOverlayView addSubview:view];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)resetOverlayView {
- if (_tableOverlayView && !_tableOverlayView.subviews.count) {
- [_tableOverlayView removeFromSuperview];
- TT_RELEASE_SAFELY(_tableOverlayView);
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)addSubviewOverTableView:(UIView*)view {
- NSInteger tableIndex = [_tableView.superview.subviews
- indexOfObject:_tableView];
- if (NSNotFound != tableIndex) {
- [_tableView.superview addSubview:view];
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)layoutOverlayView {
- if (_tableOverlayView) {
- _tableOverlayView.frame = [self rectForOverlayView];
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)layoutBannerView {
- if (_tableBannerView) {
- _tableBannerView.frame = [self rectForBannerView];
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)fadeOutView:(UIView*)view {
- [view retain];
- [UIView beginAnimations:nil context:view];
- [UIView setAnimationDuration:TT_TRANSITION_DURATION];
- [UIView setAnimationDelegate:self];
- [UIView setAnimationDidStopSelector:@selector(fadingOutViewDidStop:finished:context:)];
- view.alpha = 0;
- [UIView commitAnimations];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)fadingOutViewDidStop:(NSString*)animationID finished:(NSNumber*)finished
- context:(void*)context {
- UIView* view = (UIView*)context;
- [view removeFromSuperview];
- [view release];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)hideMenuAnimationDidStop:(NSString*)animationID finished:(NSNumber*)finished
- context:(void*)context {
- UIView* menuView = (UIView*)context;
- [menuView removeFromSuperview];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- #pragma mark UIViewController
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)loadView {
- [super loadView];
- [self tableView];
- // If this view was unloaded and is now being reloaded, and it was previously
- // showing a table banner, then redisplay that banner now.
- if (_tableBannerView) {
- UIView* savedTableBannerView = [_tableBannerView retain];
- [self setTableBannerView:nil animated:NO];
- [self setTableBannerView:savedTableBannerView animated:NO];
- [savedTableBannerView release];
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)viewDidUnload {
- [super viewDidUnload];
- _tableView.delegate = nil;
- _tableView.dataSource = nil;
- TT_RELEASE_SAFELY(_tableDelegate);
- TT_RELEASE_SAFELY(_tableView);
- [_tableOverlayView removeFromSuperview];
- TT_RELEASE_SAFELY(_tableOverlayView);
- [_loadingView removeFromSuperview];
- TT_RELEASE_SAFELY(_loadingView);
- [_errorView removeFromSuperview];
- TT_RELEASE_SAFELY(_errorView);
- [_emptyView removeFromSuperview];
- TT_RELEASE_SAFELY(_emptyView);
- [_menuView removeFromSuperview];
- TT_RELEASE_SAFELY(_menuView);
- [_menuCell removeFromSuperview];
- TT_RELEASE_SAFELY(_menuCell);
- // Do not release _tableBannerView, because we have no way to recreate it on demand if
- // this view gets reloaded.
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)viewWillAppear:(BOOL)animated {
- [super viewWillAppear:animated];
- if (_lastInterfaceOrientation != self.interfaceOrientation) {
- _lastInterfaceOrientation = self.interfaceOrientation;
- [_tableView reloadData];
- } else if ([_tableView isKindOfClass:[TTTableView class]]) {
- TTTableView* tableView = (TTTableView*)_tableView;
- tableView.highlightedLabel = nil;
- tableView.showShadows = _showTableShadows;
- }
- if (_clearsSelectionOnViewWillAppear) {
- [_tableView deselectRowAtIndexPath:[_tableView indexPathForSelectedRow] animated:animated];
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)viewDidAppear:(BOOL)animated {
- [super viewDidAppear:animated];
- if (_flags.isShowingModel) {
- [_tableView flashScrollIndicators];
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)viewWillDisappear:(BOOL)animated {
- [super viewWillDisappear:animated];
- [self hideMenu:YES];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)setEditing:(BOOL)editing animated:(BOOL)animated {
- [super setEditing:editing animated:animated];
- [self.tableView setEditing:editing animated:animated];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- #pragma mark UTViewController (TTCategory)
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (BOOL)persistView:(NSMutableDictionary*)state {
- CGFloat scrollY = _tableView.contentOffset.y;
- [state setObject:[NSNumber numberWithFloat:scrollY] forKey:@"scrollOffsetY"];
- return [super persistView:state];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)restoreView:(NSDictionary*)state {
- CGFloat scrollY = [[state objectForKey:@"scrollOffsetY"] floatValue];
- if (scrollY) {
- CGFloat maxY = _tableView.contentSize.height - _tableView.height;
- if (scrollY <= maxY) {
- _tableView.contentOffset = CGPointMake(0, scrollY);
- } else {
- _tableView.contentOffset = CGPointMake(0, maxY);
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- #pragma mark TTViewController
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)keyboardDidAppear:(BOOL)animated withBounds:(CGRect)bounds {
- [super keyboardDidAppear:animated withBounds:bounds];
- self.tableView.frame = TTRectContract(self.tableView.frame, 0, bounds.size.height);
- [self.tableView scrollFirstResponderIntoView];
- [self layoutOverlayView];
- [self layoutBannerView];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)keyboardWillDisappear:(BOOL)animated withBounds:(CGRect)bounds {
- [super keyboardWillDisappear:animated withBounds:bounds];
- // If we do this when there is currently no table view, we can get into a weird loop where the
- // table view gets doubly-initialized. self.tableView will try to initialize it; this will call
- // self.view, which will call -loadView, which often calls self.tableView, which initializes it.
- if (_tableView) {
- CGRect previousFrame = self.tableView.frame;
- self.tableView.frame = TTRectContract(self.tableView.frame, 0, -bounds.size.height);
- // There's any number of edge cases wherein a table view controller will get this callback but
- // it shouldn't resize itself -- e.g. when a controller has the keyboard up, and then drills
- // down into this controller. This is a sanity check to avoid situations where the table
- // extends way off the bottom of the screen and becomes unusable.
- if (self.tableView.height > self.view.bounds.size.height) {
- self.tableView.frame = previousFrame;
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)keyboardDidDisappear:(BOOL)animated withBounds:(CGRect)bounds {
- [super keyboardDidDisappear:animated withBounds:bounds];
- [self layoutOverlayView];
- [self layoutBannerView];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- #pragma mark TTModelViewController
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)beginUpdates {
- [super beginUpdates];
- [_tableView beginUpdates];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)endUpdates {
- [super endUpdates];
- [_tableView endUpdates];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (BOOL)canShowModel {
- if ([_dataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) {
- NSInteger numberOfSections = [_dataSource numberOfSectionsInTableView:_tableView];
- if (!numberOfSections) {
- return NO;
- } else if (numberOfSections == 1) {
- NSInteger numberOfRows = [_dataSource tableView:_tableView numberOfRowsInSection:0];
- return numberOfRows > 0;
- } else {
- return YES;
- }
- } else {
- NSInteger numberOfRows = [_dataSource tableView:_tableView numberOfRowsInSection:0];
- return numberOfRows > 0;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)didLoadModel:(BOOL)firstTime {
- [super didLoadModel:firstTime];
- [_dataSource tableViewDidLoadModel:_tableView];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)didShowModel:(BOOL)firstTime {
- [super didShowModel:firstTime];
- if (![self isViewAppearing] && firstTime) {
- [_tableView flashScrollIndicators];
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)showModel:(BOOL)show {
- [self hideMenu:YES];
- if (show) {
- [self updateTableDelegate];
- _tableView.dataSource = _dataSource;
- } else {
- _tableView.dataSource = nil;
- }
- [_tableView reloadData];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)showLoading:(BOOL)show {
- if (show) {
- if (!self.model.isLoaded || ![self canShowModel]) {
- NSString* title = _dataSource
- ? [_dataSource titleForLoading:NO]
- : [self defaultTitleForLoading];
- if (title.length) {
- TTActivityLabel* label =
- [[[TTActivityLabel alloc] initWithStyle:TTActivityLabelStyleWhiteBox]
- autorelease];
- label.text = title;
- label.backgroundColor = _tableView.backgroundColor;
- self.loadingView = label;
- }
- }
- } else {
- self.loadingView = nil;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)showError:(BOOL)show {
- if (show) {
- if (!self.model.isLoaded || ![self canShowModel]) {
- NSString* title = [_dataSource titleForError:_modelError];
- NSString* subtitle = [_dataSource subtitleForError:_modelError];
- UIImage* image = [_dataSource imageForError:_modelError];
- if (title.length || subtitle.length || image) {
- TTErrorView* errorView = [[[TTErrorView alloc] initWithTitle:title
- subtitle:subtitle
- image:image] autorelease];
- errorView.backgroundColor = _tableView.backgroundColor;
- self.errorView = errorView;
- } else {
- self.errorView = nil;
- }
- _tableView.dataSource = nil;
- [_tableView reloadData];
- }
- } else {
- self.errorView = nil;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)showEmpty:(BOOL)show {
- if (show) {
- NSString* title = [_dataSource titleForEmpty];
- NSString* subtitle = [_dataSource subtitleForEmpty];
- UIImage* image = [_dataSource imageForEmpty];
- if (title.length || subtitle.length || image) {
- TTErrorView* errorView = [[[TTErrorView alloc] initWithTitle:title
- subtitle:subtitle
- image:image] autorelease];
- errorView.backgroundColor = _tableView.backgroundColor;
- self.emptyView = errorView;
- } else {
- self.emptyView = nil;
- }
- _tableView.dataSource = nil;
- [_tableView reloadData];
- } else {
- self.emptyView = nil;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- #pragma mark TTModelDelegate
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)model:(id<TTModel>)model didUpdateObject:(id)object atIndexPath:(NSIndexPath*)indexPath {
- if (model == _model) {
- if (_flags.isShowingModel) {
- if ([_dataSource respondsToSelector:@selector(tableView:willUpdateObject:atIndexPath:)]) {
- NSIndexPath* newIndexPath = [_dataSource tableView:_tableView willUpdateObject:object
- atIndexPath:indexPath];
- if (newIndexPath) {
- if (newIndexPath.length == 1) {
- TTDCONDITIONLOG(TTDFLAG_TABLEVIEWMODIFICATIONS,
- @"UPDATING SECTION AT %@", newIndexPath);
- NSInteger sectionIndex = [newIndexPath indexAtPosition:0];
- [_tableView reloadSections:[NSIndexSet indexSetWithIndex:sectionIndex]
- withRowAnimation:UITableViewRowAnimationTop];
- } else if (newIndexPath.length == 2) {
- TTDCONDITIONLOG(TTDFLAG_TABLEVIEWMODIFICATIONS, @"UPDATING ROW AT %@", newIndexPath);
- [_tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
- withRowAnimation:UITableViewRowAnimationTop];
- }
- [self invalidateView];
- } else {
- [_tableView reloadData];
- }
- }
- } else {
- [self refresh];
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)model:(id<TTModel>)model didInsertObject:(id)object atIndexPath:(NSIndexPath*)indexPath {
- if (model == _model) {
- if (_flags.isShowingModel) {
- if ([_dataSource respondsToSelector:@selector(tableView:willInsertObject:atIndexPath:)]) {
- NSIndexPath* newIndexPath = [_dataSource tableView:_tableView willInsertObject:object
- atIndexPath:indexPath];
- if (newIndexPath) {
- if (newIndexPath.length == 1) {
- TTDCONDITIONLOG(TTDFLAG_TABLEVIEWMODIFICATIONS,
- @"INSERTING SECTION AT %@", newIndexPath);
- NSInteger sectionIndex = [newIndexPath indexAtPosition:0];
- [_tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
- withRowAnimation:UITableViewRowAnimationTop];
- } else if (newIndexPath.length == 2) {
- TTDCONDITIONLOG(TTDFLAG_TABLEVIEWMODIFICATIONS, @"INSERTING ROW AT %@", newIndexPath);
- [_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
- withRowAnimation:UITableViewRowAnimationTop];
- }
- [self invalidateView];
- } else {
- [_tableView reloadData];
- }
- }
- } else {
- [self refresh];
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)model:(id<TTModel>)model didDeleteObject:(id)object atIndexPath:(NSIndexPath*)indexPath {
- if (model == _model) {
- if (_flags.isShowingModel) {
- if ([_dataSource respondsToSelector:@selector(tableView:willRemoveObject:atIndexPath:)]) {
- NSIndexPath* newIndexPath = [_dataSource tableView:_tableView willRemoveObject:object
- atIndexPath:indexPath];
- if (newIndexPath) {
- if (newIndexPath.length == 1) {
- TTDCONDITIONLOG(TTDFLAG_TABLEVIEWMODIFICATIONS,
- @"DELETING SECTION AT %@", newIndexPath);
- NSInteger sectionIndex = [newIndexPath indexAtPosition:0];
- [_tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
- withRowAnimation:UITableViewRowAnimationLeft];
- } else if (newIndexPath.length == 2) {
- TTDCONDITIONLOG(TTDFLAG_TABLEVIEWMODIFICATIONS, @"DELETING ROW AT %@", newIndexPath);
- [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
- withRowAnimation:UITableViewRowAnimationLeft];
- }
- [self invalidateView];
- } else {
- [_tableView reloadData];
- }
- }
- } else {
- [self refresh];
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- #pragma mark Public
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (UITableView*)tableView {
- if (nil == _tableView) {
- _tableView = [[TTTableView alloc] initWithFrame:self.view.bounds style:_tableViewStyle];
- _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth
- | UIViewAutoresizingFlexibleHeight;
- UIColor* separatorColor = _tableViewStyle == UITableViewStyleGrouped
- ? TTSTYLEVAR(tableGroupedCellSeparatorColor)
- : TTSTYLEVAR(tablePlainCellSeparatorColor);
- if (separatorColor) {
- _tableView.separatorColor = separatorColor;
- }
- _tableView.separatorStyle = _tableViewStyle == UITableViewStyleGrouped
- ? TTSTYLEVAR(tableGroupedCellSeparatorStyle)
- : TTSTYLEVAR(tablePlainCellSeparatorStyle);
- UIColor* backgroundColor = _tableViewStyle == UITableViewStyleGrouped
- ? TTSTYLEVAR(tableGroupedBackgroundColor)
- : TTSTYLEVAR(tablePlainBackgroundColor);
- if (backgroundColor) {
- _tableView.backgroundColor = backgroundColor;
- self.view.backgroundColor = backgroundColor;
- }
- [self.view addSubview:_tableView];
- }
- return _tableView;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)setTableView:(UITableView*)tableView {
- if (tableView != _tableView) {
- [_tableView release];
- _tableView = [tableView retain];
- if (!_tableView) {
- self.tableBannerView = nil;
- self.tableOverlayView = nil;
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)setTableBannerView:(UIView*)tableBannerView {
- [self setTableBannerView:tableBannerView animated:YES];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)setTableBannerView:(UIView*)tableBannerView animated:(BOOL)animated {
- if (tableBannerView != _tableBannerView) {
- if (_tableBannerView) {
- if (animated) {
- [self fadeOutView:_tableBannerView];
- } else {
- [_tableBannerView removeFromSuperview];
- }
- }
- [_tableBannerView release];
- _tableBannerView = [tableBannerView retain];
- if (_tableBannerView) {
- self.tableView.contentInset = UIEdgeInsetsMake(0, 0, TTSTYLEVAR(tableBannerViewHeight), 0);
- self.tableView.scrollIndicatorInsets = self.tableView.contentInset;
- _tableBannerView.frame = [self rectForBannerView];
- _tableBannerView.autoresizingMask = (UIViewAutoresizingFlexibleWidth
- | UIViewAutoresizingFlexibleTopMargin);
- [self addSubviewOverTableView:_tableBannerView];
- if (animated) {
- _tableBannerView.top += TTSTYLEVAR(tableBannerViewHeight);
- [UIView beginAnimations:nil context:nil];
- [UIView setAnimationDuration:TT_TRANSITION_DURATION];
- [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
- _tableBannerView.top -= TTSTYLEVAR(tableBannerViewHeight);
- [UIView commitAnimations];
- }
- } else {
- self.tableView.contentInset = UIEdgeInsetsZero;
- self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)setTableOverlayView:(UIView*)tableOverlayView animated:(BOOL)animated {
- if (tableOverlayView != _tableOverlayView) {
- if (_tableOverlayView) {
- if (animated) {
- [self fadeOutView:_tableOverlayView];
- } else {
- [_tableOverlayView removeFromSuperview];
- }
- }
- [_tableOverlayView release];
- _tableOverlayView = [tableOverlayView retain];
- if (_tableOverlayView) {
- _tableOverlayView.frame = [self rectForOverlayView];
- [self addToOverlayView:_tableOverlayView];
- }
- // XXXjoe There seem to be cases where this gets left disable - must investigate
- //_tableView.scrollEnabled = !_tableOverlayView;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)setDataSource:(id<TTTableViewDataSource>)dataSource {
- if (dataSource != _dataSource) {
- [_dataSource release];
- _dataSource = [dataSource retain];
- _tableView.dataSource = nil;
- self.model = dataSource.model;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)setVariableHeightRows:(BOOL)variableHeightRows {
- if (variableHeightRows != _variableHeightRows) {
- _variableHeightRows = variableHeightRows;
- // Force the delegate to be re-created so that it supports the right kind of row measurement
- _tableView.delegate = nil;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)setLoadingView:(UIView*)view {
- if (view != _loadingView) {
- if (_loadingView) {
- [_loadingView removeFromSuperview];
- TT_RELEASE_SAFELY(_loadingView);
- }
- _loadingView = [view retain];
- if (_loadingView) {
- [self addToOverlayView:_loadingView];
- } else {
- [self resetOverlayView];
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)setErrorView:(UIView*)view {
- if (view != _errorView) {
- if (_errorView) {
- [_errorView removeFromSuperview];
- TT_RELEASE_SAFELY(_errorView);
- }
- _errorView = [view retain];
- if (_errorView) {
- [self addToOverlayView:_errorView];
- } else {
- [self resetOverlayView];
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)setEmptyView:(UIView*)view {
- if (view != _emptyView) {
- if (_emptyView) {
- [_emptyView removeFromSuperview];
- TT_RELEASE_SAFELY(_emptyView);
- }
- _emptyView = [view retain];
- if (_emptyView) {
- [self addToOverlayView:_emptyView];
- } else {
- [self resetOverlayView];
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (id<UITableViewDelegate>)createDelegate {
- if (_variableHeightRows) {
- return [[[TTTableViewVarHeightDelegate alloc] initWithController:self] autorelease];
- } else {
- return [[[TTTableViewDelegate alloc] initWithController:self] autorelease];
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)showMenu:(UIView*)view forCell:(UITableViewCell*)cell animated:(BOOL)animated {
- [self hideMenu:YES];
- _menuView = [view retain];
- _menuCell = [cell retain];
- // Insert the cell below all content subviews
- [_menuCell.contentView insertSubview:_menuView atIndex:0];
- if (animated) {
- [UIView beginAnimations:nil context:nil];
- [UIView setAnimationDuration:TT_FAST_TRANSITION_DURATION];
- [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
- }
- // Move each content subview down, revealing the menu
- for (UIView* subview in _menuCell.contentView.subviews) {
- if (subview != _menuView) {
- subview.left -= _menuCell.contentView.width;
- }
- }
- if (animated) {
- [UIView commitAnimations];
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)hideMenu:(BOOL)animated {
- if (_menuView) {
- if (animated) {
- [UIView beginAnimations:nil context:_menuView];
- [UIView setAnimationDuration:TT_FAST_TRANSITION_DURATION];
- [UIView setAnimationDelegate:self];
- [UIView setAnimationDidStopSelector:@selector(hideMenuAnimationDidStop:finished:context:)];
- }
- for (UIView* view in _menuCell.contentView.subviews) {
- if (view != _menuView) {
- view.left += _menuCell.contentView.width;
- }
- }
- if (animated) {
- [UIView commitAnimations];
- } else {
- [_menuView removeFromSuperview];
- }
- TT_RELEASE_SAFELY(_menuView);
- TT_RELEASE_SAFELY(_menuCell);
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)didSelectObject:(id)object atIndexPath:(NSIndexPath*)indexPath {
- if ([object respondsToSelector:@selector(URLValue)]) {
- NSString* URL = [object URLValue];
- if (URL) {
- TTOpenURLFromView(URL, self.view);
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (BOOL)shouldOpenURL:(NSString*)URL {
- return YES;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)didBeginDragging {
- [self hideMenu:YES];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)didEndDragging {
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (CGRect)rectForOverlayView {
- return [_tableView frameWithKeyboardSubtracted:0];
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (CGRect)rectForBannerView {
- CGRect tableFrame = [_tableView frameWithKeyboardSubtracted:0];
- const CGFloat bannerViewHeight = TTSTYLEVAR(tableBannerViewHeight);
- return CGRectMake(tableFrame.origin.x,
- (tableFrame.origin.y + tableFrame.size.height) - bannerViewHeight,
- tableFrame.size.width, bannerViewHeight);
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)invalidateModel {
- [super invalidateModel];
- // Renew the tableView delegate when the model is refreshed.
- // Otherwise the delegate will be retained the model.
- // You need to set it to nil before changing it or it won't have any effect
- _tableView.delegate = nil;
- [self updateTableDelegate];
- }
- @end