2

I want to add some n views inside a table cell (lets say each view is a row). Based on the designerExpertiseList count, i have created a view for each row and added 1 image view and 1 label.

But when i scroll, the data for cells is not correct. If i long press on a cell, i can see a different view overlapped with the one visible now. Please check the attached screenshots

1st time when the view is loaded : https://i.stack.imgur.com/M8itL.png
After i scroll down, scroll up again and long press: https://i.stack.imgur.com/AuTG0.png

And when i scroll up, the data which was correct the first time for few cell, even that is getting messed up. I even tried to add these dynamic views only once, on First Render.

There are the global declarations:

let rowHeight:CGFloat = 20.0
let imageWidth:CGFloat = 15.0
let imageHeight:CGFloat = 15.0
let labelX:CGFloat = 30.0
let labelHeight:CGFloat = 20.0
var firstRender:[Bool] = [Bool]()


code inside tableView cellForRowAtIndexPath method:

self.designer = AppController.getElementFromDesignersList(indexPath.section)

        cell.designerName.text = designer.getFirstName() + " " + designer.getLastName()
        cell.location.text = designer.getAddress()

        // Add more ROWS of expertise if exist! Getting only 1st expertise now, if it exists
        let expertiseList = self.designer.getExpertiseList()

        if self.firstRender[indexPath.section] {
            var i:Int = 0
            for e in expertiseList {
                let v = UIView(frame: CGRectMake(0, CGFloat(i)*rowHeight, cell.frame.width, rowHeight))
                v.backgroundColor = UIColor.yellowColor()
                let im = UIImageView(frame: CGRectMake(0, CGFloat(i)*imageHeight, imageWidth, imageHeight ))
                //print("expertise image path: ", e.getImagePath())
                im.af_setImageWithURL(
                    NSURL(string: e.getImagePath())!,
                    placeholderImage: UIImage(named: "default_galary_demo2")!
                )

                im.translatesAutoresizingMaskIntoConstraints = false
                v.addSubview(im)

                // Adding constraints
                NSLayoutConstraint(item: im, attribute: .CenterY, relatedBy: .Equal, toItem: v, attribute: .CenterY, multiplier: 1, constant: 0).active = true
                NSLayoutConstraint(item: im, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: v, attribute: NSLayoutAttribute.LeadingMargin, multiplier: 1.0, constant: 0.0).active = true
                im.widthAnchor.constraintEqualToAnchor(nil, constant: imageWidth).active = true
                im.heightAnchor.constraintEqualToAnchor(nil, constant: imageHeight).active = true

                // cell.frame.width - im.frame.width - 50
                let label = UILabel(frame: CGRectMake(0, CGFloat(i)*labelHeight, UIScreen.mainScreen().bounds.width - imageWidth, labelHeight))
                label.font = UIFont(name: "OpenSans", size: 12)
                print("expertise dump:  ", dump(e.getExpertiseValuesList()))
                //print("expertise str: ", e.getExpertiseValuesList().map({"\($0.getName())"}).joinWithSeparator(","))
                label.text = e.getExpertiseValuesList().map({"\($0.getName())"}).joinWithSeparator(",")

                //label.translatesAutoresizingMaskIntoConstraints = false
                v.addSubview(label)


                NSLayoutConstraint(item: label, attribute: .CenterY, relatedBy: .Equal, toItem: v, attribute: .CenterY, multiplier: 1, constant: 0).active = true

                NSLayoutConstraint(item: label, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: im, attribute: NSLayoutAttribute.LeadingMargin, multiplier: 1.0, constant: 10.0).active = true

                cell.designerExpertiseView.addSubview(v)
                i += 1
            }
            self.firstRender[indexPath.section] = false
kishorer747
  • 810
  • 1
  • 10
  • 24
  • Do you add constraints? `view.addConstraint` take a look here http://stackoverflow.com/questions/31651022/how-to-create-layout-constraints-programmatically – tbilopavlovic Jun 15 '16 at 13:33
  • I am using . active=true which will activate the constraint. No need to separately add constraint. Atleast that's what I read – kishorer747 Jun 15 '16 at 14:15

3 Answers3

0

I don't think it correct to render cell only when if self.firstRender[indexPath.section] because when user scroll table view, the cells out of screen will be reused to show others different indexPaths. Another thing, you create constraints, but not use them

J.Hunter
  • 576
  • 4
  • 14
  • I added this condition only for elements I am adding dynamically. Other UI elements will render. I am creating constraints and activating directly by using .active=true – kishorer747 Jun 15 '16 at 14:19
  • I suppose a cell will be render, for example cell at indexPath(0, ), the delegate method `cellForRowAtIndexPath ` will be called, your code add subviews dynamic correctly, as `self.firstRender[0]` is true. Then user scroll table view, cell(0, 0) becomes invisible,so cell(0, 0) will be reused by another cell. When cell(0, 0) becomes visible again, codes in `cellForRowAtIndexPath ` will not be executed because `self.firstRender[0]` is false, then no subview will be added, is it what you want? – J.Hunter Jun 16 '16 at 01:24
  • I added firstRender check because I thought it would prevent adding the dynamic views multiple times to the same cell. But now I think I will take s different approach. I will have 3 rows. If not needed, I will just do remove sub view – kishorer747 Jun 16 '16 at 06:46
  • @kishorer747 suggest you to do an experiment, add some rows more than one screen. Another thing, I'm a Objective-C programer, knowing little about Swift. In Object-C, you should call `[self.view addConstraint:constraint];` to apply constraint. Is it not necessary to call `self.view.addConstraint(constraint)` in Swift? I search in Apple's document, it seems like you should do the same thing – J.Hunter Jun 16 '16 at 09:55
0

You could use UIStackViews to handle the constraints for you. You would:

  1. Greatly reduce the boilerplate-constraint code in your class;
  2. Simplify adding subviews;
  3. Allow for Adaptive Layouts.

Add a Vertical UIStackViewFor into your UITableViewCell. For more complex layouts, you can either embed more UIStackViews into the Vertical stack view or mix AutoLayout and UIStackViews.

Using a UIStackView

I've quickly tried to recreate your layout and I've added a UIStackView. This implementation uses AutoLayout for some fixed components (like the profile picture) and the stack view to handle UILabels added programatically.

I hope this helps!

Lucas
  • 880
  • 7
  • 12
  • Hi, I don't want to use stack views because it is only for iOS 9+. I am targeting Indian audience where some users are still on iOS 7 and iOS 8. Stack view seemed very promising, but I decided not to use it for the same reason above – kishorer747 Jun 15 '16 at 14:16
0

As it seems, adding views programmatically to cell using .addSubView() won't go well with reusable cell. It happens since cells are reused when go out of view, but when become visible again, the subviews are being added again.

The solution/workaround I used was to add another placeholder view from storyboard. And i removed the view from superview using .removeFromSuperview() when not needed.

I know this method is not good Ex: Lets say I put 10 views in a cell ( assuming 10 is maximum views i need), but many of them might not be needed based on the data i get from server. So i will remove most of them from the view.

I am still looking for a better/actual solution for this issue.

kishorer747
  • 810
  • 1
  • 10
  • 24