2014-12-04 72 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