Multiline label in UIView vs UITableViewCell - ios

I created a custom UITableViewCell with two labels and a button.
It behaved perfectly as expected, and looked like this:
Later, I realised I needed to use this particular view multiple places, one of which was not in a TableView. Therefore I decided to delete my cell, and create a custom UIView instead - and create an empty custom UITableViewCell only containing this custom view - constrained to 0 in all directions.
With the exact same elements and constraints, the UIView turned out like this when shown in a TableView as a cell:
Everything is identical. NumberOfLines is set to 0, all the constraints are the same.
I also observed something weird, that when I rotated the entire phone to landscape, it turned out like this:
This is exactly how I'd like it to look when in landscape.
If I now turn it back to portrait, suddenly it looks like this:
Now it suddenly looks exactly like I want it to look in portrait.. I just had to flip it to landscape and back to portrait.. Why is that?..
I played around with some variables, among them: preferredMaxLayoutWidth, and found that if I set this value to the size of the screen (minus the 16*2px padding) it looks as expected at first launch in portrait. However, I don't want to set this value. There is no magic number in px that will be right. The whole point of this is to get it fit properly on all screen sizes and orientations. I probably could re-set the preferred width every time superview's layoutSubviews or something is called, but I figured there has to be a solution for this..? Why did it work for UITableViewCell and not for UIView inside a UITableViewCell?
I have tried all sorts of sizeToFit etc., and don't find any questions or answers that targets this particular difference in UIView and UITableViewCell..

The problem here is a bit complex but I will try to explain it... When the view lays out, it initially does so independently of its container and then reports its height to the system when the system needs to determine the height of the cell, which happens before the cell is constructed. Once the cell exists and the device is rotated, the view "realizes" it's in a cell and constraints itself and reports its height appropriately.
Sadly, this is an issue in the table view system stemming from legacy issues with how cells were originally sized back before constraints existed.
The simplest solution is to put the labels and button back directly in the cell. Another option might be to make your external view a table view cell. You can instantiate a table view cell outside of a table view for that one spot it's needed in. Another option is to implement the table view delegate's tableView:heightForRowAtIndexPath: method and manually determine the size.

I am assuming that you are using following method for table view's height:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
So, Just add all your view's in stack and set top, bottom, leading and trailing constraints of UIStackView with respect to UIView of xib as:

Related

Setting UITableViewCell height based on UITableViewHeight BEFORE drawing

iOS 13 (but seems to hold true in iOS 10.3, also).
I'm having a very hard time getting the final height of a view in a complex view hierarchy before anything is drawn to the screen. I have a UIPageViewControl with a series of custom UIViewController. One of the subviews in that is a table view, and I want to adjust the cell height based on the table view's height so that I have an integral number of cells showing.
Unfortunately, the final height of the table view is not determined, it seems, until after all hooks have been exhausted. I've tried all of the following: viewDidLoad(), viewWill/DidAppear(), viewDidLayoutSubviews(), layoutSubviews() and didMoveToWindow() (on the table view and the root view). I've tried calling layoutIfNeeded() in each of the above.
In the end, well after didMoveToWindow() (which comes after viewDidAppear, annoyingly), layoutSubviews() gets called a couple more times, and it’s only in those last two calls that the view has its final height. At this point it’s not possible to set the cell height and reload the table view without it visibly changing on-screen.
There seems to be no way to accomplish this.
Am I missing something? I guess I can try UICollectionView instead.

Only first collectionViewCell reacts to user interactions

The setup
I have a rather complex view hierarchy which made me do terrible things with tableViews and collectionViews. Right now I'm having a regular grouped TableView with custom TableCells. TableCell itself contains a couple of views and a Collection View. The height of the table cell is calculated based on number of items in the data source. On its creation table cell creates a collection view with a calculated size to fit necessary data.
-- UITableView
---- UITableViewCell
------ UICollectionView
--------- UICollectionViewCell
The problem
I've encountered unusual problem with a custom collectionViewCell. I have a vertical single-column collectionView with dynamic amount of cells. Ideally tapping on the cell should call didSelectItemAt. The cell also has three buttons. Tapping on the button should trigger some action. All of the desired functions work only for a first cell. The rest of the cells are not responsive to any actions.
Things that look strange
By default the scrolling of a collectionView inside of the tableViewCell is disabled because it basically fits all the content based on calculated height and doesn't require scrolling. (Also I don't want it to interfere with tableView scrolling logic).
First
I've tried to hardcode some value for the height of collectionView and enable scrolling. What happened is a mystery for me.
Let's say that calculated height required for the collectionView to show all the content without scrolling is 740. When I manually set it to be 280 (this is enough for exactly 2 cells to fit) and enabled scrolling my first cell were still working, but also when I scrolled collection just a little bit my second cell started to act normally as well. When I scrolled back to the top of the collectionView it was disabled once again.
So it looks like when the scrolling is enabled and will actually occur because of insufficient height to fit the content, cells behave as they should. As soon as I set height of the collectionView to be enough to fit its content, things go wrong.
Second
In some cases I can actually tap on the second cell and it will call the delegate. But the weird thing is it works when I tap in the top area of the cell, like 10pts from the top. The other areas of the cell are unresponsive so are the rest of the cells in the collection.
The working delegates and buttons with enabled scrolling forces to think that this has nothing to do with delayed or canceled touches. The frame for collectionView and height of the table cell are calculated properly as well.
xCode 8.3, iOS9+
As it turned out when I was creating my tableViewCell, I hardcoded the height of the collection to value of 214. The reason for that is very simple. I create a cell programmatically and with tableViewCells created programmatically the height of the cell is always 44. When you override init(style: UITableViewCellStyle, reuseIdentifier: String?)
You need to either hardcore the height, or update it in cellForRowAtIndexPath. Even though I updated collection frame when it was filled with data, the container view of this collection wasn't updated resulting in some sort of clipping area for user interactions.

