Layout CollectionView动态单元格高度

Layout CollectionView动态单元格高度,layout,swift,height,uicollectionview,Layout,Swift,Height,Uicollectionview,我正在尝试创建一个集合视图,其中单元格显示长度可变的字符串 Im使用此功能设置单元格布局: func collectionView(collectionView : UICollectionView,layout collectionViewLayout:UICollectionViewLayout,sizeForItemAtIndexPath indexPath:NSIndexPath) -> CGSize { var cellSize:CGSize = CGS

我正在尝试创建一个集合视图,其中单元格显示长度可变的字符串

Im使用此功能设置单元格布局:

 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
。 基本逻辑是

if((cell.labelString.text) > 70){
   cellSize.height = x
}
else{
   cellSize.height = y
}
但是,我无法检索总是返回nil的单元格标签字符串长度。(我想它还没有加载

为了更好地理解,以下是完整的代码:

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

您可以在
CellForItemIndexPath
函数中动态设置单元格的框架,因此,如果不考虑
SizeForItemIndexPath
函数,您可以基于标签自定义高度。通过自定义大小,您可能需要查看集合视图布局流程,但希望这能为您指明方向t方向。它可能看起来像这样:

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)
    }
}
这就产生了这样的动态高度

虽然上面的答案可能会解决您的问题,但它建立了一种相当粗糙的分配每个单元格高度的方法。您被迫根据一些估计值对每个单元格高度进行硬编码。处理此问题的更好方法是在collectionview的
SizeFormiteIndeXPath
委托方法中设置每个单元格的高度

我将引导您完成下面有关如何执行此操作的步骤

步骤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)
}
private func estimateFrameForText(文本:字符串)->CGRect{
//我们将高度设为任意大,这样在计算中就不会忽略高度
让高:CGFloat=
let size=CGSize(宽度:您想要的宽度,高度:高度)
let options=NSStringDrawingOptions.UseSFuntleding.union(.UsesLineFragmentOrigin)
let attributes=[NSFontAttributeName:UIFont.systemFontOfSize(18,权重:UIFontWeightLight)]
返回NSString(字符串:文本)。boundingRectWithSize(大小、选项:选项、属性:属性、上下文: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)
}
func collectionView(collectionView:UICollectionView,layout collectionViewLayout:UICollectionViewLayout,sizeFormiteIndepath indepath:nsindepath)->CGSize{
变量高度:CGFloat=
//我们只是测量高度,所以我们添加了一个填充常数,给标签一些呼吸的空间!
变量填充:CGFloat=
//估计每个单元的高度
如果let text=array?[indexPath.item].text{
高度=估计FrameforText(文本)。高度+填充
}
返回CGSize(宽度:yourDesiredWidth,高度:height)
}

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

太棒了,我探索了几种解决此问题的方法,但我没有考虑在cellForItemAtIndexPath中执行此操作。感谢您提供的提示,我会让您知道!好的,似乎我可以使用您的方法调整高度,但现在我的问题变成了行间距。我有一些不需要的重要空间。我的所有设置都设置为零w我在我的故事板高度[indexPath.row]是服务器数据对象。你能告诉我吗。你不应该计算单元格的高度而不是将其存储在数组中吗?你在哪里调用此方法?我在“行数”方法中调用此方法,以便它每次都为行创建高度。此外,你可以在“方向更改”方法中调用它并调用重新加载集合view.Method.boundingRectWithSize似乎无法与swift 3和ios 10配合使用。计算在不同的屏幕尺寸上是不同的,更重要的是,它计算的文本高度太高了。非常感谢。我一直在努力解决这个问题,但我看到的示例中没有一个足够清楚地指出需要扩展UICollectionViewDelegateFlowLayout。没问题,很高兴你已经弄明白了。