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

标签 ios iphone objective-c uitableview

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

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

更新1

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

进一步说明

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

表格 View

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

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

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

Tableview 编辑模式

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

当您滚动表格 View 时,未选中的单元格围绕所选单元格流动,而所选单元格停留在中间。

最佳答案

如果所选单元格不是真正的单元格而是一个单独的 UIView(可以使它看起来像一个单元格),是否可以接受?如果是这样,您可以这样做:

  1. 当点击一个单元格时,将其从表格中移除并在表格上方显示类似单元格的 UIView
  2. 通过响应 -scrollViewDidScroll: 在滚动时重新定位单元格。
  3. 点击“完成”按钮后,将项目重新插入表格并隐藏类似单元格的 UIView

为了查看实际效果,我创建了一个 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

关于ios - 如何使用类似选择器的自定义编辑模式创建 UITableView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20751064/

相关文章:

ios - 如何在模态视图 Controller 之间转换并删除隐藏的 View Controller ?

iphone - 使用 AVFoundation 播放视频时遇到问题

objective-c - 你能为 UITableView 单元格插入做自定义动画吗?

php - 从本地主机 :8888 in objective c using json 获取数据时出现问题

ios - CGRect 图像调整大小

iphone - iOS - 如何实现带有多个参数和 afterDelay 的 performSelector?

iphone - UIAppFonts自动加载字体?

iphone - ARKit 是否公开了一种显示地平面的自动方式,还是应该将其作为对象添加到某些坐标

objective-c - 有没有办法以编程方式突出显示状态栏项目?

ios - 来自 URL 的 PDF 在 WKWebView 中无法正确显示?