ScrollView for all iPhones simulators

For some reason, I could not find a simple answer and know a couple people from my school who had trouble with this too. I have a UIScrollView, and want let users scroll through it like the way it runs in the simulator for iPhone 7, but when I run it on an iPhone SE, the scroll view is a much longer and the label previously viewable inside the view now is farther down in the view and you have to scroll farther down. Now I can tell you my steps, and then show you some pictures. So I put a scrollview in my view then I put a view on top of the scroll view, but scroll view and view were pinned 0 in all directions. But I set the view height to 700 and control clicked from the view to the parent view and said equal widths. Then set viewcontroller to free form and set the height to 700. Then I added a button and a label just pinned to top right and left for button on and bottom left and right for label (0 to the bottom). This question is to answer a beginners question on what code should he or she implement to have the scrollview resize to each size iPhone. Here's photo of the storyboard.
I do not have any code written in the viewcontroller except for 3 outlets - label, button, and scrollview.
Please tell me what code I need to implement to correct this issue, thank you.
One thing I can suggest you is, Instead of taking a UIScrollView. Take a UITableViewController, and make the UITableView as Static. Now take one cell and add the labels the way you want.
No need to create a Custom Table Cell class, you can directly drag and drop the outlets and action.
Please remove all the Datasource and delegate methods, except the
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
And mention the row height as screen size. Now it will manage as per the device height. Will look approximately same in all the devices.
Thanks.
Feel free to ask if you didn't understand my answer.

UITableView Visible Cells Not Laying Out Subviews When View Appears

I have a UITableView that has multiple sections, with custom UITableViewCell subclasses populating the table. Within these UITableViewCell subclasses, I am implementing layoutSubviews to customize the position of labels, etc.
All of the cell's subviews are adjusting as they should, except the first batch of visible cells upon the view loading. They are identical to how they are laid out in storyboard, which is not what I want. For example:
http://i.stack.imgur.com/L5GJh.png
Note: The green and orange borders are a visual aid to see if the labels are resizing.
Upon scrolling, all of the new cells that appear have their subviews the way that I programmed them to be in layoutSubviews. As far as the first batch of visible cells, I can scroll them offscreen and then back on, and then the subviews are laid out perfectly. For example:
http://i.stack.imgur.com/jgjch.png
Within tableView: cellForRowAtIndexPath:, I call [cell layoutIfNeeded] before the method returns the cell. If I change this to [cell layoutSubviews], then the inverse happens, where the first batch of visible cells are laid out as they should be, but all of the cells that get loaded upon scrolling are not laid out properly.
I have tried to put [cell layoutIfNeeded] within [tableview: willDisplayCell: with no luck. Any ideas on how to fix this?
Thanks in advance!
This is the behavior you'd see if you were setting frames in your layout code with Auto Layout enabled. This won't work. The Auto Layout system is responsible for setting frames and will overwrite the values you set.
You should either specify your layout using constraints or turn off Auto Layout.

Getting real width of UITableViewCell's contentView before it displays

I have a grouped table view with custom cells (created programmatically by subclassing, not with IB). To properly position custom cell's subviews (such as labels and text fields), I need to know the current width of the cell's contentView just before the cell displays (taking into account that real cell width in a table view can change (according to screen orientation, modal presentation style, etc.)).
if I override in custom cell class the layoutSubviews method, it works perfectly, but it can be called frequently, thus I have to reposition my subviews every time when it's called, even when there's no need to do that.
Please, recommend me more elegant solution.
The recommended way of doing this is by setting the autoresizingMask of the table cell. If you need to have more control over the layout, you can store the last used view width in a member variable, and only layout the subviews if this differs from the current view width.

Resources