VFL constraints in swift: Crashes due to no superview - ios

I'm trying to do a simple layout programmatically and I'm missing something simple, or have something out of place, I think. The following ViewController should center the label in the super view. However, it crashes with this trimmed message: The view hierarchy is not prepared for the constraint ... When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled... View not found in container hierarchy: ... That view's superview: NO SUPERVIEW The other SO questions with this error message are using nibs for the most part, and I'm tring to avoid that, or use Obj-C instead of swift. This question deals with the topic a bit but is a bit old.
Can anyone point me in the right direction?
class ViewController: UIViewController {
let label1 = UILabel() as UILabel
func layoutView(){
label1.text = "Click to see device configuration"
label1.setTranslatesAutoresizingMaskIntoConstraints(false)
view.addSubview(label1)
let viewsDictionary = ["label1":label1]
let label1_H:NSArray = NSLayoutConstraint.constraintsWithVisualFormat("H:|-[label1]-|",
options: NSLayoutFormatOptions(0),
metrics: nil,
views: viewsDictionary)
let label1_V:NSArray = NSLayoutConstraint.constraintsWithVisualFormat("V:|-[label1]-|",
options: NSLayoutFormatOptions(0),
metrics: nil, views:
viewsDictionary)
label1.addConstraints(label1_H) // Comment these 2 lines and it runs, but
label1.addConstraints(label1_V) // of course the label is upper left
}
override func viewDidLoad() {
super.viewDidLoad()
layoutView()
}
}

Those constraints are made between the label and its superview. The constraints should be added to that superview, not to the label.

You're almost there. Just replace the following lines...
label1.addConstraints(label1_H) // Comment these 2 lines and it runs, but
label1.addConstraints(label1_V) // of course the label is upper left
... with the following code:
view.addConstraints(label1_H) // Comment these 2 lines and it runs, but
view.addConstraints(label1_V) // of course the label is upper left
However, the constraints H:|-[label1]-|" and V:|-[label1]-|" are equivalent to H:|-8-[label1]-8-|" and V:|-8-[label1]-8-|" (see the iOS Developer Library for more details on default margins). Thus, those constraints are not meant to center your label. In fact, you will just have an enormous label that has 8 unit top, leading, trailing and bottom margins to the viewController's view.
Add the following line of code in your layoutView() method to see what I mean:
label1.backgroundColor = UIColor.orangeColor()
It can be OK but if you really want to center your label, you will have to use the following code:
func layoutView() {
label1.text = "Click to see device configuration"
label1.backgroundColor = UIColor.orangeColor()
//Set number of lines of label1 to more than one if necessary (prevent long text from being truncated)
label1.numberOfLines = 0
label1.setTranslatesAutoresizingMaskIntoConstraints(false)
view.addSubview(label1)
let xConstraint = NSLayoutConstraint(item: label1,
attribute: NSLayoutAttribute.CenterX,
relatedBy: NSLayoutRelation.Equal,
toItem: view,
attribute: NSLayoutAttribute.CenterX,
multiplier: 1.0,
constant: 0.0)
self.view.addConstraint(xConstraint)
let yConstraint = NSLayoutConstraint(item: label1,
attribute: NSLayoutAttribute.CenterY,
relatedBy: NSLayoutRelation.Equal,
toItem: view,
attribute: NSLayoutAttribute.CenterY,
multiplier: 1.0,
constant: 0)
self.view.addConstraint(yConstraint)
//Add leading and trailing margins if necessary (prevent long text content in label1 to be larger than screen)
let viewsDictionary = ["label1" : label1]
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-(>=10#750)-[label1]-(>=10#750)-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDictionary))
}

Related

NSLayoutConstraints with visual format doesn't work as expected when in UIScrollView? [duplicate]

This question already has answers here:
UIScrollView doesn't use autolayout constraints
(4 answers)
scroll view not scrolling vertically
(2 answers)
Closed 4 years ago.
I am putting a UILabel inside a UIScrollView, I'd like to add these constraints to the label:
Top margin is 20
Left margin is 20
Right margin is 20
Label's height is 40
Here's the code I wrote for these constraints using visual format:
let label = UILabel.init(frame: .zero)
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Some Text"
scrollView.addSubview(label)
let horizontalConstraints = NSLayoutConstraint.constraints(
withVisualFormat: "H:|-20-[label]-20-|",
options: [],
metrics: nil,
views: ["label": label]
)
let verticalConstraints = NSLayoutConstraint.constraints(
withVisualFormat: "V:|-20-[label(40)]",
options: [],
metrics: nil,
views: ["label": label]
)
NSLayoutConstraint.activate(horizontalConstraints + verticalConstraints)
But this picture is what I'm getting (can't upload photo because I have low reputations). I added borders to the views, the blue one is the border of UIScrollView, the red one is the border of the UILabel
Your code isn't correct. Here is how your code should looks. And if you use this way of adding constraints, you don't need to activate constraints with special method. These vertical and horizontal constraints have already activated.
//horizontal constraints
scrollView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-20-[v0]-20-|",
options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": label]))
//vertical constraints
scrollView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-20-[v0(40)]",
options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": label]))
Explanation of syntax:
"H", "V" - horizontal, vertical constraints
"|" - anchor, for example "H:|[v0]" - left anchor
"-20-" - offset, for example "V:|-20-[v0]" - top anchor with offset 20
"[v0(40)]" - this is the size of view, for example "V:[v0(40)]" - height is 40
"[v0]" - your first view in visual format. This format can set constraints for several views.
For example:
(format: "V:|-16-[v0]-8-[v1(44)]-16-[v2(1)]|", views: ["v0": firstView], ["v1": secondView], ["v2": thirdView])
I figured out. Although I do not know why this happens, whether it's a bug with UIScrollView or things just have to be done this way.
Apparently, when you add subviews to a UIScrollView in Story Board, you'll always get a warning saying that the position of the subviews are ambiguous. So, what you would do is to add a UIView to the scroll view and add these constraints to it:
Top = 0, Leading = 0, Trailing = 0 and Bottom = 0
UIView is centered both horizontally and vertically in the scroll view
After that, you're good to add subviews to UIView without getting those warnings.
Same reason applies here, when you want to add subviews to a UIScrollView programmatically, and also set up some constraints, you should put a UIView inside the scroll view and put all subviews inside the UIView:
let subviewContainer = UIView.init(frame: .zero)
subviewContainer.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(subviewContainer)
var constraints: [NSLayoutConstraint] = []
// Step 1, set up horizontal constraints to make leading & trailing = 0
constraints += NSLayoutConstraint.constraints(
withVisualFormat: "H:|-0-[subviewContainer]-0-|",
options: [],
metrics: nil,
views: ["subviewContainer": subviewContainer]
)
// Step 2, set up vertical constraints to make top & bottom = 0
constraints += NSLayoutConstraint.constraints(
withVisualFormat: "V:|-0-[subviewContainer]-0-|",
options: [],
metrics: nil,
views: ["subviewContainer": subviewContainer]
)
// **Step 3, center "subviewContainer" horizontally & vertically in scroll view**
constraints.append(
NSLayoutConstraint.init(
item: subviewContainer,
attribute: .centerX,
relatedBy: .equal,
toItem: scrollView,
attribute: .centerX,
multiplier: 1,
constant: 0
)
)
constraints.append(
NSLayoutConstraint.init(
item: subviewContainer,
attribute: .centerY,
relatedBy: .equal,
toItem: scrollView,
attribute: .centerY,
multiplier: 1,
constant: 0
)
)
// Step 4, activate all constraints
NSLayoutConstraint.activate(constraints)
After these 4 steps, you are now good to add subviews to "subviewContainer".

Swift - Xcode 8 - iOS 10 - Can't create simple constraint

EDIT - I fixed it. See answer.
I'm trying to learn how to create constraints programmatically. I tried to constrain a UILabel to the top of the screen, using this code, in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
//the variable label already exists. I make a UILabel.
label = UILabel()
label.text = "Hello"
label.backgroundColor = UIColor(red: 1, green: 1, blue: 0, alpha: 1)
//I add the label to the ViewController
view.addSubview(label)
//I use an array of constraints because I will make more later.
let constraints : [NSLayoutConstraint] = [
NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: topLayoutGuide, attribute: .bottom, multiplier: 1.0, constant: 0.0)
]
view.addConstraints(constraints)
}
When I run my app, I get an error beginning with:
[LayoutConstraints] Unable to simultaneously satisfy constraints.
My intended equation for the constraint was:
label.top = 1.0 * topLayoutGuide.bottom + 0.0
When I created what seems to be an identical constraint with Interface Builder, it worked. However, it doesn't when I try to do it programatically. What's wrong with what I'm doing, and how can I programmatically make a constraint using NSLayoutConstraint that will pin my Label to the top of the screen right below the status bar?
My sources:
Apple - Anatomy of a Constraint
Apple - Programmatically creating Constraints
StackOverflow - SWIFT | Adding constraints programmatically
This can be fixed by setting translatesAutoresizingMaskIntoConstraints to be false, to avoid clashes with existing Mask Constraints:
label.translatesAutoresizingMaskIntoConstraints = false

