2016-09-25 10 views
9

すべてのセルのカスタムサイズでコレクションビューのセルを並べ替える必要があります。
コレクションビューのすべてのセルには、1語のラベルがあります。
私はこのコードですべてのセルの大きさを設定します。私はこのコードでコレクションビューを並べ替えるコレクションビューの再現性の問題カスタムディメンションのセルを表示

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 

    let word = textArray[indexPath.row] 

    let font = UIFont.systemFont(ofSize: 17) 
    let fontAttributes = [NSFontAttributeName: font] 
    var size = (word as NSString).size(attributes: fontAttributes) 
    size.width = size.width + 2 
    return size 
} 

override func viewDidLoad() { 
    super.viewDidLoad() 

    self.installsStandardGestureForInteractiveMovement = false 
    let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(gesture:))) 
    self.collectionView?.addGestureRecognizer(panGesture) 

} 

func handlePanGesture(gesture: UIPanGestureRecognizer) { 
    switch gesture.state { 
    case UIGestureRecognizerState.began : 
     guard let selectedIndexPath = self.collectionView?.indexPathForItem(at: gesture.location(in: self.collectionView)) else { 
      break 
     } 
     collectionView?.beginInteractiveMovementForItem(at: selectedIndexPath) 
     print("Interactive movement began") 

    case UIGestureRecognizerState.changed : 
     collectionView?.updateInteractiveMovementTargetPosition(gesture.location(in: gesture.view!)) 
     print("Interactive movement changed") 

    case UIGestureRecognizerState.ended : 
     collectionView?.endInteractiveMovement() 
     print("Interactive movement ended") 

    default: 
     collectionView?.cancelInteractiveMovement() 
     print("Interactive movement canceled") 
    } 
} 

override func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { 

    // Swap values if sorce and destination 
    let change = textArray[sourceIndexPath.row] 


    textArray.remove(at: sourceIndexPath.row) 
    textArray.insert(change, at: destinationIndexPath.row) 

    // Reload data to recalculate dimensions for the cells 
    collectionView.reloadData() 
} 

ビューは次のようになります。 collection view

問題並べ替え中に、セルはindexPathで元のセルのサイズを維持するため、並べ替えの間、ビューは次のようになります。 reordering 現時点では、正しい順序を再計算するために、並べ替えの最後にデータを再読み込みする問題を修正しました。 インタラクティブな移動やカスタムサイズのセルの並べ替えの際にも、セルの正しい寸法をどうやって判断できますか?

+0

申し訳ありませんが、私は – ale00

答えて

3

これは一週間中私に迷惑をかけていましたので、私はこの夕方に解決策を試してみました。私はあなたが必要と思うのは、注文が変更されたときに各セルのレイアウトを動的に調整できるコレクションビューのカスタムレイアウトマネージャです。

次のコードは明らかにあなたのレイアウトよりもはるかに荒いものを作り出しますが、基本的には、あなたが望む動作を達成します。セルが並べ替えられたときに新しいレイアウトに決定的に移動すると、

すべてのキーは、ビューコントローラのsourceData変数のdidSet関数です。この配列の値が変更されると(並べ替えボタンを押すことでジェスチャ認識子に近似しています)、必要なセル寸法の再計算が自動的にトリガされ、レイアウトがトリガーされて再計算され、データ。

ご不明な点がありましたら、お知らせください。それが役に立てば幸い!

更新:ありがとう、私はあなたが今何をしようとしているのか理解しており、添付の更新コードがあなたに届くと思います。組み込みのインタラクションメソッドを使用するのではなく、カスタムレイアウトマネージャを実装して委譲を使用する方が簡単だと思います。パンジェスチャレコグナイザがセルを選択すると、その単語に基づいてサブビューが作成されますジェスチャー。バックグラウンドで同時にデータソースから単語を削除し、レイアウトを更新します。ユーザーが単語を配置する場所を選択すると、そのプロセスを逆にして、デリゲートに単語をデータソースに挿入してレイアウトを更新するように指示します。ユーザーがコレクションビューの外にある単語または有効でない場所に単語をドラッグすると、その単語は開始位置に戻されます(オリジナルのインデックスをラベルのタグとして保存する狡猾なテクニックを使用します)。

あなたを助ける希望!

私は言語とサイムミスを犯してしまった場合、[ウィキペディアのテキスト礼儀]

import UIKit 

