2014-12-04 70 views
20

我试图创建一个集合视图,显示单元格显示可变长度的字符串。CollectionView动态细胞高度迅速

即时通讯使用此功能设置单元布局:

func collectionView(collectionView : UICollectionView,layout collectionViewLayout:UICollectionViewLayout,sizeForItemAtIndexPath indexPath:NSIndexPath) -> CGSize 
    { 
     var cellSize:CGSize = CGSizeMake(self.whyCollectionView.frame.width, 86) 
     return cellSize 
    } 

什么,我想这样做是基于我cell.labelString.utf16Count长度操纵cellSize.height。 的基本逻辑将SA是

if((cell.labelString.text) > 70){ 
    cellSize.height = x 
} 
else{ 
    cellSize.height = y 
} 

但是,我不能设法找回我的电池标签字符串长度总是返回零。 (我认为这是没有载入...

为了更好的理解,这里是全码:

// 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 
    } 

回答

11

可以动态地设置在cellForItemAtIndexPath功能细胞的框架,所以你可以自定义如果你忽略了sizeForItemAtIndexPath函数的高度,那么在定制尺寸的时候,你可能不得不查看收集视图的布局流程,但是希望这可以让你指向正确的方向:它可能看起来像这样:

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

这样可以提供动态高度 enter image description here

+0

真棒,我探索了几种方法来解决这个问题,但我没有看到在cellForItemAtIndexPath中这样做。感谢您的提示,我会让你知道! – SKYnine 2014-12-04 14:35:07

+1

好吧,看来我可以使用你的方法调整高度,但现在我的问题变成了行之间的间距。我有一些我不需要的重要空间。我的故事板内所有设置都设置为零 – SKYnine 2014-12-05 02:17:31

+0

高度[indexPath.row]是服务器数据对象。请告诉我。 – Malleswari 2018-03-10 08:21:36

15

尽管上面的答案可以解决您的问题,但它确立了一种非常简单的分配每个单元高度的方法。您正在被迫根据一些估计对每个单元高度进行硬编码。处理此问题的更好方法是通过设置collectionview的sizeForItemAtIndexPath委托方法中每个单元格的高度。

我会引导您完成如何在下面执行此操作的步骤。

第1步:使你的类扩展UICollectionViewDelegateFlowLayout

第2步:创建一个函数来估算你的文字大小:此方法会返回一个高度值,将适合您的字符串!

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

第3步:使用或覆盖的委托方法如下:

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) 
} 
+0

Method .boundingRectWithSize似乎不适用于swift 3和ios 10.计算在不同的屏幕尺寸上有所不同,更重要的是它计算的文本高度过高 – virusss8 2016-12-13 14:52:55

+1

非常感谢。我一直试图弄清楚这一点,我看到的例子都没有清楚地指出你需要扩展UICollectionViewDelegateFlowLayout。 – 2017-05-22 18:53:44

+0

没问题,很高兴你明白了。 – 2017-05-22 19:01:54

6

在斯威夫特3,使用下面的方法:

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

你在哪里调用这个方法? – 2016-09-18 14:39:11

+1

我以行数方法调用此方法,以便每次都会为该行创建高度。此外,您可以使用方向更改方法调用它,并调用重新加载集合视图。 – 2016-09-18 19:15:12