4

I am having some serious trouble getting my multiline label to show all of its lines. Often, the last line of the label simply does not appear, but it is apparent that the dynamically calculated cell height has taken in to account that it should have appeared, leaving around the appropriate amount of white space left over in my cell.

The affected label can display 1-7 lines depending on the data. I have played around with many various constraints to try and get it to display but regardless of what is on the last line, it just won't display.

The weird thing is, sometimes it will display when I segue in to the VC, but then when I use the segmented controller inside the VC to display different data and then go back again, the last line will again not display. The opposite happens frequently too (last line of label cutting off when I segue in to the VC, but then using the segmented controller inside the VC to change the displayed data and then go back, it will then display fine).

Things I have ensured: The label is set to word wrap, has line count of 0, has a height greater than or equal to the height of one of it's lines, its resistance and vertical content hugging is set to the highest of anything in the cell, and the width is set appropriately.

The below code is how I determine how many lines the label will have:

let descString = NSMutableAttributedString()

let bountyStart = NSMutableAttributedString(string: "Bounty: ", attributes: [NSFontAttributeName : UIFont.boldSystemFontOfSize(15)])
let bountyDesc = NSMutableAttributedString(string: bounty.description)
descString.appendAttributedString(bountyStart)
descString.appendAttributedString(bountyDesc)

let bLine = NSMutableAttributedString(string: "\n\n", attributes: [NSFontAttributeName : UIFont.systemFontOfSize(2)])
descString.appendAttributedString(bLine)

if !(bounty.turtles == 0.0){
    let turtleStart = NSMutableAttributedString(string: "Your turtle count: ")
    let turtleAmount = NSMutableAttributedString(string: bounty.turtleCount.description)
    descString.appendAttributedString(turtleStart)
    descString.appendAttributedString(turtleAmount)
}
descriptionLabel.attributedText = descString

In the screen shot below, you can see that the height of the cell is being calculated appropriately but for some reason, the last line of the label just refuses to show. It should appear after the "is for noobs" line. I've manipulated the white space to appear after the line instead of elsewhere by setting the problem labels bottom to constraint to be greater than or equal, and all other top to bottom constraints as equal to.

enter image description here

Constraints of the problem label:

enter image description here

I've been stumped for quite a while on this one, and I'm starting to think it's not the constraints I've set but something much deeper. All though I would love to be proven wrong.

Here is my VC code.

    import UIKit

class TrendingVC: UIViewController, UITableViewDataSource, UITableViewDelegate{

    @IBOutlet weak var menubtn:UIBarButtonItem!
    @IBOutlet var trendingTableView:UITableView!  

    var trendingToggle:Int = 0
    let nwt = NWTrending()
    let appUserId = NSUserDefaults.standardUserDefaults().stringForKey("UserId") ?? "1" //@TODO: remove ?? 1
    var bountyArr: [Bounty] = []
    var compArr: [Completion] = []
    var peopleArr: [Person] = []

    var userId: String = "0"
    var username: String = ""

    let bountyCellIdentifier = "BountyCellNew"
    let personCellIdentifier = "PersonCell"
    let completedCellIdentifier = "TrendingCompletedImageCell"



    @IBAction func toggleTrending(sender:UISegmentedControl){
        switch sender.selectedSegmentIndex{
        case 0:
            //loads the bounties on segmented control tab
            trendingToggle=0
            nwt.getTrendingBounties(appUserId, position: 0){(bountyArr, err) in //@TODO: change pos
                self.bountyArr = bountyArr as [Bounty]
                self.reloadTableViewContent()
            }
        case 1:
            trendingToggle=1
            nwt.getTrendingCompletions(appUserId, position: 0){(compArr, err) in
                self.compArr = compArr as [Completion]
                self.reloadTableViewContent()
            }
        case 2:
            trendingToggle=2
            nwt.getTrendingPeople(appUserId, position: 0){(peopleArr, err) in
                self.peopleArr = peopleArr as [Person]
                self.reloadTableViewContent()
            }
        default:
            break
        }

        //reloadTableViewContent()
    }



    override func viewDidLoad() {
    super.viewDidLoad()

    trendingTableView.estimatedRowHeight = 300.0
    trendingTableView.rowHeight = UITableViewAutomaticDimension


    /******* Kyle Inserted *******/
    //for followers and following back button text, you set it here for when you segue into that section
    let backItem = UIBarButtonItem(title: " ", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
    navigationItem.backBarButtonItem = backItem
    /******* END Kyle Inserted *******/

    trendingTableView.allowsSelection = false;
    trendingTableView.delegate = self
    trendingTableView.dataSource = self

    //sidebar code
    if self.revealViewController() != nil {
        menubtn.target = self.revealViewController()
        menubtn.action = "revealToggle:"
        self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
    }

    //loads the bounty on segue
    nwt.getTrendingBounties(appUserId, position: 0){(bountyArr, err) in
        self.bountyArr = bountyArr as [Bounty]
        self.reloadTableViewContent()
    }
}


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        //deselectAllRows()

    }


