Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Table View with lockable columns and horizontal scrolling?

I am looking to create a table that has the following features:

  • Variable number of columns that scroll horizontally with the rest of the table
  • Variable number of lockable columns that don't scroll horizontally with the rest of the table
  • Sortable column headers
  • Scrolls vertically
  • Scrolls horizontally

I have seen people use different strategies to accomplish similar functionality including:

  1. UITableview with each table cell containing a UITableview transformed 90 degrees placed inside of another UIScrollview to handle the horizontal scrolling. For this to work however i would need to use locked rotated tableviews for each cell and it seems like there would be some funkiness with cell dequeing if i wanted to scroll the entire main table instead of the table within each table cell.
  2. A variation of number one but using a custom uiview instead of a uitableview for each tablecell. (leading candidate so far)
  3. Custom UIScrollview class that replicates the functionality of a UITableview with added features such as MDSpreadView. Seems like the most flexible and most difficult to implement.

Does anyone have any examples/suggestions/advice on what to look into that will be reasonably easy to get up and running, great scrolling performance, and flexible.

I am still in the planning phase, trying to figure out the best way to go about this and have put down some ideas so far which consists of two uitableviews with synced scrolling. The left table view would be used for the locked columns and the right table would be used for the horizontal scrolling table. Both tables would use the same uiviews as tablecells with custom uiview objects for each column.

enter image description here

UPDATE

It seems pretty straight forward to be able to sync the scrolling between the locked columns and horizontally scrolling tables and so far this seems to be the option i am leaning towards (#2 above). Does anyone see any potential pitfalls or roadblocks with this approach?

A great example of the desired finished product i am after can be found in the Roambi App (screenshot below)

enter image description here

Thx

like image 372
s.newave Avatar asked Mar 12 '13 17:03

s.newave


2 Answers

I achieved this with three views, managed via standard protocols and a single view controller. In this solution, you're only concerned with linking the vertical scrolling of the two table view controllers. The horizontal scrolling comes for free via a scroll view wrapping the table view body. I utilize the following UITableViewDelegate function to get locked column headers:

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section;

enter image description here

View Structure:

- View - The VCs root view
  - Table View     - rowHeadersTable                 -  the locked left hand columns
  - Scroll View    - rowBodyHorizontalScrollView     - a container of the table body
    - Table View   - rowBodyVerticalContentTableView - the table body, as wide as it needs to be beyond the frame of the parent scroll view

View Controller:

Implemented protocols:

@interface LockedTableColumnsViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>

Setup:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    [self.rowHeadersTable                 setDataSource:self];
    [self.rowHeadersTable                 setDelegate:self];

    [self.rowBodyVerticalContentTableView setDataSource:self];
    [self.rowBodyVerticalContentTableView setDelegate:self];
}

-(void)viewDidAppear:(BOOL)animated
{
    // This ensures the scroll view can only scroll horizontally, and adapts to the size of its member content
    [self.rowBodyHorizontalScrollView setContentSize:self.rowBodyVerticalContentTableView.frame.size]; // Important to do this here rather than viewDidLoad, because we want a final reading on self.rowBodyVerticalContentTableView.frame.size

    [super viewDidAppear:animated];
}

// Do your Table Data Source however you choose

Manage vertical scroll linking between rowHeadersTable and rowBodyVerticalContentTableView:

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView == self.rowBodyVerticalContentTableView) {
        [self.rowHeadersTable setContentOffset:CGPointMake(self.rowHeadersTable.contentOffset.x, scrollView.contentOffset.y)];
    }
    else if (scrollView == self.rowHeadersTable) {
        [self.rowBodyVerticalContentTableView setContentOffset:CGPointMake(self.rowBodyVerticalContentTableView.contentOffset.x, scrollView.contentOffset.y)];
    }
}

N.B. This will perform nicely for long (meaning tall) tables since it utilizes prototype cells etc. However it doesn't optimize for horizontally intense row content. My table body content is text, and a finite number of columns. If you don't have that luxury you could always make your table row body a horizontally flowing collection view, but then you'd have to link all your row collection view offsets (via visible cells I guess). That's a whole other animal. Good luck!

like image 56
bdalziel Avatar answered Oct 12 '22 09:10

bdalziel


If your target OS is older than iOS 6.0, you could use nested table views ad described here. Don't forget to synchronize content offset of each horizontal table.

But if you targeted iOS 6.0+ you can use MMSpreadsheetView, but its performance is very bad and you are not allowed to set different sizes for its columns.

The best option you have is to write your own UICollectionView layout.

like image 21
Vyacheslav Avatar answered Oct 12 '22 08:10

Vyacheslav