UITableViewCell with autolayout left margin different on iPhone and iPad
How to fix it
After fighting with the apple bug reporting team with many sample projects and screenshots and dissecting that answer, I've found that the solution to have your custom-style cells behave consistently regarding their margins and be just like the default UITableViewCells, you have to do the following (mostly based on Becky's answer, I've highlighted what's different and what made it work for me) :
Select your cell's content view in IB
Go to the Size Inspector
In the Layout Margins section, check Preserve Superview Margins (do not click the plus-sign)
(And here's the key) Do the same for the cell itself (the content view's parent if you will)
Setup your constraints as follows : Label.Leading = Superview.Leading Margin (with a constant of 0)
Now all your cells will have their label consistent with the default cells! This works for me in Xcode 7 and up and it includes the fix mentioned in the thread I referred to. IB and the simulator should now show properly aligned labels.
You could also do some of this programmatically, for example in the View Controller's class :
cell.preservesSuperviewLayoutMargins = true
cell.contentView.preservesSuperviewLayoutMargins = true
Or you could have it set up by calling UIAppearance once at startup (I only know Swift, sorry) :
UITableViewCell.appearance().preservesSuperviewLayoutMargins = true
UITableViewCell.appearance().contentView.preservesSuperviewLayoutMargins = true
How and why it works
As Ethan kindly pointed out, Apple's own documentation on UIView describes preservesSuperviewLayoutMargins
as follows :
When the value of this property is
true
, the superview’s margins are also considered when laying out content. This margin affects layouts where the distance between the edge of a view and its superview is smaller than the corresponding margin. For example, you might have a content view whose frame precisely matches the bounds of its superview. When any of the superview’s margins is inside the area represented by the content view and its own margins, UIKit adjusts the content view’s layout to respect the superview’s margins. The amount of the adjustment is the smallest amount needed to ensure that content is also inside the superview’s margins.
Therefore, if you want your cell's content to align with the TableView's margins (it's great-grandparent if you will), you need to have your content's two ascendants, Content View and the Table Cell itself, preserve the margins of their own superview.
Why this isn't default behavior surprises me : I feel like most developers who don't want to customize everything would expect this "inheritance" by default.
Avoid extra leading margin for UITableViewCell under iOS 11
I was able to solve this problem using the answer to UITableViewCell with autolayout left margin different on iPhone and iPad. Setting “Preserve Superview Margins” on both the table view cell and the content view it contains resolved the inconsistency between rendering in iOS 10 and iOS 11.
UILabel Autolayout ignores UITableViewCell's left margin on iOS 8.1 and 8.2
Update 04/26/2016
While the below strikethrough text is valid to some extend, it's not as simple as that. AutoLayout in UITableViewCells
is broken for iOS 8.1/8.2 and buggy for anything older than iOS 9. Period.
I created a small demo with two simple multi-line labels (over/under), both preserving superview margins. Under iOS 8.1/8.2 they don't preserve the superview margin with long text and the height is incorrect when rotated.
Here is the same problem, in a demo repo of an Apple UIKit developer Tyler Fox:
https://github.com/smileyborg/TableViewCellWithAutoLayoutiOS8
And my Issue on that Repo:
https://github.com/smileyborg/TableViewCellWithAutoLayoutiOS8/issues/39
Still seeking help..
Frederik
Answering my own question: As always the devil is in the detail.
The usual .ReloadData()
in viewWillAppear
is enough to fix AutoLayout for UITableView
with iOS 8 (especially 8.1/8.2), except - and that's the reason for all the AutoLayout bugs in my question:
When your are using a UITableView
inside a UISplitViewController
and changing UISplitViewController`s preferredPrimaryColumnWidthFraction
to something else larger than default (iirc 0.4).
Even when changing the primary column width just once, in a viewDidLoad()
with added splitViewController?.setNeedsLayout()
and splitViewController?.layoutIfNeeded()
the above misalignments will occur under iOS 8 if you use dynamic sized UITableViewCells
with UILabels
which show long texts.
Cheers, thank you for that long debugging session ;)
P.S. As a side note, I was able to observe a difference setting preservesSuperviewLayoutMargins
in IB compared to code, too. If you a struggling with turning them on, do it in IB. Setting them in awakeFromNib()
for the UITableViewCells
and it's contentView
behaves differently, at least for iOS 8 which we need to support for a while..
UITableViewCell margin inconsistency (Static cells)
The problem is that you have pinned your label's leading edge to the edge of the content view with a constant of 16. That is not how the other cells work, so you get different results.
Instead, pin your label's leading edge to the left margin of the content view, with a constant of 0. The left margin is 16 on a smaller device and 20 on a larger device, which is exactly the difference you're seeing for the other cells (because that is exactly how they are configured).
UITableViewCell margin left and right
From your screenshots it looks like you're trying to do this with a grouped table view. To do this you should use a UITableView
added to a UIViewController
not a UITableViewController
.
To set the inset you should just set constraints/frame of your table view to be slightly in from the left and right edge and set your view's background color to UIColor.groupTableViewBackgroundColor()
Then in cellForRowAtIndexPath
you can say something like:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cornerRadius:CGFloat = 5.0
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
// Configure your cell
let sectionCount = tableView.numberOfRowsInSection(indexPath.section)
let shapeLayer = CAShapeLayer()
cell.layer.mask = nil
if sectionCount > 1
{
switch indexPath.row {
case 0:
var bounds = cell.bounds
bounds.origin.y += 1.0
let bezierPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.TopLeft, .TopRight], cornerRadii: CGSize(width: cornerRadius,height: cornerRadius))
shapeLayer.path = bezierPath.CGPath
cell.layer.mask = shapeLayer
case sectionCount - 1:
var bounds = cell.bounds
bounds.size.height -= 1.0
let bezierPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.BottomLeft, .BottomRight], cornerRadii: CGSize(width: cornerRadius,height: cornerRadius))
shapeLayer.path = bezierPath.CGPath
cell.layer.mask = shapeLayer
default:
break
}
return cell
}
else
{
let bezierPath = UIBezierPath(roundedRect: CGRectInset(cell.bounds,0.0,2.0), cornerRadius: cornerRadius)
shapeLayer.path = bezierPath.CGPath
cell.layer.mask = shapeLayer
return cell
}
}
You just apply a mask based on the index path of the row and the number of rows in the section. If you have dynamically sized cells you will likely need to move applying the mask to your UITableViewCell
subclass.
You should get a result like:
Matching left alignment of custom & standard UITableViewCell types
I was having the same issue but the other answer couldn't help me as I was using static cells in UITableViewController. After a few trial-and-errors, I finally found a way to solve the separatorInset
issue on iPhone 6+ without a single line of code.
I simply align the left edge of the UILabel with value of 0 [Content View (current distance = 0)] from the left margin. And also change the UILabel's X position to get 0. (For my case, in Interface Builder, margin was 7, UILabel's X = 8). The alignment works well on iPhone 4S, 5S, 6 and 6+.
EDIT: This changed in XCode 7.
Additional step: Set "Preserve Superview Margins" of the respective cell's TableViewCell
and its contentView
to TRUE or check it in IB -> Size Inspector. Refer to @tebs1200's answer.
Hope this helps.
UITableViewCell looks different at runtime than in InterfaceBuilder (on iPad)
Note that generally there is (still) a bug in Storyboard, even in Xcode7,
you have to set the background color of cells in code, it won't work from storyboard
class YourCell:UITableViewCell
{
override func awakeFromNib()
{
super.awakeFromNib()
self.backgroundColor = UIColor.clearColor()
}
}
You may have another issue also, but this is one issue.
Related Topics
Remove Separatorinset on iOS 8 Uitableview for Xcode 6 iPhone Simulator
How to Regenerate iOS Folder in React Native Project
Immutable/Mutable Collections in Swift
Replacement for Stringbyaddingpercentescapesusingencoding in iOS9
Itms-90535 Unable to Publish iOS App with Latest Google Signin Sdk
How to Add Animation to Launch Screen in iOS 9.3 Using Objective C
Make App Appear as iOS 8 Suggested App at Lockscreen
Swift 3 Core Data Delete Object
Handling Private Frameworks in Xcode ≥ 7.3
Class Is Implemented in Both, One of the Two Will Be Used. Which One Is Undefined
How to Distinguish Between Locking the Device and Sending an App to Background
How to Shrink a Uipickerview on the Iphone
Nsdate Beginning of Day and End of Day
How to Set Adaptive Multiline Uilabel Text