class ViewController: UIViewController, bespokeCollectionViewControllerDelegate { 

    let sourceText : String = "So Midas, king of Lydia, swelled at first with pride when he found he could transform everything he touched to gold; but when he beheld his food grow rigid and his drink harden into golden ice then he understood that this gift was a bane and in his loathing for gold, cursed his prayer" 

    var sourceData : [String]! { 
     didSet { 
      refresh() 
     } 
    } 
    var sortedCVController : UICollectionViewController! 
    var sortedLayout : bespokeCollectionViewLayout! 
    var sortButton : UIButton! 
    var sortDirection : Int = 0 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     // Do any additional setup after loading the view, typically from a nib. 

     sortedLayout = bespokeCollectionViewLayout(contentWidth: view.frame.width - 200) 
     sourceData = { 
      let components = sourceText.components(separatedBy: " ") 
      return components 
     }() 

     sortedCVController = bespokeCollectionViewController(sourceData: sourceData, collectionViewLayout: sortedLayout, frame: CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: view.frame.width - 200, height: view.frame.height - 200))) 
     (sortedCVController as! bespokeCollectionViewController).delegate = self 
     sortedCVController.collectionView!.frame = CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: view.frame.width - 200, height: view.frame.height - 200)) 

     sortButton = { 
      let sB : UIButton = UIButton(frame: CGRect(origin: CGPoint(x: 25, y: 100), size: CGSize(width: 50, height: 50))) 
      sB.setTitle("Sort", for: .normal) 
      sB.setTitleColor(UIColor.black, for: .normal) 
      sB.addTarget(self, action: #selector(sort), for: .touchUpInside) 
      sB.layer.borderColor = UIColor.black.cgColor 
      sB.layer.borderWidth = 1.0 
      return sB 
     }() 

     view.addSubview(sortedCVController.collectionView!) 
     view.addSubview(sortButton) 
    } 

    func refresh() -> Void { 
     let dimensions : [CGSize] = { 
      var d : [CGSize] = [CGSize]() 
      let font = UIFont.systemFont(ofSize: 17) 
      let fontAttributes = [NSFontAttributeName : font] 
      for item in sourceData { 
       let stringSize = ((item + " ") as NSString).size(attributes: fontAttributes) 
       d.append(CGSize(width: stringSize.width, height: stringSize.height)) 
      } 
      return d 
     }() 

     if self.sortedLayout != nil { 
      sortedLayout.dimensions = dimensions 
      if let _ = sortedCVController { 
       (sortedCVController as! bespokeCollectionViewController).sourceData = sourceData 
      } 
      self.sortedLayout.cache.removeAll() 
      self.sortedLayout.prepare() 
      if let _ = self.sortedCVController { 

       self.sortedCVController.collectionView?.reloadData() 
      } 
     } 
    } 


    func sort() -> Void { 
     sourceData = sortDirection > 0 ? sourceData.sorted(by: { $0 > $1 }) : sourceData.sorted(by: { $0 < $1 }) 
     sortDirection = sortDirection + 1 > 1 ? 0 : 1 
    } 

    func didMoveWord(atIndex: Int) { 
     sourceData.remove(at: atIndex) 
    } 

    func didPlaceWord(word: String, atIndex: Int) { 
     print(atIndex) 
     if atIndex >= sourceData.count { 
      sourceData.append(word) 
     } 
     else 
     { 
      sourceData.insert(word, at: atIndex) 
     } 

    } 

    func pleaseRefresh() { 
     refresh() 
    } 

} 

protocol bespokeCollectionViewControllerDelegate { 
    func didMoveWord(atIndex: Int) -> Void 
    func didPlaceWord(word: String, atIndex: Int) -> Void 
    func pleaseRefresh() -> Void 
} 

class bespokeCollectionViewController : UICollectionViewController { 

    var sourceData : [String] 
    var movingLabel : UILabel! 
    var initialOffset : CGPoint! 
    var delegate : bespokeCollectionViewControllerDelegate! 