    func deselectAllRows() {
        if let selectedRows = trendingTableView.indexPathsForSelectedRows() as? [NSIndexPath] {
            for indexPath in selectedRows {
                trendingTableView.deselectRowAtIndexPath(indexPath, animated: false)
            }
        }
    }

    func reloadTableViewContent() {
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            self.trendingTableView.reloadData()
            println("reloading table view content")
            self.trendingTableView.scrollRectToVisible(CGRectMake(0, 0, 1, 1), animated: false)
        })
    }

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }


    func tableView(trendingTableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if trendingToggle == 0{
        return bountyArr.count
        }
        else if trendingToggle == 1{
            return compArr.count
        }
        else {
            return peopleArr.count
        }
    }


    func tableView(trendingTableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        if trendingToggle == 0{
            return bountyCellAtIndexPath(indexPath)
        }
        else if trendingToggle == 1{
            return completedCellAtIndexPath(indexPath)
        }
        else{
        return personCellAtIndexPath(indexPath)
        }
    }

    //calls method to set and display each trending bounty cell
    func bountyCellAtIndexPath(indexPath:NSIndexPath) -> BountyCellNew {
        let cell = trendingTableView.dequeueReusableCellWithIdentifier(bountyCellIdentifier) as! BountyCellNew
        var bounty = bountyArr[indexPath.row]
        cell.setBountyCellTrending(bounty)
        return cell
    }

    func completedCellAtIndexPath(indexPath:NSIndexPath) -> CompletedCell{
        let cell = trendingTableView.dequeueReusableCellWithIdentifier(completedCellIdentifier) as! CompletedCell
        var comp = compArr[indexPath.row]
        cell.setTrendingCompletedCell(comp)
        return cell
    }


    func personCellAtIndexPath(indexPath:NSIndexPath) -> PersonCell{
        let cell = trendingTableView.dequeueReusableCellWithIdentifier(personCellIdentifier) as! PersonCell
        var peop = peopleArr[indexPath.row]
        cell.setTrendingPeopleCell(peop)
        return cell
    }   
}
Mitchell
  • 333
  • 5
  • 20
  • Try to print in console `descString` at the end of code. And verify what it contains when it works fine and when it bug. – Mindsers Sep 17 '15 at 05:31
  • @Mindsers I have, that's not the problem. It's a problem with my constraints and auto layout – Mitchell Sep 17 '15 at 05:39

3 Answers3

0

Unable to comment due to rep low. This seems like a IB thing, very likely constraints. Make sure that any component's top constraint placed under this multiline UILabel is set to the bottom of said UILabel.

For example, in this project of mine, both the TitleView and TimeAndDetailsView contain UILabels that span multiplelines. In order to allow autolayout to properly space things all constraints need to be in order, also notice how the top constraint of TimeAndDetails is the bottom of TitleView enter image description here




Edit note: Before you tableView.reloadData() type the following 2 lines, or if uncertain, put it inside viewDidLoad

