Last active
December 19, 2021 15:05
-
-
Save alessionossa/a34c575e1d4f6f16774c68a921759d3b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
class ViewController: UIViewController { | |
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 { | |
let dimensions : [CGSize] = { | |
var d : [CGSize] = [CGSize]() | |
let font = UIFont.systemFont(ofSize: 17) | |
let fontAttributes = [NSFontAttributeName : font] | |
print(sourceData) | |
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() | |
} | |
} | |
} | |
} | |
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.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 sort() -> Void { | |
sourceData = sortDirection > 0 ? sourceData.sorted(by: { $0 > $1 }) : sourceData.sorted(by: { $0 < $1 }) | |
sortDirection = sortDirection + 1 > 1 ? 0 : 1 | |
} | |
} | |
class bespokeCollectionViewController : UICollectionViewController { | |
var sourceData : [String] | |
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) { | |
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 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 | |
} | |
} | |
print(cache) | |
} | |
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 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment