web-dev-qa-db-ja.com

UICollectionViewダイナミックセルの高さswift

可変長の文字列を表示するセルを持つコレクションビューを作成しようとしています。

この関数を使用してセルレイアウトを設定する:

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

私がやりたいのは、cellSize.height私のcell.labelString.utf16Count 長さ。基本的なロジックは

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
    }
29
SKYnine

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

17
Ian

上記の答えはあなたの問題を解決するかもしれませんが、それは各セルの高さを割り当てるかなり粗雑な方法を確立します。いくつかの推定に基づいて、各セルの高さをハードコーディングする必要があります。この問題を処理するより良い方法は、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)
}
21
Eli Whittle

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()
    }
}
7
Manpreet Singh