UITableViewCell detail text label improper layout - ios

I have UITableViewCell subclass that insets the content view. When the content view is inset the text label is okay but the detail text label becomes out of frame and has does not adjust itself to the new content view frame.
Screen shot of cell
I have tried calling setNeedsLayout to tell the layout engine to adjust the layout of all the subviews but issue has still persisted. Below is the code to inset the content view in my UITableViewCell subclass
override func layoutSubviews() {
super.layoutSubviews()
contentView.frame = contentView.frame.inset(by: UIEdgeInsets.custom)
contentView.layer.cornerRadius = CornerRadius.defaultRadius
}
enum CornerRadius {
static let defaultRadius: CGFloat = 10
}

Issue has been resloved by changing the frame then calling
super.layoutSubviews()
This has to be called after you make the changes to the frame.

Related

Make UICollectionViewCell's height match its content (sum height of all sub views) with AutoLayout

I found the next answer to make UIView's height match its content https://stackoverflow.com/a/39527226/7767664
I tested it, it works fine (if UIView height size in storyboard bigger or smaller than its content then during runtime it autoresize itself to match the content).
But if I use UICollectionViewCell instead of UIView then nothing changes, height of cell is never changed, it always has the hardcoded height we have in storyboard properties:
What else can I do?
Also I have 2 sections in UIControllerView (2 columns).
Two cells in one row should have the same size even if their size content is different (something like this implemented in Android natively when using RecyclerView with GridLayoutManager, very easy)
Update
It works with UIView because I set its top constraint to Safe Are'a top
I can't do it with UICollectionViewCell
Update 2
It seems I have some progress with this answer https://stackoverflow.com/a/25896386/7767664
But instead of newFrame.size.width = CGFloat(ceilf(Float(size.width))) I need newFrame.size.height = CGFloat(ceilf(Float(size.height)))
and when we use this solution, don't add any constraints to cell's bottom otherwise it will not work
With this solution I can't really use any margins otherwise some part of becomes invisible at the bottom of cell
I guess it can be solved with this question https://stackoverflow.com/a/31279726/7767664
You can try with this function called inside you collectionView Extension or inside your native collectionViewController class:
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
//return something like the size. I write you an example how to use it.
// You can easily change the value according to your stuff contents height.
return CGSize(width: collectionView.frame.size.width/3, height: 100)
}
I solved my issue thanks to https://stackoverflow.com/a/25896386/7767664 and https://stackoverflow.com/a/31279726/7767664
I decided to put one UIView with all needed child views inside it to UICollecitonViewCell
I set UIView trailing, leading, top to ICollecitonViewCell but I didn't set bottom to cell view (you should use the latest child view inside UIView and connect their bottoms, not with the cell view)
Then I added reference of UIView to my custom cell class and I use its height for cell's height:
public class MyCustomItemCell: UICollectionViewCell {
// other child views references ...
#IBOutlet weak var wrapperView: UIView! // view which contains your other views
//forces the system to do one layout pass
var isHeightCalculated: Bool = false
override public func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
//Exhibit A - We need to cache our calculation to prevent a crash.
if !isHeightCalculated {
setNeedsLayout()
layoutIfNeeded()
var newFrame = layoutAttributes.frame
newFrame.size.height = wrapperView.frame.height // use height of our UIView
layoutAttributes.frame = newFrame
isHeightCalculated = true
}
return layoutAttributes
}
}

Changing frame and contentInset of UICollectionView in layoutSubviews() using IGListKit

I need to change the frame and top content inset (contentInset.top) of my collection view (which is a UICollectionView). The top inset and frame changes depend on the bounds of the superview and the content offset of the collection view, thus I put the inset-changing code in layoutSubviews().
override func layoutSubviews() {
super.layoutSubviews()
collectionView.collectionViewLayout.invalidateLayout()
collectionView.contentInset.top = new_inset_top
collectionView.frame = new_frame
}
However, the collection view does not account for the new insets and the log shows the following:
The behavior of the UICollectionViewFlowLayout is not defined because:
the item height must be less than the height of the UICollectionView
minus the section insets top and bottom values, minus the content
insets top and bottom values.
How can I fix this to let the collection view displays correctly?
I am using IGListKit
You do not need to do all these inside of layoutSubviews.
in viewDidLoad implement it like following
func viewDidLoad() {
super.viewDidLoad()
// let say you want 40px top inset
collectionView.contentInset = UIEdgeInsetsMake(40, 0, 0, 0)
}
Another thing is that, i don't get, why are you setting the frame in layoutSubviews. There is no need to set frame to change the contentInset.

