Expandable UITableView Cell using Autolayout results in UIViewAlertForUnsatisfiableConstraints - ios

I am trying to make expandable cells using combination of UITableViewAutomaticDimension row height and Autolayout of the cells. To make things simple, I started with the following simple code, that I expected to work:
import UIKit
class ViewController: UITableViewController {
let cells = [Cell(), Cell(), Cell()]
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 50
for cell in cells {
cell.tableView = tableView
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return cells[indexPath.row]
}
}
class Cell: UITableViewCell {
var cons: NSLayoutConstraint?
var tableView: UITableView?
init() {
super.init(style: .default, reuseIdentifier: nil)
let button = UIButton()
contentView.addSubview(button)
button.addTarget(self, action: #selector(Cell.tapped(_:)), for: .touchUpInside)
button.setTitle("Press me", for: .normal)
button.setTitleColor(.red, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
// constraining the button to the view, so that buttons height would define cell height
let constraints = [
button.topAnchor.constraint(equalTo: contentView.topAnchor),
button.rightAnchor.constraint(equalTo: contentView.rightAnchor),
button.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
button.leftAnchor.constraint(equalTo: contentView.leftAnchor),
]
NSLayoutConstraint.activate(constraints)
cons = button.heightAnchor.constraint(equalToConstant: 100)
cons?.isActive = true
}
func tapped(_ sender: UIButton) {
// change the height of the button, thus of the contentView too (since the button is constrained to the contentView)
if cons?.constant == 100 {
cons?.constant = 200
} else {
cons?.constant = 100
}
// tell the tableView to redraw itself to apply the change
tableView?.beginUpdates()
tableView?.setNeedsDisplay()
tableView?.endUpdates()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Now this seems to be working, however, I still do get UIViewAlertForUnsatisfiableConstraints warning with the following message:
(
"<NSLayoutConstraint:0x17408c7b0 V:|-(0)-[UIButton:0x109e10290'Press me'] (active, names: '|':UITableViewCellContentView:0x109e0fd90 )>",
"<NSLayoutConstraint:0x17408c8a0 UIButton:0x109e10290'Press me'.bottom == UITableViewCellContentView:0x109e0fd90.bottom (active)>",
"<NSLayoutConstraint:0x17408c990 UIButton:0x109e10290'Press me'.height == 200 (active)>",
"<NSLayoutConstraint:0x17408db10 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x109e0fd90.height == 100 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x17408c990 UIButton:0x109e10290'Press me'.height == 200 (active)>
It seems that the estimated (or current) row height collides with the autolayout constraint provided by me. Still, refreshing the tableView using following code will display it as expected:
tableView?.beginUpdates()
tableView?.setNeedsDisplay()
tableView?.endUpdates()
How do I remove the warning?
I am aware that simple changing the priority of the height constraint to 999 will remove the warning, but it seems to me as a hack to remove it, not as a solution. If I am mistaken, please explain. Thanks.

Setting the height constraint priority to 999 may feel like a hack, but according to Apple's docs:
NOTE Don’t feel obligated to use all 1000 priority values. In fact, priorities should general cluster around the system-defined low (250), medium (500), high (750), and required (1000) priorities. You may need to make constraints that are one or two points higher or lower than these values, to help prevent ties. If you’re going much beyond that, you probably want to reexamine your layout’s logic.
Probably a little less than intuitive:
"I want the button height to control the cell height, so lower its priority????"
But, that does appear to be the "correct" way to do it.
Ref: https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/AnatomyofaConstraint.html#//apple_ref/doc/uid/TP40010853-CH9-SW19

Remove the height constraint i.e 200 as it is conflicting with top and bottom constraint.
Hope it helps..

Related

Add/remove subview and constraints in UITableViewCell on button click

I have a UITableView that I'm using to show an array of custom objects. Each object has several properties including a Boolean property that indicates if this item is new or not.
My UITableViewCell content view is defined in the storyboard and has an initial layout similar to this:
In my UITableViewController, when I dequeue my cells, I call a method on my UITableViewCell that configures the data to be displayed in the cell before I return it. One of the properties that I check is the .isNew property that I mentioned previously. If this value is true, then I am creating a UIButton and inserting it as a subview in the cell's content view so I end up with something like this:
Just for context, this button will show a "new" image to indicate that this item is new. I am also hooking up a method that will fire when the button is tapped. That method is also defined in my UITableViewCell and looks like this:
#objc func newIndicatorButtonTapped(sender: UIButton!) {
// call delegate method and pass this cell as the argument
delegate?.newIndicatorButtonTapped(cell: self)
}
I have also created a protocol that defines a delegate method. My UITableViewController conforms to this and I see that code fire when I tap on the button in my cell(s). Here's is the delegate method (defined in an extension on my UITableViewController):
func newIndicatorButtonTapped(cell: UITableViewCell) {
if let indexPath = self.tableView.indexPath(for: cell) {
print(indexPath.row)
}
}
I see the row from the indexPath print out correctly in Xcode when I tap on the cell. When my user taps on this button, I need to remove it (the button) and update the constraint for my UILabel so that is aligned again with the leading edge of the content view as shown in the first mockup above. Unfortunately, I seem to be running into an issue with cell recycling because the UIButton is disappearing and re-appearing in different cells as I scroll through them. Do I need to reset the cell's layout/appearance before it gets recycled or am I misunderstanding something about how cell recycling works? Any tips would be much appreciated!
What you may be thinking is that you get a "fresh" cell, but when a cell gets re-cycled that means it gets re-used.
You can see this very easily by changing the text color of a basic cell.
For example:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyID", for: indexPath) as! MyCustomCell
if indexPath.row == 3 {
cell.theLabel.textColor = .red
}
return cell
}
As you would expect, when the table first loads the text color will change for the 4th row (row indexing is zero-based).
However, suppose you have 100 rows? As you scroll, the cells will be re-used ... and each time that original-4th-cell gets re-used, it will still have red text.
So, as you guessed, yes... you need to "reset" your cell to its original layout / content / colors / etc each time you want to use it:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyID", for: indexPath) as! MyCustomCell
if indexPath.row == 3 {
cell.theLabel.textColor = .red
} else {
cell.theLabel.textColor = .black
}
return cell
}
You may want to consider to have the button hidden and then change the layout when it is clicked.
Firing the action from the cell to the tableView with a protocol and then reseting the layout at cell reuse is a good way to do it
Doing it in a cell fully programatic would be like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! Cell
cell.isNew = indexPath.row == 0 ? true : false
cell.layoutIfNeeded()
return cell
}
And the cell class needs to be similar to: (you can do what you need by changing the Autolayout constraint, manipulating the frame directly or using a UIStackView)
class Cell: UITableViewCell {
var isNew: Bool = false {
didSet {
if isNew {
button.isHidden = true
leftConstraint.constant = 20
} else {
button.isHidden = false
leftConstraint.constant = 100
}
self.setNeedsLayout()
}
}
var button: UIButton!
var label: UILabel!
var leftConstraint: NSLayoutConstraint!
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
button = UIButton(type: .system)
button.setTitle("Click", for: .normal)
self.contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.widthAnchor.constraint(equalToConstant: 50).isActive = true
button.heightAnchor.constraint(equalToConstant: 10).isActive = true
button.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 20).isActive = true
button.topAnchor.constraint(equalTo: self.topAnchor, constant: 20).isActive = true
label = UILabel()
label.text = "Label"
self.contentView.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.widthAnchor.constraint(equalToConstant: 200).isActive = true
label.heightAnchor.constraint(equalToConstant: 20).isActive = true
label.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
leftConstraint = label.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 100)
leftConstraint.isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

