In a regular UITableView
in edit mode you drag the cell into the position you want the cell to be in and the other cells can pop into place. I want to create a UITableView
edit mode where you select a cell and it is held in the center as you scroll the tableview to move the selected item, holding the selected item in the center with the table cells moving around the center selected cell.
A valid 'bounty worthy' answer will require a minimally working example that holds a selected cell in the center of the table and can be moved by swiping the table up and down. Including the edge cases of first and last position in the table. Alternatively, you can outline the key points of what you think would work and if they lead me in the right direction, then you'll get the bounty.
I have established a project called PickerTableView on GitHub. Working on the develop branch. Selection is working and I'm working on subclassing TableView to handle the movement of the cell on scroll. Finding a working solution before me will still earn the bounty.
Based upon a comment, I'll provide some ASCII art.
|==========|
| Next|
|==========|
| |
|----------|
| |
|----------|
| |
|----------|
| |
|----------|
| |
|==========|
|==========|
| Next|
|==========|
| |
|----------|
| |
|----------|
| X|
|----------|
| |
|----------|
| |
|==========|
|=============|
| Done|
|=============|
| |
|-------------|
| |
|-------------|
| This cell is|
| Highlighted |
| and locked |
| in place |
|-------------|
| |
|-------------|
| |
|=============|
As you scroll the tableview the cells that were not selected flow around the selected cell while the selected cell stays in the middle.
A prototype cell acts a template for your cell's appearance. It includes the views you want to display and their arrangement within the content area of the cell. At runtime, the table's data source object creates actual cells from the prototypes and configures them with your app's data.
A table view displays a single column of vertically scrolling content, divided into rows and sections. Each row of a table displays a single piece of information related to your app.
Would it be acceptable if the selected cell was not really a cell but a separate UIView
(that could be made to look like a cell)? If so, you could do something like this:
UIView
over the table.-scrollViewDidScroll:
.UIView
To see it in action, I've created a UIViewController subclass for you to test:
PickerTableViewController.h
#import <UIKit/UIKit.h>
@interface PickerTableViewController : UIViewController
@end
@interface SelectedItemView : UIView
@property (nonatomic, readonly) UILabel *label;
@end
PickerTableViewController.m
#import "PickerTableViewController.h"
@interface PickerTableViewController () <UITableViewDataSource, UITableViewDelegate>
@property (strong, nonatomic) UIButton *button;
@property (strong, nonatomic) UITableView *tableView;
@property (strong, nonatomic) UIView *tableViewContainer;
@property (strong, nonatomic) SelectedItemView *selectedItemView;
@property (strong, nonatomic) NSMutableArray *items;
@property (nonatomic, getter = isPicking) BOOL picking;
@property (strong, nonatomic) NSNumber *selectedItem;
@end
@implementation PickerTableViewController
- (id)init
{
self = [super init];
if (self) {
// generate random cell contents
NSInteger countItems = 20;
self.items = [NSMutableArray arrayWithCapacity:countItems];
for (int i = 0; i < countItems; i++) {
[self.items addObject:@(arc4random() % 100)];
}
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.button.backgroundColor = [UIColor yellowColor];
[self.button setTitle:@"Done" forState:UIControlStateNormal];
[self.button addTarget:self action:@selector(stopPicking) forControlEvents:UIControlEventTouchUpInside];
self.button.enabled = self.isPicking;
[self.view addSubview:self.button];
// use a container for easy alignment of selected item view to center of table
_tableViewContainer = [[UIView alloc] init];
[self.view addSubview:_tableViewContainer];
_tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.tableViewContainer addSubview:self.tableView];
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
CGFloat const buttonHeight = 100.0f;
CGRect const buttonFrame = CGRectMake(0.0f, 0.0f, self.view.bounds.size.width, buttonHeight);
self.button.frame = buttonFrame;
CGRect tableFrame = self.view.bounds;
tableFrame.origin.y += buttonHeight;
tableFrame.size.height -= buttonHeight;
self.tableViewContainer.frame = tableFrame;
self.tableView.frame = self.tableViewContainer.bounds;
// allow table to scroll to first and last row
CGFloat selectedItemViewY = self.selectedItemView.center.y;
self.tableView.contentInset = UIEdgeInsetsMake(selectedItemViewY, 0.0f, selectedItemViewY, 0.0f);
}
#pragma mark - Custom
- (SelectedItemView *)selectedItemView
{
if (!_selectedItemView) {
CGRect frame = CGRectMake(0.0f, 0.0f, self.tableView.bounds.size.width, self.tableView.rowHeight);
_selectedItemView = [[SelectedItemView alloc] initWithFrame:frame];
_selectedItemView.center = CGPointMake(self.tableView.bounds.size.width * 0.5f, self.tableView.bounds.size.height * 0.5f);
_selectedItemView.hidden = YES;
[self.tableViewContainer addSubview:_selectedItemView];
}
return _selectedItemView;
}
- (void)startPickingForItemAtIndex:(NSInteger)index
{
if (self.isPicking) {
return;
}
self.picking = YES;
// update tableview
self.selectedItem = [self.items objectAtIndex:index];
[self.items removeObjectAtIndex:index];
[self.tableView reloadData];
[self repositionCells];
// update views
self.selectedItemView.label.text = [NSString stringWithFormat:@"%@", self.selectedItem];
self.selectedItemView.hidden = NO;
self.button.enabled = YES;
}
- (void)stopPicking
{
if (!self.isPicking) {
return;
}
self.picking = NO;
// calculate new index for item
NSSortDescriptor *sd = [NSSortDescriptor sortDescriptorWithKey:@"row" ascending:YES];
NSArray *sds = [NSArray arrayWithObject:sd];
NSArray *indexPaths = [[self.tableView indexPathsForVisibleRows] sortedArrayUsingDescriptors:sds];
NSInteger selectedItemIndex = NSNotFound;
for (NSIndexPath *indexPath in indexPaths) {
if ([self isCellAtIndexPathBelowSelectedItemView:indexPath]) {
selectedItemIndex = indexPath.row;
break;
}
}
if (selectedItemIndex == NSNotFound) {
selectedItemIndex = self.items.count;
}
// update tableview
[self.items insertObject:self.selectedItem atIndex:selectedItemIndex];
self.selectedItem = nil;
[self.tableView reloadData];
// update views
self.selectedItemView.hidden = YES;
self.button.enabled = NO;
}
- (BOOL)isCellAtIndexPathBelowSelectedItemView:(NSIndexPath *)indexPath
{
CGFloat yInTable = indexPath.row * self.tableView.rowHeight;
CGFloat yInContainer = yInTable - self.tableView.contentOffset.y;
return yInContainer > self.selectedItemView.frame.origin.y;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
cell.textLabel.text = [NSString stringWithFormat:@"%@", [self.items objectAtIndex:indexPath.row]];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.isPicking) {
[self stopPicking];
}
// scroll position seems to be confused... UITableViewScrollPositionMiddle doesn't work?
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
[self startPickingForItemAtIndex:indexPath.row];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (self.isPicking) {
[self repositionCells];
}
}
- (void)repositionCells
{
CGFloat tableOffset = self.tableView.contentOffset.y;
NSArray *indexPaths = [self.tableView indexPathsForVisibleRows];
CGFloat selectedItemViewY = self.selectedItemView.frame.origin.y;
CGFloat const bufferHeight = self.tableView.rowHeight; // adjust to liking
for (NSIndexPath *indexPath in indexPaths) {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
CGRect cellFrame = cell.frame;
CGFloat cellYInTable = indexPath.row * self.tableView.rowHeight;
cellFrame.origin.y = cellYInTable;
CGFloat cellYInContainer = cellYInTable - tableOffset;
if (cellYInContainer <= selectedItemViewY) {
cellFrame.origin.y -= bufferHeight;
} else {
cellFrame.origin.y += bufferHeight;
}
cell.frame = cellFrame;
}
}
@end
@interface SelectedItemView ()
@property (strong, nonatomic) UILabel *label;
@end
@implementation SelectedItemView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.userInteractionEnabled = NO;
self.backgroundColor = [UIColor blueColor];
_label = [[UILabel alloc] init];
_label.backgroundColor = [UIColor clearColor];
[self addSubview:_label];
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.label.frame = self.bounds;
}
@end
when u select a cell in edit mode. Do these:
renderInContext
to cut out an image of the moved cell.Cheers have fun.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With