【问题标题】:Collectionview interactive cell swappingCollectionview 交互式单元格交换
【发布时间】:2017-01-02 23:59:33
【问题描述】:

我已经实现了一个集合视图,如here 所述。如您所见,它使用了 iOS 9 中提供的 collectionview 单元格的交互式重新排序。但问题是,我无法控制单元格的重新排序。

说细胞是这样的 -

1  2  3  4
5  6  7  8
9  10 11 12

我只想交换单元格 6 和 4。所以重新排序后单元格将是

1  2  3  6
5  4  7  8
9  10 11 12

这里,在教程中,在程序开始的时候,collection view是这样的——

如果我把 Starbuck 放在 Rose Tyler 上面,就会发生这种情况 -

请注意,Sarah Connor 的位置是 Starbuck

我想控制单元格的重新排序,以便在此处交换 Rose TylerStarbuck 的位置。

我该怎么做?

【问题讨论】:

    标签: swift uicollectionview ios9


    【解决方案1】:

    听起来好像您只是在移动一个物体,或者在您的情况下是星巴克。

    当您这样做时,列表将自行重新排序;基于此,Rose Tyler 将在逻辑上处于您所展示的位置。

    如果您将其视为在主题公园中排队等候骑行,那么您所表现出的行为非常合理。 Sam、Dana 和 Rose 排在第 2 位和第 3 位,Sarah、Starbuck 和 River 排在第 4、5 和第 6 位。如果 Rose 然后让星巴克排在她前面,那么 Rose 排在第四位,而 Sarah 现在排在第五位。

    要获得您想要的东西,您需要保留 Rose 在列表中的位置以及 Starbuck 在列表中的位置的参考。然后,您需要逐个移动需要移动的每个人,而不是像现在这样移动星巴克。

    【讨论】:

    • 是的,如果您考虑队列,这是合乎逻辑的。但是如果我想交换他们的位置,那也应该是可能的。想想,他们正在换地方。问题是我不知道集合视图如何重新排序其单元格。我需要知道在移动单元格时主动重新排序单元格的方法。我找不到。
    • 你知道怎么做吗?
    【解决方案2】:

    为了重新排序集合视图项目,我们需要在集合视图上添加长按手势。为了使用特定的 indexPath 重新排序集合视图项,UICollectionViewDelegate 提供了“targetIndexPathForMoveFromItemAt”方法。 为了获得完整的教程,您可以按照Reordering CollectionView Item

    【讨论】:

    • 对不起,我不想这样,我只是源和目标位置交换只是不想重新排序collectionview
    • 好的,我了解您的问题。但我认为对于这个问题没有任何直接的解决方案,您可以在特定位置使用“targetIndexPathForMoveFromItemAt”方法移动集合视图项。并更新你的数据数组,最后像这样重新加载集合视图。
    • func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { arrayCellTitle.swapAt(sourceIndexPath.item, destinationIndexPath.item) self.collectionView.reloadData() }
    • 但我不想要交互式动画我也只需要简单的动画
    • 我们可以禁用交互式动画吗
    【解决方案3】:

    我会按照以下步骤进行...

    第一步创建这样的模型

    class CrewMembersModel {
        var name: String?
        var position: Int?
    
        fileprivate init (name: String, position: Int) {
            self.name = name
            self.position = position
        }
    
        static var crewMembersDataList: [CrewMembersModel]? = nil
    
        class func getMembersList() -> [CrewMembersModel] {
            if (self.crewMembersDataList == nil) {
                self.crewMembersDataList = [CrewMembersModel]()
                self.crewMembersDataList?.append(CrewMembersModel(name: "Sam Carter", position: 0))
                self.crewMembersDataList?.append(CrewMembersModel(name: "Dana Scully", position: 1))
                self.crewMembersDataList?.append(CrewMembersModel(name: "Rose Taylor", position: 2))
                self.crewMembersDataList?.append(CrewMembersModel(name: "Sarah Conor", position: 3))
                self.crewMembersDataList?.append(CrewMembersModel(name: "Starbuck", position: 4))
                self.crewMembersDataList?.append(CrewMembersModel(name: "River Tam", position: 5))
                self.crewMembersDataList?.append(CrewMembersModel(name: "Sarah Jane", position: 6))
    
            }
            return crewMembersDataList!.sorted(by: { $0.position! < $1.position!})
        }
    }
    

    请注意 在这里您可以添加模型的其他属性,例如图像等...

    第二步ViewControllerviewDidLoad方法中获取模型列表

    self.crewMembersDataList = CrewMembersModel.getMembersList()
    

    第三步func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -&gt; UICollectionViewCell解析模型数据

    let crewMembersData: CrewMembersModel = self.crewMembersDataList[indexPath.row - 1]
    cell.name = crewMembersData.name!
    

    最后在第 4 步中,每当您进行更改时,更改所选单元格的位置并调用 collectionView.reloadData()

    func changePositions(selectedModelOne: CrewMembersModel, selectedModelTwo: CrewMembersModel) {
        let tmpPosition = selectedModelOne.position!
        selectedModelOne.position = selectedModelTwo.position!
        selectedModelTwo.position = tmpPosition
    
        self.crewMembersDataList.sorted(by: { $0.position! < $1.position!})
        self.collectionView.reloadData()
    }
    

    请注意我没有检查整个代码,可能是一些语法问题

    【讨论】:

      【解决方案4】:

      简单的解决方案是实现您自己的交互式单元格排序行为,只需沿着屏幕中的平移移动单元格并在手势结束于另一个单元格的位置时交换(使用您自己的动画和数据源更新)。这是一个工作示例:

      class ViewController: UIViewController, UICollectionViewDataSource {
      
          var arr: [String] = (0...100).map { return "\($0)" }
      
          lazy var collectionView: UICollectionView =  {
              let layout = UICollectionViewFlowLayout()
              let cv: UICollectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
              cv.register(Cell.self, forCellWithReuseIdentifier: Cell.id)
              layout.itemSize = CGSize(width: view.bounds.width/3.5, height: 100)
              cv.dataSource = self
              cv.addGestureRecognizer(longPressGesture)
              return cv
          }()
      
          lazy var longPressGesture: UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.handleLongGesture(gesture:)))
      
          private var movingCell: MovingCell?
      
          override func viewDidLoad() {
              super.viewDidLoad()
              view = collectionView
          }
      
          @objc func handleLongGesture(gesture: UILongPressGestureRecognizer) {
      
              var cell: (UICollectionViewCell?, IndexPath?) {
                  guard let indexPath = collectionView.indexPathForItem(at: gesture.location(in: collectionView)),
                      let cell = collectionView.cellForItem(at: indexPath) else { return (nil, nil) }
                  return (cell, indexPath)
              }
      
              switch(gesture.state) {
      
              case .began:
                  movingCell = MovingCell(cell: cell.0, originalLocation: cell.0?.center, indexPath: cell.1)
                  break
              case .changed:
      
                  /// Make sure moving cell floats above its siblings.
                  movingCell?.cell.layer.zPosition = 100
                  movingCell?.cell.center = gesture.location(in: gesture.view!)
      
                  break
              case .ended:
      
                  swapMovingCellWith(cell: cell.0, at: cell.1)
                  movingCell = nil
              default:
                  movingCell?.reset()
                  movingCell = nil
              }
          }
      
          func swapMovingCellWith(cell: UICollectionViewCell?, at indexPath: IndexPath?) {
              guard let cell = cell, let moving = movingCell else {
                  movingCell?.reset()
                  return
              }
      
              // update data source
              arr.swapAt(moving.indexPath.row, indexPath!.row)
      
              // swap cells
              animate(moving: moving.cell, to: cell)
          }
      
          func animate(moving movingCell: UICollectionViewCell, to cell: UICollectionViewCell) {
              longPressGesture.isEnabled = false
      
              UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.1, initialSpringVelocity: 0.7, options: UIViewAnimationOptions.allowUserInteraction, animations: {
                  movingCell.center = cell.center
                  cell.center = movingCell.center
              }) { _ in
                  self.collectionView.reloadData()
                  self.longPressGesture.isEnabled = true
              }
          }
      
          func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
              return arr.count
          }
      
          func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
              let cell: Cell = collectionView.dequeueReusableCell(withReuseIdentifier: Cell.id, for: indexPath) as! Cell
              cell.titleLable.text = arr[indexPath.row]
              return cell
          }
      
          private struct MovingCell {
              let cell: UICollectionViewCell
              let originalLocation: CGPoint
              let indexPath: IndexPath
      
              init?(cell: UICollectionViewCell?, originalLocation: CGPoint?, indexPath: IndexPath?) {
                  guard cell != nil, originalLocation != nil, indexPath != nil else { return nil }
                  self.cell = cell!
                  self.originalLocation = originalLocation!
                  self.indexPath = indexPath!
              }
      
              func reset() {
                  cell.center = originalLocation
              }
          }
      
          final class Cell: UICollectionViewCell {
              static let id: String = "CellId"
      
              lazy var titleLable: UILabel = UILabel(frame: CGRect(x: 0, y: 20, width: self.bounds.width, height: 30))
      
              override init(frame: CGRect) {
                  super.init(frame: frame)
                  addSubview(titleLable)
                  titleLable.backgroundColor = .green
                  backgroundColor = .white
              }
      
              required init?(coder aDecoder: NSCoder) {
                  super.init(coder: aDecoder)
              }
          }
      }
      

      【讨论】:

      • 但在我的情况下没有固定布局
      • 这应该不是问题。如果单元格大小是根据 indexPath 计算的,例如假设偶数行索引的高度是奇数行的两倍,这些项目将根据它们的新 indexPath 调整大小。如果尺寸取决于型号,则无论移动到何处,该物品都将保持其尺寸。这是合乎逻辑的行为,但如果你想要别的东西,那就是另一个问题:) 请随意解释,我会看看。
      • 我想要总共 5 列 3 行的集合视图,在这个所有单元格高度等于和第 1、第 2、第 4 和第 5 列宽度的宽度与方形格式中的高度相同,并且第 3 列单元格从不替换为任何其他的
      • 让我试试发生了什么!
      • 对不起,你能给我一分钟吗
      【解决方案5】:
      UICollectionView *collection;
      MSMutableArray *datasource;
      NSIndexPath *from = [NSIndexPath indexPathForItem:0 inSection:0];
      NSIndexPath *to = [NSIndexPath indexPathForItem:1 inSection:0];
      
      // swap animated
      [collection performBatchUpdates:^{
          // To avoid problems, you should update your data model inside the updates block 
          [datasource exchangeObjectAtIndex:from.item withObjectAtIndex:to.item];
          // for swap need two move operations
          [collection moveItemAtIndexPath:from toIndexPath:to];
          [collection moveItemAtIndexPath:to toIndexPath:from];
          // no need to update the moved cells because the content has not changed
          //[collection reloadSections:[NSIndexSet indexSetWithIndex:0]];
      } completion:^(BOOL finished) {
      
      }];
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-09
        • 1970-01-01
        • 2015-12-18
        • 2021-09-23
        • 2020-08-17
        相关资源
        最近更新 更多