UITableView inside UIView

I'm trying to create a dynamic UITableView in a xib, so what I think might work is to put the table view inside a blank UIView. Then I would subclass this UIView and make it adhere to the protocols UITableViewDelegate and UITableViewDataSource. Can someone guide me with this, because I've tried many things, but none of them worked. Many Thanks!
EDIT:
I'll show you what I tried before (sorry, don't have the original code):
class TableControllerView: UIView, UITableViewDelegate, UITableViewDataSource {
let tableView = UITableView()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
tableView.frame = self.frame
tableView.delegate = self
tableView.dataSource = self
}
func tableView(...cellForRowAt...) {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.titleLabel?.text = "test title"
return cell
}
}
but in this case the table view ended up being too big and not aligned with the view, even though I set it's frame to be the same as the view
EDIT 2 :
My code now looks like this:
class PharmacyTableView: UIView, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var pharmacyTableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//PROVISOIRE depends on user [medications].count
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)
cell.textLabel?.text = "text label test"
cell.detailTextLabel?.text = "detail label test"
return cell
}}
table view is initialized but only shows up when height anchor is constrained, which I don't want because it might grow or shrink depending on user data. I guess I'll be done after solving this?
P.S. : Also, thank you very much to the people that took the time to help me :)
EDIT 3:
So, I've changed the class of the table view to this:
class IntrinsicResizingTableView: UITableView {
override var contentSize:CGSize {
didSet {
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return CGSize(width: UIViewNoIntrinsicMetric, height: contentSize.height)
}
}
And now everything works fine! Finally!
Your question is unclear. Based on your final statement the following code is what you should use to keep the table view and its superview aligned.
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
If this is not the answer you're looking for, please clarify your problem. Also it would help if you explain what you're doing this for? Why do you need this superview?

How to adjust left and right insets or margins for UITableView tableHeaderView?

I have a "plain" style UITableView. I am setting a view as the tableViewHeader for the table view. The table also shows the section index down the right side.
My issue is figuring out how to inset the left and right edge of the header view to take into account safe area insets if run on an iPhone X (in landscape) and the table view's section index (if there is one).
I created a simple test app that adds a few dummy rows, a section header, and the section index.
Here is my code for creating a simple header view using a UILabel. My real app won't be using a label but a custom view.
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 30)
label.backgroundColor = .green
label.text = "This is a really long Table Header label to make sure it is working properly."
label.sizeToFit()
tableView.tableHeaderView = label
Without any special attempts to fix the left and right edges, the result in the iPhone X simulator is as follows:
Portait:
Landscape:
Note that without any extra effort, the cells and section header get the desired insets but the header view does not.
I'd like the left edge of the header view to line up with the left edge of the section header and the cells.
I'd like the right edge of the header view to stop where it meets the left edge of the section index. Note that the portrait picture seems like it is already do that but if you look close you can tell the header view goes all the way to the right edge of the table view. You can't see the third . of the ellipses and you can barely see the green behind the section title view.
One attempt I've made was to add the following to my table view controller:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let header = tableView.tableHeaderView {
var insets = header.layoutMargins
insets.left = tableView.layoutMargins.left
insets.right = tableView.layoutMargins.right
header.layoutMargins = insets
}
}
That code has no effect.
What properties do I set to ensure the header view's left and right edges are indented as needed? Are there constraints that should be applied?
Please note that I'm doing everything in code. So please don't post any solutions that require storyboards or xib files. Answers in either Swift or Objective-C are welcome.
For anyone that wants to replicate this, create a new single view project. Adjust the main storyboard to use a UITableViewController instead of a plan UIViewController and use the following for ViewController:
import UIKit
class ViewController: UITableViewController {
// MARK: - UITableViewController methods
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Row \(indexPath.row)"
cell.accessoryType = .disclosureIndicator
return cell
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Section Header"
}
override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
let coll = UILocalizedIndexedCollation.current()
return coll.sectionIndexTitles
}
override func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
return index
}
// MARK: - UIViewController methods
override func viewDidLoad() {
super.viewDidLoad()
tableView.sectionIndexMinimumDisplayRowCount = 1
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 30)
label.backgroundColor = .green
label.text = "This is a really long Table Header label to make sure it is working properly."
label.sizeToFit()
tableView.tableHeaderView = label
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let header = tableView.tableHeaderView {
var insets = header.layoutMargins
insets.left = tableView.layoutMargins.left
insets.right = tableView.layoutMargins.right
header.layoutMargins = insets
}
}
}
For UITableViews without section index:
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
label.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
])
For UITableViews with section index:
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
label.trailingAnchor.constraint(equalTo: tableView.layoutMarginsGuide.trailingAnchor)
])
You need to add some Auto Layout constraints to the label after you add it to the tableview:
…
tableView.tableHeaderView = label
//Add this
label.translatesAutoresizingMaskIntoConstraints = false
label.leadingAnchor.constraint(equalTo: (label.superview?.safeAreaLayoutGuide.leadingAnchor)!).isActive = true
label.trailingAnchor.constraint(equalTo: (label.superview?.safeAreaLayoutGuide.trailingAnchor)!).isActive = true
Also, if you want to see all the text in the label use label.numberOfLines = 0.
You can get rid of the code you added to viewDidLayoutSubviews.
Update:
For fun I did some experimenting in a playground and found that using the layoutMarginsGuide didn't push the trailing edge of the header label far enough over (I'm thinking it comes out right on iPhone X but maybe not on all devices, or the playground behaves a bit differently). I did find though that for table views with at least one cell already in place I could use aCell.contentView.bounds.width, subtract the table view's width and divide the result by two and the header label would sit very nicely next to the section index. As a result I wrote a helper function for setting up constraints. The table view is optional so the function can be used with any view that has a superview and needs to keep inside the safe area. If a table view is passed in it can have a section index or not but it does need to have at least one cell at row 0 section 0:
func constrain(view: UIView, inTableView aTableView: UITableView?) {
guard let container = view.superview else {
print("Constrain error! View must have a superview to be constrained!!")
return
}
view.translatesAutoresizingMaskIntoConstraints = false
view.leadingAnchor.constraint(equalTo: container.safeAreaLayoutGuide.leadingAnchor).isActive = true
if let table = aTableView, let aCell = table.cellForRow(at: IndexPath(row: 0, section: 0)) {
let tableWidth = table.bounds.width
let cellWidth = aCell.contentView.bounds.width
view.trailingAnchor.constraint(equalTo: table.safeAreaLayoutGuide.trailingAnchor, constant: cellWidth - tableWidth).isActive = true
} else {
view.trailingAnchor.constraint(equalTo: container.safeAreaLayoutGuide.trailingAnchor).isActive = true
}
}
I did find one issue when using this. When using a label set to 0 lines with your text it covers the first section header and the first cell of that section. It takes a bit of scrolling to get them out from under the header too. Clipping the label to one line works out quite well though.

