如何使用类似自定义选择器的编辑模式创建UITableView

时间:2013-12-23 20:56:55

标签: ios iphone objective-c uitableview

在常规UITableView编辑模式下,将单元格拖动到您希望单元格所在的位置,其他单元格可以弹出到位。我想创建一个UITableView编辑模式,您可以在其中选择一个单元格,当您滚动桌面视图以移动所选项目时,它将保持在中心,将所选项目保持在中心,表格单元格围绕中心移动选定的细胞。

有效的'赏金值'答案将需要一个最低限度的工作示例,将选定的单元格保存在表格的中心,并可以通过向上和向下滑动表格来移动。包括表中第一个和最后一个位置的边缘情况。或者,您可以概述您认为可行的关键点,如果它们引导我朝着正确的方向发展,那么您将获得赏金。

更新1

我在GitHub上建立了一个名为PickerTableView的项目。在开发分支上工作。选择正在进行,我正在研究子类化TableView来处理滚动单元格的移动。在我之前找到一个有效的解决方案仍然可以获得赏金。

进一步澄清

根据评论,我将提供一些ASCII艺术。

TableView

|==========|
|      Next|
|==========|
|          |
|----------|
|          |
|----------|
|          |
|----------|
|          |
|----------|
|          |
|==========|

选择一个单元格,然后点击下一步

|==========|
|      Next|
|==========|
|          |
|----------|
|          |
|----------|
|         X|
|----------|
|          |
|----------|
|          |
|==========|

Tableview编辑模式

|=============|
|         Done|
|=============|
|             |
|-------------|
|             |
|-------------|
| This cell is|
| Highlighted |
| and locked  |
| in place    |
|-------------|
|             |
|-------------|   
|             |
|=============|

滚动tableview时,未选中的单元格会在所选单元格周围流动,而所选单元格位于中间位置。

4 个答案:

答案 0 :(得分:2)

如果选定的单元格不是真正的单元格而是单独的UIView(可以看起来像单元格),是否可以接受?如果是这样,你可以这样做:

  1. 当点击一个单元格时,将其从表格中移除并在表格上显示类似单元格的UIView
  2. 通过回复-scrollViewDidScroll:来重新定位单元格。
  3. 点击完成按钮后,将项目重新插入表格并隐藏类似单元格的UIView
  4. 为了看到它的实际效果,我已经创建了一个UIViewController子类供您测试:

    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
    

答案 1 :(得分:2)

当你在编辑模式中选择一个单元格时。做这些:

  1. 将单元格滚动到中心位置。
  2. 使用renderInContext剪切移动的单元格的图像。
  3. 将图像放入imageView中,并将其添加到tableView的superview中。根据中心位置,相对于tableView的superView。
  4. 之后你可以自由地围着桌子滚动。只需预先删除选定的单元格,以避免在UI中出现重复。
  5. 按下接下来。使用所需数据在所需位置插入一行,然后删除添加的ImageView。
  6. 所涉及的动画完全由我自行决定:D
  7. 干杯很开心。

答案 2 :(得分:0)

由于我的答案是最好的,所以我会提供自己的答案。

我在GitHub上建立了一个名为PickerTableView的项目。在开发分支上工作。该项目使用Cocoapods作为依赖项。选择正在运行,我已经将TableView子类化为处理滚动单元格的移动。

答案 3 :(得分:0)

这是一个做你想做的事情的例子(也就是经过一些调整之后)。我认为它的代码非常简单:

https://github.com/nielsbot/funny-tables

(对于您的使用案例,当您进入编辑模式时,您会显示centerTableViewbottomTableView。)