layout - automatic - uicollectionview dynamic cell size
CollectionView Dynamic cell height swift (3)
Estoy intentando crear una vista de colección con celdas que muestran una cadena con longitud variable.
Estoy usando esta función para establecer el diseño de la celda:
func collectionView(collectionView : UICollectionView,layout collectionViewLayout:UICollectionViewLayout,sizeForItemAtIndexPath indexPath:NSIndexPath) -> CGSize
{
var cellSize:CGSize = CGSizeMake(self.whyCollectionView.frame.width, 86)
return cellSize
}
Lo que me gustaría hacer es manipular cellSize.height
función de mi longitud de cell.labelString.utf16Count
. La lógica básica sería sa que
if((cell.labelString.text) > 70){
cellSize.height = x
}
else{
cellSize.height = y
}
Sin embargo, no puedo recuperar la longitud de la cadena de la etiqueta de mi celda que siempre devuelve nula. (Creo que aún no está cargado ...
Para una mejor comprensión, aquí está el código completo:
// WhyCell section
var whyData:NSMutableArray! = NSMutableArray()
var textLength:Int!
@IBOutlet weak var whyCollectionView: UICollectionView!
//Loading data
@IBAction func loadData() {
whyData.removeAllObjects()
var findWhyData:PFQuery = PFQuery(className: "PlacesWhy")
findWhyData.whereKey("placeName", equalTo: placeName)
findWhyData.findObjectsInBackgroundWithBlock({
(objects:[AnyObject]!,error:NSError!)->Void in
if (error == nil) {
for object in objects {
self.whyData.addObject(object)
}
let array:NSArray = self.whyData.reverseObjectEnumerator().allObjects
self.whyData = array.mutableCopy() as NSMutableArray
self.whyCollectionView.reloadData()
println("loadData completed. datacount is /(self.whyData.count)")
}
})
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.loadData()
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return whyData.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell:whyCollectionViewCell = whyCollectionView.dequeueReusableCellWithReuseIdentifier("whyCell", forIndexPath: indexPath) as whyCollectionViewCell
// Loading content from NSMutableArray to cell
let therew:PFObject = self.whyData.objectAtIndex(indexPath.row) as PFObject
cell.userWhy.text = therew.objectForKey("why") as String!
textLength = (therew.objectForKey("why") as String!).utf16Count
self.whyCollectionView.layoutSubviews()
// Displaying user information
var whatUser:PFQuery = PFUser.query()
whatUser.whereKey("objectId", equalTo: therew.objectForKey("reasonGivenBy").objectId)
whatUser.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]!, error: NSError!)->Void in
if !(error != nil) {
if let user:PFUser = (objects as NSArray).lastObject as? PFUser {
cell.userName.text = user.username
// TODO Display avatar
}
}
})
return cell
}
func collectionView(collectionView : UICollectionView,layout collectionViewLayout:UICollectionViewLayout,sizeForItemAtIndexPath indexPath:NSIndexPath) -> CGSize
{
var cellSize:CGSize = CGSizeMake(self.whyCollectionView.frame.width, 86)
return cellSize
}
En Swift 3, use el siguiente método:
private func updateCollectionViewLayout(with size: CGSize) {
var margin : CGFloat = 0;
if isIPad {
margin = 10
}
else{
margin = 6
/* if UIDevice.current.type == .iPhone6plus || UIDevice.current.type == .iPhone6Splus || UIDevice.current.type == .simulator{
margin = 10
}
*/
}
if let layout = menuCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.itemSize = CGSize(width:(self.view.frame.width/2)-margin, height:((self.view.frame.height-64)/4)-3)
layout.invalidateLayout()
}
}
Puede establecer dinámicamente el marco de la celda en la función cellForItemAtIndexPath
, por lo que puede personalizar la altura en función de una etiqueta si ignora la función sizeForItemAtIndexPath
. Con la personalización del tamaño, es probable que tenga que ver el flujo de diseño de la vista de colección, pero espero que esto le indique la dirección correcta. Puede parecer algo como esto:
class CollectionViewController: UICollectionViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var array = ["a","as","asd","asdf","asdfg","asdfgh","asdfghjk","asdfghjklas","asdfghjkl","asdghjklkjhgfdsa"]
var heights = [10.0,20.0,30.0,40.0,50.0,60.0,70.0,80.0,90.0,100.0,110.0] as [CGFloat]
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return array.count
}
override func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CellID", forIndexPath: indexPath) as Cell
cell.textLabel.text = array[indexPath.row]
cell.textLabel.sizeToFit()
// Customize cell height
cell.frame = CGRectMake(cell.frame.origin.x, cell.frame.origin.y, cell.frame.size.width, heights[indexPath.row])
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(64, 64)
}
}
Lo que da alturas dinámicas como tal.
Si bien la respuesta anterior puede resolver su problema, establece una forma bastante burda de asignar la altura de cada celda. Se le obliga a codificar de forma rígida cada altura de celda en función de alguna estimación. Una mejor manera de manejar este problema es estableciendo la altura de cada celda en el método delegado sizeForItemAtIndexPath
la sizeForItemAtIndexPath
de colección.
Te guiaré a través de los pasos sobre cómo hacer esto a continuación.
Paso 1: Haz que tu clase extienda UICollectionViewDelegateFlowLayout
Paso 2: crea una función para estimar el tamaño de tu texto: ¡Este método devolverá un valor de altura que se ajustará a tu cadena!
private func estimateFrameForText(text: String) -> CGRect {
//we make the height arbitrarily large so we don''t undershoot height in calculation
let height: CGFloat = <arbitrarilyLargeValue>
let size = CGSize(width: yourDesiredWidth, height: height)
let options = NSStringDrawingOptions.UsesFontLeading.union(.UsesLineFragmentOrigin)
let attributes = [NSFontAttributeName: UIFont.systemFontOfSize(18, weight: UIFontWeightLight)]
return NSString(string: text).boundingRectWithSize(size, options: options, attributes: attributes, context: nil)
}
Paso 3: Utilice o anule el método de delegado a continuación:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
var height: CGFloat = <someArbitraryValue>
//we are just measuring height so we add a padding constant to give the label some room to breathe!
var padding: CGFloat = <someArbitraryPaddingValue>
//estimate each cell''s height
if let text = array?[indexPath.item].text {
height = estimateFrameForText(text).height + padding
}
return CGSize(width: yourDesiredWidth, height: height)
}