2016-09-25 13 views
9

Chcę zmienić kolejność komórek w widoku kolekcji z niestandardowym rozmiarem dla każdej komórki.
W każdej komórce widoku kolekcji znajduje się etykieta z napisem.
ustawić wymiar każdej komórki z tego kodu:Problemy z ponownym przetwarzaniem widoku kolekcji Komórka o niestandardowych wymiarach

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 
} 

I uporządkować kolekcji widok z tego kodu:

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() 
} 

Widok wygląda tak: collection view

Problem jest to, że podczas zmiany kolejności komórki zachowują wymiary oryginalnej komórki w ścieżce indexPath, więc podczas zmiany kolejności widok wygląda następująco: reordering W tej chwili poprawiono problem z ponownym ładowaniem danych po zakończeniu zmiany kolejności, aby ponownie obliczyć właściwe wymiary. W jaki sposób mogę zachować prawidłowe wymiary komórek również podczas ruchu interaktywnego i zmieniać ich rozmiary?

+0

Niestety jeśli zrobiłem Sime błędów z językiem, jestem włoski – ale00

Odpowiedz

3

Przez cały tydzień dręczyło mnie to, więc usiadłem dziś wieczorem i spróbowałem znaleźć rozwiązanie. Myślę, że potrzebujesz niestandardowego menedżera układu dla widoku kolekcji, który dynamicznie dostosowuje układ każdej komórki w miarę zmiany kolejności.

Poniższy kod w oczywisty sposób daje coś znacznie większego niż powyższy układ, ale zasadniczo osiąga pożądane zachowanie: w przypadku zmiany kolejności zachodzi natychmiastowe przejście do nowego układu, "natychmiast" bez żadnych wymaganych korekt tymczasowych.

Kluczem do wszystkiego jest funkcja didSet w zmiennej sourceData kontrolera widoku. Gdy wartość tej tablicy zostanie zmieniona (poprzez naciśnięcie przycisku sortowania - moje przybliżone przybliżenie do rozpoznawanego gestu), to automatycznie powoduje ponowne obliczenie wymaganych wymiarów komórki, co powoduje również wygaszenie układu i ponowne obliczenie oraz widok kolekcji w celu ponownego załadowania dane.

Jeśli masz jakiekolwiek pytania na ten temat, daj mi znać. Mam nadzieję, że to pomoże!

AKTUALIZACJA: OK, rozumiem, co próbujesz teraz zrobić, i myślę, że załączony zaktualizowany kod cię tam zawiedzie. Zamiast korzystać z wbudowanych metod interakcji, myślę, że jest to łatwiejsze, biorąc pod uwagę sposób, w jaki zaimplementowałem niestandardowy menedżer układu do korzystania z delegacji: gdy wybierak gestów panoramy wybierze komórkę, tworzymy widok podzapytania oparty na tym słowie, który porusza się wraz z gest. W tym samym czasie usuwamy słowo ze źródła danych i odświeżamy układ. Gdy użytkownik wybierze lokalizację, w której umieszcza słowo, odwracamy ten proces, nakazując delegatowi wstawienie słowa do źródła danych i odświeżenie układu. Jeśli użytkownik przeciągnie słowo poza widok kolekcji lub do niewłaściwej lokalizacji, słowo zostanie po prostu cofnięte tam, gdzie ono się zaczęło (użyj sprytnej techniki przechowywania oryginalnego indeksu jako znacznika etykiety).

Mam nadzieję, że ci pomogę!

[uprzejmości Tekst Wikipedia]

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

Dziękuję za waszą pracę, będę starać się zrozumieć i wdrożyć to. Będę Cię aktualizował – ale00

+0

@ ale00 bez problemu, podobała mi się praca nad tym. Rozumiem, że jest to stosunkowo złożone, ale jeśli go uruchomisz i wykonasz kod, powinno to mieć sens.Uważam, że układ widoków kolekcji zindywidualizowanych zapewnia o wiele większą kontrolę w takich przypadkach. – Sparky

+0

Uruchomiłem kod i działa dobrze, a następnie próbowałem zaimplementować zmianę kolejności za pomocą rozpoznawania gestów, ale jeśli przeniesię komórkę w rzeczywistą linię, komórka zniknie i jeśli przeniesię ją poza aplikację, ulegnie awarii. Oto mój kod: https://gist.github.com/ale00/a34c575e1d4f6f16774c68a921759d3b, zmiany zaczynają się od linii 88. Może robię to w niewłaściwy sposób. (Przepraszamy za błędy w języku angielskim) – ale00

Powiązane problemy