How to add NSConstraints to UITextView in Swift 3

So I have successfully converted almost all my project to Swift 3 besides from one View Controller where I manually add constraints.
I was getting errors for
view.addSubview(textView)
textView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(textView)
let views = ["textView": textView]
var constraints = NSLayoutConstraint.constraints(withVisualFormat: "V:|-70-[textView]-8-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-8-[textView]-8-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views)
NSLayoutConstraint.activate(constraints)
So I changed it to
textView.translatesAutoresizingMaskIntoConstraints = true
view.addSubview(textView)
textView.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
textView.autoresizingMask = [UIViewAutoresizing.flexibleLeftMargin, UIViewAutoresizing.flexibleRightMargin, UIViewAutoresizing.flexibleTopMargin, UIViewAutoresizing.flexibleBottomMargin]
All I am trying to do is add a UITextView to a UIView in a view controller so it takes up the entire height and width and stays vertically and horizontally in the centre. I'm clearly missing something obvious with my updated code and am unsure why my previous code was refusing to run as I worked in Swift 2.2.
Can anyone answer the correct way of adding constraints in Swift 3 ?
UPDATE:
Here's the error from my original code which was converted to Swift 3.0
2016-09-06 22:08:38.751636 APPNAME[570:65129] -[_SwiftValue
nsli_superitem]: unrecognized selector sent to instance 0x17044a0e0
2016-09-06 22:08:38.752963 APPNAME[570:65129] * Terminating app due
to uncaught exception 'NSInvalidArgumentException', reason:
'-[_SwiftValue nsli_superitem]: unrecognized selector sent to instance
0x17044a0e0'
* First throw call stack: (0x18430c1c0 0x182d4455c 0x184313278 0x184310278 0x18420a59c 0x184d42104 0x184d40948 0x184d3f79c
0x184d3f340 0x100221ec0 0x1002289ac 0x100228be8 0x18a0e5b08
0x18a19f4cc 0x18a19f3a4 0x18a19e6ec 0x18a19e138 0x18a19dcec
0x18a19dc50 0x18a0e2c78 0x1875ab40c 0x1875a00e8 0x18759ffa8
0x18751cc64 0x1875440d0 0x18a0d8348 0x1842b97dc 0x1842b740c
0x1842b789c 0x1841e6048 0x185c67198 0x18a150bd0 0x18a14b908
0x100171a04 0x1831c85b8) libc++abi.dylib: terminating with uncaught
exception of type NSException (lldb)
I had this error at the last line of the following code:
scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(scrollView)
contentView = UIView()
contentView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(contentView)
let viewDict = [
"contentView": contentView,
"scrollView": scrollView,
]
let vFormat = "V:|[contentView]|"
let constraints = NSLayoutConstraint.constraints(withVisualFormat: vFormat, options: [], metrics: nil, views: viewDict)
When I set the debugger at the last line, it shows the viewDict type as [String: UIView?] (notice the optional here).
The error does NOT occur when I change the declaration as follows:
let viewDict: [String: UIView] = [
Your first code sample has a syntax error in this line:
textView.addConstraints(constraints: [NSLayoutConstraint])
Presumably it's trying to create an empty array, but there's no point in adding an empty array of constraints, so just delete the line.
Note that your first code sample (with the erroneous line removed) doesn't meet your requirement (“takes up the entire height and width and stays vertically and horizontally in the centre”). You're specifying margins, so the text view won't take up the entire height and width, and the vertical margins are asymmetric, so it won't be centred vertically.
The problem in your second code sample is that you're using the wrong mask. For example, .flexibleLeftMargin tells the system to allow the distance between the left edge of view and the left edge of textView to change, but that is the opposite of what you want.
This should do what you want:
textView.frame = view.bounds
textView.autoresizingMask = [ .flexibleWidth, .flexibleWidth ]
If you want margins, set the frame like this:
var frame = view.bounds.insetBy(dx: 8, dy: 0)
frame.origin.y += 70
frame.size.height -= 78
textView.frame = frame
Note that the size of view must already be at least 16 by 78.
If you want to keep using translatesAutoresizingMaskIntoConstraints=false, rather than the accepted answer, I note the following:
1) I get the errors you get when I generate NSLayoutConstraints using the visual formatting approach - code that compiled file in iOS9.3 SDK and ran fine on devices even those running iOS10 - building with iOS10 SDK crashes with your errors
2) If you change this to manually create the NSLayoutConstraints, e.g. similar to this, then it works as expected:
var allConstraints = [NSLayoutConstraint]()
allConstraints.append(NSLayoutConstraint(item: slideView, attribute: .left, relatedBy: .equal, toItem: slideButton, attribute: .left, multiplier: 1.0, constant: 0.0))
allConstraints.append(NSLayoutConstraint(item: slideView, attribute: .right, relatedBy: .equal, toItem: slideButton, attribute: .right, multiplier: 1.0, constant: 0.0))
allConstraints.append(NSLayoutConstraint(item: slideView, attribute: .top, relatedBy: .equal, toItem: slideButton, attribute: .top, multiplier: 1.0, constant: 0.0))
allConstraints.append(NSLayoutConstraint(item: slideView, attribute: .bottom, relatedBy: .equal, toItem: slideButton, attribute: .bottom, multiplier: 1.0, constant: 0.0))
NSLayoutConstraint.activate(allConstraints)
I don't know what the issue with the more elegant-looking visual formatting, but it appears bust at the moment (or there has been a change to the order of view layout code in iOS between 9.3 and 10).