UIView's not being added to contentView?

I'm trying to add some views in code to the contentView of a table view cell. However nothing shows up. I get an empty cell. Below is the code of me adding subviews to the content view of a custom cell.
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupViews()
}
func setupViews() {
//feedback_title setup
feedback_title.text = Constants.IndividualStudiesPage.FEEDBACK_TITLE
feedback_title.numberOfLines = 0
feedback_title.lineBreakMode = .ByTruncatingTail
feedback_title.preferredMaxLayoutWidth = standard_height_width
//feedback_box setup
feedback_box.text = Constants.IndividualStudiesPage.DEFAULT_FEEDBACK
// TODO: Change to infinte (0) number of lines. Height constraint interferes with this
feedback_box.numberOfLines = 2
feedback_box.preferredMaxLayoutWidth = standard_height_width
contentView.addSubview(feedback_title)
contentView.addSubview(feedback_box)
}
Here's the interesting thing. When I add some dummy view like an empty UILabel to the content view in the storyboard, then everything shows up including the views I added in the code. Here's a picture to show you what I mean:
But when I don't add any dummy view then nothing shows up...Why and how can I fix this? Thanks in advance.
EDIT:
The feedback_cell and feedback_box are instantiated like so at the top of the class
class FeedbackCell: UITableViewCell {
var feedback_title : UILabel = UILabel.newAutoLayoutView()
var feedback_box : UILabel = UILabel.newAutoLayoutView()
newAutoLayoutView is a PureLayout function that essentially just instantiates the labels to empty views. I then update the AutoLayout constraints, also using PureLayout in the updateConstraints function:
override func updateConstraints() {
if !did_update_constraints {
//prevent labels from being compressed below intrinsic height but also from taking too much height
NSLayoutConstraint.autoSetPriority(UILayoutPriorityRequired){
self.feedback_title.autoSetContentHuggingPriorityForAxis(.Vertical)
self.feedback_title.autoSetContentCompressionResistancePriorityForAxis(.Vertical)
self.feedback_box.autoSetContentHuggingPriorityForAxis(.Vertical)
self.feedback_box.autoSetContentCompressionResistancePriorityForAxis(.Vertical)
}
//height constraints
feedback_title.autoSetDimension(.Height, toSize: standard_height_width)
//feedback_title constraints
feedback_title.autoPinEdgeToSuperviewMargin(.Leading)
feedback_title.autoPinEdgeToSuperviewMargin(.Trailing, relation: NSLayoutRelation.LessThanOrEqual)
feedback_title.autoPinEdgeToSuperviewMargin(.Top)
//feedback_box constraints
feedback_box.autoPinEdge(.Top, toEdge: .Bottom, ofView: feedback_title, withOffset: 10, relation: .GreaterThanOrEqual)
feedback_box.autoPinEdgeToSuperviewMargin(.Leading)
feedback_box.autoPinEdgeToSuperviewMargin(.Trailing, relation: NSLayoutRelation.LessThanOrEqual)
feedback_box.autoPinEdgeToSuperviewMargin(.Bottom)
did_update_constraints = true
}
super.updateConstraints()
View hierarchy debugger:
Tableview code:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch (indexPath.row) {
case 0:
return setupDescriptionCell(tableView, indexPath: indexPath)
case 1:
return setupFeedbackCell(tableView, indexPath: indexPath)
case 2:
return setupStatsCell(tableView, indexPath: indexPath)
case 3:
return setupSurveyCell(tableView, indexPath: indexPath)
default:
return setupDefaultCell(tableView, indexPath: indexPath)
}
}
func setupFeedbackCell(tableView : UITableView, indexPath : NSIndexPath)->UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cell_IDs[1], forIndexPath: indexPath) as! FeedbackCell
//in the future download feedback in view did load and adjust here
return cell
}

