【问题标题】:iOS - Allow default row movement in `UITableView` without editing modeiOS - 允许在不编辑模式的情况下在“UITableView”中移动默认行
【发布时间】:2014-04-03 21:59:59
【问题描述】:

我希望允许UITableView 中的默认行移动,而不使其处于编辑模式,并且不影响UITableView 的默认行为。

上图显示了一个处于编辑模式的单元格,已启用移动。

我尝试简单地运行 for (UIView *subview in cell.subviews)(而我的 UITableView 处于编辑模式),但按钮没有出现:

<UITableViewCellScrollView: 0x8cabd80; frame = (0 0; 320 44); autoresize = W+H; gestureRecognizers = <NSArray: 0x8c9ba20>; layer = <CALayer: 0x8ca14b0>; contentOffset: {0, 0}>

如何在我的UITableView 不启用编辑模式的情况下允许/添加移动“按钮”?

创建和添加UIButton 并使用默认function 进行移动也是一种选择。

【问题讨论】:

  • 不妨试试最近这篇文章的方法:raywenderlich.com/63089/…
  • 这可行,而且我知道这种方法是可行的。但我的问题是如何使用单元格移动的默认代码启用它。
  • @AleksanderAzizi 请问你为什么害怕编辑模式?
  • 但是为什么不首先以编辑模式呈现表格视图呢?我会告诉你我为什么建议这个。如果不接触私有 API,就无法使用 Apple 的重新排序方法。您必须自己实现或使用开源(有可用的)。
  • 使用 100% tableview 委托方法回答。我在实时应用中使用此代码。

标签: ios uitableview default


【解决方案1】:

我实际上为我的一个应用程序做了类似的事情。它使用委托方法进行表格编辑和一些“欺骗”用户。 100% 内置 Apple 功能。

1 - 将表格设置为编辑(我在 viewWillAppear 中进行)

-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [self.tableView setEditing:YES];
}

2 - 隐藏默认附件图标:

-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView 
        editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
        //remove any editing accessories (AKA) delete button 
        return UITableViewCellAccessoryNone;
       }

3 - 保持编辑模式不将所有内容向右移动(在单元格中)

 -(BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath{
return NO;
}

4 - 此时,您应该能够拖动单元格,而不会看起来像是处于编辑模式。在这里我们欺骗用户。创建您自己的“移动”图标(默认情况下为三行,在您的情况下使用任何您想要的图标)并将 imageView 添加到它通常在单元格上的位置。

5 - 最后,实现委托方法以实际重新排列底层数据源。

-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{

    //Get original id
    NSMutableArray *originalArray = [self.model.items objectAtIndex:sourceIndexPath.section];
    Item * original = [originalArray objectAtIndex:sourceIndexPath.row];

    //Get destination id
    NSMutableArray *destinationArray = [self.model.items objectAtIndex:destinationIndexPath.section];
    Item * destination = [destinationArray objectAtIndex:destinationIndexPath.row];

    CGPoint temp = CGPointMake([original.section intValue], [original.row intValue]);

    original.row = destination.row;
    original.section = destination.section;

    destination.section = @(temp.x);
    destination.row = @(temp.y);

    //Put destination value in original array
    [originalArray replaceObjectAtIndex:sourceIndexPath.row withObject:destination];

    //put original value in destination array
    [destinationArray replaceObjectAtIndex:destinationIndexPath.row withObject:original];

    //reload tableview smoothly to reflect changes
    dispatch_async(dispatch_get_main_queue(), ^{
        [UIView transitionWithView:tableView duration:duration options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
            [tableView reloadData];
        } completion:NULL];
    });
 }

【讨论】:

  • 我认为这也可以,我有完全相同的代码,但是当UITableView 处于编辑模式时,默认滑动(向左)删除被禁用。 Ergo 关于如何在不使用编辑模式的情况下启用行移动的问题。
  • 如果您需要向左滑动,请使用不同的实现启用它。在购物清单下查看我的应用程序。这听起来正是您正在寻找的。 appstore.com/mealidea
  • 我这里的滑动实现是基本的,但是如果你在单元格中嵌入一个滚动视图,你可以获得与苹果相同的效果
  • 如果不将表格置于编辑模式,您将无法对其进行编辑!缺少手势识别器、屏幕截图和手动移动每个单元格,这是尽可能接近
  • 另外,您的问题仅询问默认行移动。它没有提到滑动删除...
【解决方案2】:

William Falcon 在 swift 3

中的回答

1 - 将表格设置为编辑(我在 viewWillAppear 中进行)

override func viewWillAppear(_ animated: Bool) {
   super.viewWillAppear(animated: animated)
   tableView.setEditing(true, animated: false)
}

2 - 隐藏默认附件图标:

override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
    return .none
}

3 - 保持编辑模式不将所有内容向右移动(在单元格中)

override func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
    return false
}

4 - 在 swift 3 中不需要

5 - 重新排列你的数组

补充说明

如果您想让表格单元格可选,请在viewWillAppear() 函数中添加以下代码。

tableView.allowsSelectionDuringEditing = true

【讨论】:

    【解决方案3】:

    对于 Swift 5... 我知道这个问题不需要编辑,但如果您唯一需要的功能是

    1. 能够对单元格重新排序并
    2. 能够选择单元格

    然后你可以关注这个(重新排序的来源:https://www.ralfebert.de/ios-examples/uikit/uitableviewcontroller/reorderable-cells/):

    1. 设置tableView.isEditing = true
    2. 设置tableView.allowsSelectionDuringEditing = true
    3. 使用UITableViewDataSource,添加以下方法:
    func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
        return .none
    }
    
    func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
        return false
    }
    

    您可能需要也可能不需要覆盖这些函数。我不需要。

    1. (我认为)在 UITableViewDelegate 中添加:
    override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        let movedObject = items[sourceIndexPath.row]
        items.remove(at: sourceIndexPath.row)
        items.insert(movedObject, at: destinationIndexPath.row)
    }
    

    其中 items 是处理表格视图中项目数的数据源数组。

    关键是第2步,最后可以在其中添加func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)方法。

    【讨论】:

      【解决方案4】:

      如果您不想将UITableView 设置为编辑模式,那么您将需要重新发明拖动单元格的功能。

      我提出了一个相当完整的解决方案,使用户能够在UITableView 的可见区域内移动行。无论UITableView 是否处于编辑模式,它都有效。如果您希望在将行拖动到可见区域的顶部或底部附近时滚动表格,则需要扩展它。您可能还需要找出一些失败和边缘情况。

      @implementation TSTableViewController
      {
          NSMutableArray* _dataSource;
      }
      
      - (void) viewDidLoad
      {
          [super viewDidLoad];
      
          _dataSource = [NSMutableArray new];
          for ( int i = 0 ; i < 10 ; i++ )
          {
              [_dataSource addObject: [NSString stringWithFormat: @"cell %d", i]];
          }
      }
      
      - (void) longPress: (UILongPressGestureRecognizer*) lpgr
      {
          static NSString* dragCellData = nil;
          static UIView*   dragCellView = nil;
          static NSInteger dragCellOffset = 0;
      
          // determine the cell we're hovering over, etc:
          CGPoint pt = [lpgr locationInView: self.tableView];
          NSIndexPath* ip = [self.tableView indexPathForRowAtPoint: pt];
          UITableViewCell* cell = [self.tableView cellForRowAtIndexPath: ip];
          CGPoint ptInCell = [lpgr locationInView: cell];
      
          // where the current placeholder cell is, if any:
          NSInteger placeholderIndex = [_dataSource indexOfObject: @"placeholder"];
      
          switch ( lpgr.state )
          {
              case UIGestureRecognizerStateBegan:
              {
                  // get a snapshot-view of the cell we're going to drag:
                  cell.selected = cell.highlighted = NO;
                  dragCellView = [cell snapshotViewAfterScreenUpdates: YES];
                  dragCellView.clipsToBounds       = NO;
                  dragCellView.layer.shadowRadius  = 10;
                  dragCellView.layer.shadowColor   = [UIColor blackColor].CGColor;
                  dragCellView.layer.masksToBounds = NO;
                  dragCellView.frame = [cell convertRect: cell.bounds
                                              toView: self.tableView.window];
      
                  // used to position the dragCellView nicely:
                  dragCellOffset = ptInCell.y;
      
                  // the cell will be removed from the view hierarchy by the tableview, so transfer the gesture recognizer to our drag view, and add it into the view hierarchy:
                  [dragCellView addGestureRecognizer: lpgr];
                  [self.tableView.window addSubview: dragCellView];
      
      
                  // swap out the cell for a placeholder:
                  dragCellData = _dataSource[ip.row];
                  _dataSource[ip.row] = @"placeholder";
      
                  [self.tableView reloadRowsAtIndexPaths: @[ip]
                                        withRowAnimation: UITableViewRowAnimationNone];
      
                  break;
              }
      
              case UIGestureRecognizerStateChanged:
              {
                  // where should we move the placeholder to?
                  NSInteger insertIndex = ptInCell.y < cell.bounds.size.height / 2.0 ? ip.row : ip.row + 1;
                  if ( insertIndex != placeholderIndex )
                  {
                      // remove from the datasource and the tableview:
                      [_dataSource removeObjectAtIndex: placeholderIndex];
                      [self.tableView deleteRowsAtIndexPaths: @[ [NSIndexPath indexPathForRow: placeholderIndex inSection: 0] ]
                                            withRowAnimation: UITableViewRowAnimationFade];
                      // adjust:
                      if ( placeholderIndex < insertIndex )
                      {
                          insertIndex--;
                      }
      
                      // insert to the datasource and tableview:
                      [_dataSource insertObject: @"placeholder"
                                        atIndex: insertIndex];
                      [self.tableView insertRowsAtIndexPaths: @[ [NSIndexPath indexPathForRow: insertIndex inSection: 0] ]
                                            withRowAnimation: UITableViewRowAnimationFade];
                  }
      
                  // move our dragCellView
                  CGRect f = dragCellView.frame;
                  f.origin.y = pt.y - dragCellOffset;
                  dragCellView.frame = f;
      
                  break;
              }
      
              case UIGestureRecognizerStateEnded:
              {
                  // replace the placeholdercell with the cell we were dragging
                  [_dataSource replaceObjectAtIndex: placeholderIndex
                                         withObject: dragCellData];
                  [self.tableView reloadRowsAtIndexPaths: @[ [NSIndexPath indexPathForRow: placeholderIndex inSection: 0] ]
                                        withRowAnimation: UITableViewRowAnimationFade];
      
                  // reset state
                  [dragCellView removeFromSuperview];
                  dragCellView = nil;
                  dragCellData = nil;
      
                  break;
              }
      
              default:
              {
                  break;
              }
          }
      }
      
      #pragma mark - Table view data source
      
      - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
      {
          return 1;
      }
      
      - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
      {
          return _dataSource.count;
      }
      
      - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
      {
          NSString* cellData = _dataSource[indexPath.row];
          if ( [cellData isEqualToString: @"placeholder" ] )
          {
              // an empty cell to denote where the "drop" would go
              return [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
                                            reuseIdentifier: nil];
          }
      
          // a cell...
          UITableViewCell* cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
                                                         reuseIdentifier: nil];
      
          // our "fake" move handle & gesture recognizer
      
          UILongPressGestureRecognizer* lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget: self action: @selector( longPress:) ];
          lpgr.minimumPressDuration = 0.3;
      
          UILabel* dragLabelView = [UILabel new];
          dragLabelView.text = @"☰";
          dragLabelView.userInteractionEnabled = YES;
          [dragLabelView addGestureRecognizer: lpgr];
          [dragLabelView sizeToFit];
      
          cell.textLabel.text = cellData;
      
          if ( tableView.isEditing )
          {
              cell.editingAccessoryView = dragLabelView;
          }
          else
          {
              cell.accessoryView = dragLabelView;
          }
      
          return cell;
      }
      
      @end
      

      【讨论】:

        【解决方案5】:

        要使单元格移动,您必须实现相应的&lt;UITableViewDelegate&gt; 移动方法并明确允许单元格(索引路径)移动。移动指示器图标不会显示,因为它取决于编辑模式。当 tableview 处于编辑状态时,move-icon 将与 accessoryType 图标重叠。编辑模式默认带有左侧的圆形删除按钮,您可以在编辑时将其隐藏。

        - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
            if (self.editing) return UITableViewCellEditingStyleNone;
            return UITableViewCellEditingStyleDelete;
        }
        

        这样你就可以让你的 UITableView 类和 UITableViewDelegate 方法清晰易懂,你不需要实现某种自制的移动模式。 您可以使用 tableview 的编辑属性来区分单元格在 tableView:cellForRowAtIndexPath: 方法中的外观。因此,即使通过将 tableview 设置回editing = NO 来关闭移动功能也更容易,移动图标会消失,而附件类型符号会再次出现。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-11-19
          • 2022-12-12
          • 2015-12-23
          • 2021-12-11
          • 1970-01-01
          相关资源
          最近更新 更多