    init(sourceData: [String], collectionViewLayout: bespokeCollectionViewLayout, frame: CGRect) { 
     self.sourceData = sourceData 
     super.init(collectionViewLayout: collectionViewLayout) 

     self.collectionView = UICollectionView(frame: frame, collectionViewLayout: collectionViewLayout) 
     self.collectionView?.backgroundColor = UIColor.white 
     self.collectionView?.layer.borderColor = UIColor.black.cgColor 
     self.collectionView?.layer.borderWidth = 1.0 

     self.installsStandardGestureForInteractiveMovement = false 

     let pangesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(gesture:))) 
     self.collectionView?.addGestureRecognizer(pangesture) 
    } 

    required init?(coder aDecoder: NSCoder) { 
     fatalError("init(coder:) has not been implemented") 
    } 

    func handlePanGesture(gesture: UIPanGestureRecognizer) { 
     guard let _ = delegate else { return } 

     switch gesture.state { 
     case UIGestureRecognizerState.began: 
      guard let selectedIndexPath = self.collectionView?.indexPathForItem(at: gesture.location(in: self.collectionView)) else { break } 
      guard let selectedCell : UICollectionViewCell = self.collectionView?.cellForItem(at: selectedIndexPath) else { break } 
      initialOffset = gesture.location(in: selectedCell) 

      let index : Int = { 
       var i : Int = 0 
       for sectionCount in 0..<selectedIndexPath.section { 
        i += (self.collectionView?.numberOfItems(inSection: sectionCount))! 
       } 
       i += selectedIndexPath.row 
       return i 
      }() 


      movingLabel = { 
       let mL : UILabel = UILabel() 
       mL.font = UIFont.systemFont(ofSize: 17) 
       mL.frame = selectedCell.frame 
       mL.textColor = UIColor.black 
       mL.text = sourceData[index] 
       mL.layer.borderColor = UIColor.black.cgColor 
       mL.layer.borderWidth = 1.0 
       mL.backgroundColor = UIColor.white 
       mL.tag = index 
       return mL 
      }() 

      self.collectionView?.addSubview(movingLabel) 

      delegate.didMoveWord(atIndex: index) 
     case UIGestureRecognizerState.changed: 
      if let _ = movingLabel { 
       movingLabel.frame.origin = CGPoint(x: gesture.location(in: self.collectionView).x - initialOffset.x, y: gesture.location(in: self.collectionView).y - initialOffset.y) 
      } 

     case UIGestureRecognizerState.ended: 
      print("Interactive movement ended") 
      if let selectedIndexPath = self.collectionView?.indexPathForItem(at: gesture.location(in: self.collectionView)) { 
       guard let _ = movingLabel else { return } 

       let index : Int = { 
        var i : Int = 0 
        for sectionCount in 0..<selectedIndexPath.section { 
         i += (self.collectionView?.numberOfItems(inSection: sectionCount))! 
        } 
        i += selectedIndexPath.row 
        return i 
       }() 

       delegate.didPlaceWord(word: movingLabel.text!, atIndex: index) 
       UIView.animate(withDuration: 0.25, animations: { 
        self.movingLabel.alpha = 0 
        self.movingLabel.removeFromSuperview() 
        }, completion: { _ in 
         self.movingLabel = nil }) 
      } 
      else 
      { 
       if let _ = movingLabel { 
        delegate.didPlaceWord(word: movingLabel.text!, atIndex: movingLabel.tag) 
        UIView.animate(withDuration: 0.25, animations: { 
         self.movingLabel.alpha = 0 
         self.movingLabel.removeFromSuperview() 
        }, completion: { _ in 
         self.movingLabel = nil }) 
       } 
      } 

     default: 
      collectionView?.cancelInteractiveMovement() 
      print("Interactive movement canceled") 
     } 
    } 

    override func numberOfSections(in collectionView: UICollectionView) -> Int { 
     guard !(self.collectionViewLayout as! bespokeCollectionViewLayout).cache.isEmpty else { return 0 } 

     return (self.collectionViewLayout as! bespokeCollectionViewLayout).cache.last!.indexPath.section + 1 
    } 

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 
     guard !(self.collectionViewLayout as! bespokeCollectionViewLayout).cache.isEmpty else { return 0 } 

     var n : Int = 0 
     for element in (self.collectionViewLayout as! bespokeCollectionViewLayout).cache { 
      if element.indexPath.section == section { 
       if element.indexPath.row > n { 
        n = element.indexPath.row 
       } 
      } 
     } 
     print("Section \(section) has \(n) elements") 
     return n + 1 
    } 

    override func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { 
     let change = sourceData[sourceIndexPath.row] 

     sourceData.remove(at: sourceIndexPath.row) 
     sourceData.insert(change, at: destinationIndexPath.row) 
    } 

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 
     collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell") 

     let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) 

     // Clean 
     for subview in cell.subviews { 
      subview.removeFromSuperview() 
     } 

     let label : UILabel = { 
      let l : UILabel = UILabel() 
      l.font = UIFont.systemFont(ofSize: 17) 
      l.frame = CGRect(origin: CGPoint.zero, size: cell.frame.size) 
      l.textColor = UIColor.black 

      let index : Int = { 
       var i : Int = 0 
       for sectionCount in 0..<indexPath.section { 
        i += (self.collectionView?.numberOfItems(inSection: sectionCount))! 
       } 
       i += indexPath.row 
       return i 
      }() 

      l.text = sourceData[index] 
      return l 
     }() 

     cell.addSubview(label) 

     return cell 
    } 

} 


class bespokeCollectionViewLayout : UICollectionViewLayout { 

    var cache : [UICollectionViewLayoutAttributes] = [UICollectionViewLayoutAttributes]() 
    let contentWidth: CGFloat 
    var dimensions : [CGSize]! 

    init(contentWidth: CGFloat) { 
     self.contentWidth = contentWidth 

     super.init() 
    } 

    required init?(coder aDecoder: NSCoder) { 
     fatalError("init(coder:) has not been implemented") 
    } 

    override func prepare() -> Void { 
     guard self.dimensions != nil else { return } 
     if cache.isEmpty { 
      var xOffset : CGFloat = 0 
      var yOffset : CGFloat = 0 

      var rowCount = 0 
      var wordCount : Int = 0 

      while wordCount < dimensions.count { 
       let nextRowCount : Int = { 
        var totalWidth : CGFloat = 0 
        var numberOfWordsInRow : Int = 0 

        while totalWidth < contentWidth && wordCount < dimensions.count { 
         if totalWidth + dimensions[wordCount].width >= contentWidth { 
          break 
         } 
         else 
         { 
          totalWidth += dimensions[wordCount].width 
          wordCount += 1 
          numberOfWordsInRow += 1 
         } 

        } 
        return numberOfWordsInRow 
       }() 

       var columnCount : Int = 0 
       for count in (wordCount - nextRowCount)..<wordCount { 
        let index : IndexPath = IndexPath(row: columnCount, section: rowCount) 
        let newAttribute : UICollectionViewLayoutAttributes = UICollectionViewLayoutAttributes(forCellWith: index) 
        let cellFrame : CGRect = CGRect(origin: CGPoint(x: xOffset, y: yOffset), size: dimensions[count]) 
        newAttribute.frame = cellFrame 
        cache.append(newAttribute) 

        xOffset += dimensions[count].width 
        columnCount += 1 
       } 

       xOffset = 0 
       yOffset += dimensions[0].height 

       rowCount += 1 

      } 
     } 
    } 

    override var collectionViewContentSize: CGSize { 
     guard !cache.isEmpty else { return CGSize(width: 100, height: 100) } 
     return CGSize(width: self.contentWidth, height: cache.last!.frame.maxY) 
    } 

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 
     var layoutAttributes = [UICollectionViewLayoutAttributes]() 
     if cache.isEmpty { 
      self.prepare() 
     } 
     for attributes in cache { 
      if attributes.frame.intersects(rect) { 
       layoutAttributes.append(attributes) 
      } 
     } 
     return layoutAttributes 
    } 
} 
+0

があなたの仕事をありがとうイタリアのよ、私はこのことを理解し、実装しようとします。私はあなたの更新を保つよ – ale00

+0

@ ale00問題はない、私はそれに取り組んで楽しんだ。私はそれが比較的複雑であることを感謝します。しかし、あなたがそれを実行し、コードを処理するなら、それは理にかなっているはずです。私は別注コレクションビューのレイアウトがこのような場合にはさらに多くのコントロールを提供することを発見しました。 – Sparky

+0

私はコードを実行し、それがうまくいく、ジェスチャー認識機能を使って並べ替えを実装しようとしましたが、実際の行のセルを移動するとセルが消え、アプリ外で移動するとクラッシュします。ここに私のコードがあります:https://gist.github.com/ale00/a34c575e1d4f6f16774c68a921759d3b、変更は88行で始まります。私は間違った方法でやっているかもしれません。 (英語の間違いの申し訳ありません) – ale00

関連する問題