Using auto layout in UITableviewCell

I am trying to use auto sizing UITableView cells in swift with snapKit! relevant flags set on the UITableView are as follows:
self.rowHeight = UITableViewAutomaticDimension
self.estimatedRowHeight = 70.0
I have a UITextField defined in my customUITableviewCell class like:
var uidTextField: UITextField = UITextField()
and the initial setup of the text field in my custom UITableViewCell looks like this:
self.contentView.addSubview(uidTextField)
uidTextField.attributedPlaceholder = NSAttributedString(string: "Woo Hoo", attributes: [NSForegroundColorAttributeName:UIColor.lightGrayColor()])
uidTextField.textAlignment = NSTextAlignment.Left
uidTextField.font = UIFont.systemFontOfSize(19)
uidTextField.returnKeyType = UIReturnKeyType.Done
uidTextField.autocorrectionType = UITextAutocorrectionType.No
uidTextField.delegate = self
uidTextField.addTarget(self, action: "uidFieldChanged", forControlEvents: UIControlEvents.EditingChanged)
uidTextField.snp_makeConstraints { make in
make.left.equalTo(self.contentView).offset(10)
make.right.equalTo(self.contentView)
make.top.equalTo(self.contentView).offset(10)
make.bottom.equalTo(self.contentView).offset(10)
}
when I run the code it shows up cut off and gives me an error in the console that reads:
Warning once only: Detected a case where constraints ambiguously
suggest a height of zero for a tableview cell's content view. We're
considering the collapse unintentional and using standard height
instead.
Is there something wrong with my autoLayout constraints or is this an issue with UIControls and autosizing of UITableView cells?
In SnapKit (and Masonry) you have to use negative values to add a padding to the right or bottom of a view. You are using offset(10) on your bottom constraint which causes the effect that the bottom 10pt of your text field will get cut off.
To fix this you have to give your bottom constraint a negative offset:
uidTextField.snp_makeConstraints { make in
make.left.equalTo(self.contentView).offset(10)
make.right.equalTo(self.contentView)
make.top.equalTo(self.contentView).offset(10)
make.bottom.equalTo(self.contentView).offset(-10)
}
Or you could get the same constraints by doing this:
uidTextField.snp_makeConstraints { make in
make.edges.equalTo(contentView).inset(UIEdgeInsetsMake(10, 10, 10, 0))
}
When you are using the inset() way you have to use positive values for right and bottom inset.
I don't understand why SnapKit uses negative values for bottom and right. I think that's counterintuitive and a bit confusing.
EDIT: This is a little example that is working fine (I hardcoded a tableView with 3 custom cells that include a UITextField):
ViewController:
import UIKit
import SnapKit
class ViewController: UIViewController {
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
tableView.dataSource = self
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 70
tableView.registerClass(CustomTableViewCell.self, forCellReuseIdentifier: "CustomCell")
tableView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(view)
}
}
}
extension ViewController: UITableViewDataSource {
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath)
return cell
}
}
CustomTableViewCell:
import UIKit
class CustomTableViewCell: UITableViewCell {
let textField = UITextField()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
textField.attributedPlaceholder = NSAttributedString(string: "Woo Hoo", attributes: [NSForegroundColorAttributeName:UIColor.lightGrayColor()])
textField.textAlignment = NSTextAlignment.Left
textField.font = UIFont.systemFontOfSize(19)
textField.returnKeyType = UIReturnKeyType.Done
textField.autocorrectionType = UITextAutocorrectionType.No
contentView.addSubview(textField)
textField.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(contentView).inset(UIEdgeInsetsMake(10, 10, 10, 0))
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Resources