Autolayout working wrong

I'm working on profile screen, there is a UITableView inside the ViewController and I placed all user info inside the UITableView Header. To make screen more accurate due to different sizes of "About" label I use Autolayout with this code:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
sizeHeaderToFit()
}
func sizeHeaderToFit() {
let headerView = tableView.tableHeaderView!
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
var frame = headerView.frame
frame.size.height = height
headerView.frame = frame
tableView.tableHeaderView = headerView
}
Everything works fine, but the last string of About label is not showing, text interrupts (I set word wrap):
Add a height constraint on tableview header and try to change height constraint where you are changing frame. As you are using autolayout so you should play with the constraints rather than playing directly with the frames
you can follow this tutorial
https://useyourloaf.com/blog/variable-height-table-view-header/
Hope it will help you.

Finding the size of a view adjusted by a UIStackView swift

A number of views in a UIStackView are adjusted to fit the stack. The views are initialised with no frame because they are resized by the stack view. Is there a way which I can get the size of the views after they have been resized by the stack view?
The sizes are available after UIStackView.layoutSubviews() finishes. You can subclass UIStackView and override layoutSubviews:
class MyStackView: UIStackView {
override func layoutSubviews() {
super.layoutSubviews()
print("arrangedSubviews now have correct frames")
// Post a notification...
// Call a method on an outlet...
// etc.
}
}
Yes. In your View's layoutSubviews().
However you need to force the UIStackView to layout first, using stack.layoutIfNeeded() before using its size.
eg:
public override func layoutSubviews() {
super.layoutSubviews()
// Force the UIStackView to layout, in order to get the updated width.
stack.layoutIfNeeded()
let tabWidth = stack.arrangedSubviews[0].frame.size.width
}
I think the better way to get a Stackview's subview frame is to use convert.
yourStackView.convert(viewInsideStackView.frame to: parentView)
This will return viewInsideStackView's frame inside parentView's coordinate system.

Can't center views in tableHeaderView with auto layout and storyboards

I can't center a view that has been placed as a subview of a UITableView's tableHeaderView in a storyboard using auto layout.
In a storyboard, I have a UITableView. I dragged a UIView (the red view) to the top, released, and it created a table header view automatically. I then dragged and dropped another UIView (the yellow view) on top of the table header view, resized, and applied some constraints to ensure it stays centered:
When I run the app on the simulator, here's what I get:
The yellow view is obviously not centered. However, the "Filter" button at the bottom is.
I know it's tricky to get the height right using auto layout and storyboards and table header views (and you can see that the height of the red view is definitely incorrect), but at this point, I'm just trying to solve for horizontally centering my yellow view.
Am I able to set this all up in my storyboard without having to configure my constraints in code?
Make sure that your UITableView has the leading, trailing, bottom, top constraints set up against its superview.
Check the table header view and all sub views have Autoresize Subviews enabled:
You can also force the table to re-render the header view by re-setting it to the same view:
[self.tableView setTableHeaderView:self.outletToHeaderView];
Update: to resize the table header view, give give it an appropriate frame in viewWillAppear:
CGRect newFrame = self.outletToHeaderView.frame;
newFrame.size.width = self.tableView.bounds.size.width;
newFrame.size.height = 44;
[self.outletToHeaderView setFrame:newFrame];
// Then reset it to force the table view to re-render/accommodate
[self.tableView setTableHeaderView:self.outletToHeaderView]
In your file TableViewHeader:
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
yourView.translatesAutoresizingMaskIntoConstraints = false
yourView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
}
You need to use prototype cell from table view, than dequeue it with reusable identifier and return it contentView. Only that's do the trick
var headerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.headerView = (self.tableView.dequeueReusableCellWithIdentifier("CustomHeaderCell") as! UITableViewCell).contentView
}
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return self.headerView
}
upd:
Oh sorry, my example is written in Swift. But it's easy to understand how to do the same in Obj-C

Resources