How do I put two views at the same position using "auto layout visual format"? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I want to put two views at the same position using "auto layout visual format" in Swift, how do I do that in an elegant way?
I succeeded using NSLayoutConstraint, but I hope I can still find an elegant way using visual format.
My code using NSLayoutConstraint:
NSLayoutConstraint(item: greenView, attribute: .Left, relatedBy: .Equal, toItem: redView, attribute: .Left, multiplier: 1.0, constant: 0).active = true
NSLayoutConstraint(item: greenView, attribute: .Top, relatedBy: .Equal, toItem: redView, attribute: .Top, multiplier: 1.0, constant: 0).active = true
NSLayoutConstraint(item: greenView, attribute: .Right, relatedBy: .Equal, toItem: redView, attribute: .Right, multiplier: 1.0, constant: 0).active = true
NSLayoutConstraint(item: greenView, attribute: .Bottom, relatedBy: .Equal, toItem: redView, attribute: .Bottom, multiplier: 1.0, constant: 0).active = true
I would echo what vacawama said in his excellent answer about using layout anchors. It's probably as clear and elegant as you get with the native API, but please note that it requires iOS 9 and later.
For the purpose of code organization, I'd like to suggest instead of looking for terser code, organize your code into methods and/or extensions, for example:
extension UIView {
func constraintsAligningAllEdges(toView view2: UIView) -> [NSLayoutConstraint] {
return [ topAnchor.constraintEqualToAnchor(view2.topAnchor),
bottomAnchor.constraintEqualToAnchor(view2.bottomAnchor),
leadingAnchor.constraintEqualToAnchor(view2.leadingAnchor),
trailingAnchor.constraintEqualToAnchor(view2.trailingAnchor) ]
}
}
Using it would look like this:
NSLayoutConstraint.activateConstraints(greenView.constraintsAligningAllEdges(toView: redView))
...or perhaps make that a class function extension for NSLayoutConstraint.
On the topic of Visual Format Language, vacawama also brought up a very interesting use of the layout options. Here's another possible way of achieving the same goal:
let views = ["greenView" : greenView, "redView" : redView]
NSLayoutConstraint.activateConstraints(
NSLayoutConstraint.constraintsWithVisualFormat("H:[greenView]-(0#1)-[redView]", options: [.AlignAllTop, .AlignAllBottom], metrics: nil, views: views) +
NSLayoutConstraint.constraintsWithVisualFormat("V:[greenView]-(0#1)-[redView]", options: [.AlignAllLeading, .AlignAllTrailing], metrics: nil, views: views)
)
What I've done here is instead of introducing extra views, I've introduced two extraneous constraints, one between green bottom & red top, another one between green trailing & top leading. They both have the lowest priority of 1 however, so as you would normally have other constraints deciding the size/position of either view, they should cause no harm.
Is this somewhat more elegant? I don't know, but personally I think layout anchors make more sense to me, and third-party frameworks like SnapKit are also fine options.
The Visual Format Language can't be used for every possible case. I believe aligning the two views to each other is such a case.
I would suggest you use layout anchors instead, like this:
override func viewDidLoad() {
super.viewDidLoad()
let redView = UIView()
redView.backgroundColor = .redColor()
redView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(redView)
let greenView = UIView()
greenView.backgroundColor = .greenColor()
greenView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(greenView)
let redHoriz = NSLayoutConstraint.constraintsWithVisualFormat("H:|-20-[redView]-20-|", options: [], metrics: nil, views: ["redView": redView])
NSLayoutConstraint.activateConstraints(redHoriz)
let redVert = NSLayoutConstraint.constraintsWithVisualFormat("V:|-50-[redView]-50-|", options: [], metrics: nil, views: ["redView": redView])
NSLayoutConstraint.activateConstraints(redVert)
// Set greenView to occupy same space as the redView
greenView.leftAnchor.constraintEqualToAnchor(redView.leftAnchor).active = true
greenView.rightAnchor.constraintEqualToAnchor(redView.rightAnchor).active = true
greenView.topAnchor.constraintEqualToAnchor(redView.topAnchor).active = true
greenView.bottomAnchor.constraintEqualToAnchor(redView.bottomAnchor).active = true
}
The Visual Format Language allows you to align parts of views. For instance, you can align the tops of bottoms of two views by passing [.AlignAllTop, .AlignAllBottom] as the options argument of constraintsWithFormat. But, you can only align values that are perpendicular to the direction of the visual format. So, if you are specifying a horizontal layout, then you can align the tops and bottoms of the views. If you are specifying a vertical layout, then you can align the lefts and rights.
You can align two views by introducing two additional views. In the example below, the blue view picks up the top and bottom alignment of the red view, and the yellow view picks up the left and right alignment of the red view. Then the green view gets aligned to the top and bottom of the blue view and the left and right of the yellow view, thus it is aligned to the red view.
Finally, hiding the blue and yellow views leaves you with the setup you desire.
let redView = UIView()
redView.backgroundColor = .redColor()
redView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(redView)
let greenView = UIView()
greenView.backgroundColor = .greenColor()
greenView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(greenView)
let blueView = UIView()
blueView.translatesAutoresizingMaskIntoConstraints = false
blueView.backgroundColor = .blueColor()
blueView.hidden = true
view.addSubview(blueView)
let yellowView = UIView()
yellowView.translatesAutoresizingMaskIntoConstraints = false
yellowView.backgroundColor = .yellowColor()
yellowView.hidden = true
view.addSubview(yellowView)
let redHoriz = NSLayoutConstraint.constraintsWithVisualFormat("H:|-80-[redView]-80-|", options: [], metrics: nil, views: ["redView": redView])
NSLayoutConstraint.activateConstraints(redHoriz)
let redVert = NSLayoutConstraint.constraintsWithVisualFormat("V:|-80-[redView]-80-|", options: [], metrics: nil, views: ["redView": redView])
NSLayoutConstraint.activateConstraints(redVert)
let blueHorizVert = NSLayoutConstraint.constraintsWithVisualFormat("H:[redView][blueView]|", options: [.AlignAllTop, .AlignAllBottom], metrics: nil, views: ["redView": redView, "blueView": blueView])
NSLayoutConstraint.activateConstraints(blueHorizVert)
let yellowHorizVert = NSLayoutConstraint.constraintsWithVisualFormat("V:[redView][yellowView]|", options: [.AlignAllLeft, .AlignAllRight], metrics: nil, views: ["redView": redView, "yellowView": yellowView])
NSLayoutConstraint.activateConstraints(yellowHorizVert)
let greenTopBottom = NSLayoutConstraint.constraintsWithVisualFormat("H:[greenView][blueView]|", options: [.AlignAllTop, .AlignAllBottom], metrics: nil, views: ["greenView": greenView, "blueView": blueView])
NSLayoutConstraint.activateConstraints(greenTopBottom)
let greenLeftRight = NSLayoutConstraint.constraintsWithVisualFormat("V:[greenView][yellowView]|", options: [.AlignAllLeft, .AlignAllRight], metrics: nil, views: ["greenView": greenView, "yellowView": yellowView])
NSLayoutConstraint.activateConstraints(greenLeftRight)
So you can do it with VFL, but it isn't elegant. I would suggest you stick with using anchors.

UIViews overlapping when adding multiple views dynamically to a tableViewCell in swift

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 : http://i.stack.imgur.com/M8itL.png
After i scroll down, scroll up again and long press: http://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
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
You could use UIStackViews to handle the constraints for you. You would:
Greatly reduce the boilerplate-constraint code in your class;
Simplify adding subviews;
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.
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!
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.

Resources