tableView.estimatedRowHeight = 60 //Type your cell estimated height
tableView.rowHeight = UITableViewAutomaticDimensionhere
sweepez
  • 585
  • 1
  • 4
  • 17
  • The label underneaths top space to the problem label is set to equal a distance of 1 – Mitchell Sep 17 '15 at 06:02
  • its fine if it is equal to 1 as long as that is the spacing you want between the labels. Make sure that anything else under the label right under the "problem label" also has a top constraint to the component right above it. For multi lines `UILabel`s is better if everything (constraint wise) follows the hierarchical order layed out in storyboards. – sweepez Sep 17 '15 at 06:10
  • What do you mean by the hierarchical order laid out in storyboards? I would post a few pictures of my constraints + what the cell should look like vs what's happening but I don't have the 10 reputation to do so. – Mitchell Sep 17 '15 at 06:30
  • I meant, using my project as reference. The uiimageview is irrelevant since it comes before any of the uilabels, so I only gave it a top and left to its superview and a height&width constraint, however, since the titleview is the first element in the view that contain a uilabel that can span multiple lines, then anything below it should have top constraints to the element right above it. The TitleView bottom constraint is set to the top of TimeAndDetailsView, the bottom constraint of TimeAndDetailsView is set to the top locationview. – sweepez Sep 17 '15 at 06:49
  • and the locationview bottom constraint being the last element in the view has as bottom constraint the bottom layout guide – sweepez Sep 17 '15 at 06:49
  • wait, did you say this is a cell? you need to use UITableViewAutomaticDimension. Adding note to answer, take a look. – sweepez Sep 17 '15 at 06:55
  • yes it's a cell. I am using that method in my overridden viewDidLoad method. – Mitchell Sep 17 '15 at 06:58
  • Not sure whats happening from looking at the picture with the black rectangles. Sadly going to bed now. If you already print `descString` and it does have all the text, then you are probably still looking at constraint problems. I know you probably have gone through all of them already many times :-( – sweepez Sep 17 '15 at 07:17
  • You said you had the height constraint set to >= Int. Where is it?, also, as far as I know, only the height needs the greater than sign, the trailing and top can be set to just equal – sweepez Sep 17 '15 at 07:18
  • I am very silly, I put the constraints of the wrong label in my question. I have updated it now to the right ones – Mitchell Sep 17 '15 at 07:22
  • Yeah I know, but if I don't put the bottom space to greater than or equal, the white space just displays somewhere else, either at the bottom of the cell or between other labels. – Mitchell Sep 17 '15 at 07:23
  • then you need to remove the >= from all the other elements under the "problem label". Say, you have bottom space to bounty type set to 1, you always want that spacing to be 1 regardless of the size of the 'problem label' so it needs to be = 1 not >= 1. All elements under the label need to have = Int constraints for top/bottom/leading/trailing. Only the height constraint of the multiline label should have >= Int, that way everything under it will move accordingly since they are strictly = Int – sweepez Sep 17 '15 at 07:28
  • That's what I had originally, I only used the >=1 to control where the white space was going to be. This is how it looks when all top/bottom constraints are equal to 1: http://i.stack.imgur.com/sXnjr.png The bottom line still doesn't display, it's just the whitespace is moved else where – Mitchell Sep 17 '15 at 13:46
0

Went to sleep last night, ok so I decided to add your code to my details label from the storyboards picture I had posted before, and it seems to have displayed just fine. I have a few questions and pointers below.

enter image description here

let descString = NSMutableAttributedString()
let bountyStart = NSMutableAttributedString(string: "Bounty: ", attributes: [NSFontAttributeName : UIFont.boldSystemFontOfSize(15)])
let bountyDesc = NSMutableAttributedString(string: "this would be 'bounty.description and is set to size 40", attributes: [NSFontAttributeName : UIFont.boldSystemFontOfSize(40)])
descString.appendAttributedString(bountyStart)
descString.appendAttributedString(bountyDesc)
let bLine = NSMutableAttributedString(string: "\n\n", attributes: [NSFontAttributeName : UIFont.systemFontOfSize(10)])
descString.appendAttributedString(bLine)
let turtleStart = NSMutableAttributedString(string: "Your turtle count: ")
let turtleAmount = NSMutableAttributedString(string: "random number 1234 goes here")
descString.appendAttributedString(turtleStart)
descString.appendAttributedString(turtleAmount)
detailsLabel.attributedText = descString

Sadly, this is in a scrollview and not in a cell, so there is the first difference. My first question; is your descriptionLabel.attributedText supposed to display different fontscolors or fonttypes/sizes?, if not then I would say to just use descriptionLabel.text instead of descriptionLabel.attributedText. Now the fact that there is white space but nothing showing us makes me wonder if bounty.turtleCount.description was previously set to color white somewhere else, or you are simply seeing the newlines you added \n\n and nothing is there because if !(bounty.turtles == 0.0) doesnt execute. Are you sure the that if statement is executed? place a breakpoint and follow along to make sure the values are appended to descString

If this is all correct, then my guess would still be on constraints. Could you elaborate on, quote from you; "sometimes it will display when I segue in to the VC, but then when I use the segmented controller inside the VC to display different data and then go back again, the last line will again not display." what are you doing different in the seg control when loading the values and reloading the tableview that makes it not display, and under what conditions does it work properly when you segue.

sweepez
  • 585
  • 1
  • 4
  • 17
  • No problem. Thanks for helping. Yeah, I am displaying different font types colours and sizes. Nope, it's not set to white and yeah, I've double checked a few times. It definitely runs and definitely attaches to the string. You can tell the cell is making room for it, it just doesn't appear. It seems to be random, I have copied the code from another VC and in some VCs it will display and others it won't and sometimes it will display when using a segmented controller and sometimes it won't – Mitchell Sep 17 '15 at 23:53
  • Good luck trying to debug. This is one of the best posts out there on selfsizing cells, maybe there might be a detail listed there that could fix your problem. http://stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights?rq=1 – sweepez Sep 18 '15 at 00:56
  • Alrighty, well, went over the code, maybe I missed something (first time I read your post didnt realize you were talking about cells, LOL), if not, everything looks good to me. Only suggestions not regarding your problem are, no need for all the `self.` in `viewDidLoad`, you only need the `self` in closure blocks, also no need to call `numberOfSectionsInTableView` if you are not adding more sections, because the default is already 1. Out of curiosity, are all 3 cells 300px height?. Did you go through the link I posted to see if you find anything you could be missing for the self-sizing cells? – sweepez Sep 18 '15 at 06:52
  • One cell can vary from around 160-240, one cell is always 117 and the other cell can vary from 680-740 (roughly). I did go through it and the things that stuck out (placement of estimatedRowHeight) I changed but they didn't appear to affect anything – Mitchell Sep 18 '15 at 07:40
-1

I updated xcode and now this doesn't happen anymore.

Mitchell
  • 333
  • 5
  • 20