How to set Frame properly in Viewdidappear using reference of cell - ios

I want to display captured video using MPMoviePlayer for that purpose I designed one customcell and number of sections is one and I am loading ten by ten records in tableView so number of rows depends on array count so every time ten by ten records displaying from getService and my problem is frame is not setting properly in cell for that purpose I wrote entire code in one method and called that method in viewDidAppear() and when getting cell reference in viewDidAppear() getting fatal error in NSIndexPath I am thinking that viewDidAppear() is calling before I am getting cell reference so how to solve this problem please tell me any solution?

You are not supposed to manipulate with frames of cells from viewDidAppear of your view controller.
A frame consists of: left, top, width, and height.
left is always 0;
top is specified by a table and is based on the index path and heights of previous cells, section headers, and a table's header;
width is always equal to a width of the table;
height you can specify by yourself by overriding UITableViewDelegate's method optional func tableView(_ tableView: UITableView,
heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
But most likely you are interested in a different thing. And you want to layout UI elements within you cells. If yes, you have a few ways to do this:
Override func layoutSubviews() method in the cell and set all frames you want. This method is called every time when frame is changed. So it's exactly what you need.
Also you can specify the desired layout using auto layouts. This may be done in a storyboard/xib file or in code (in a designated constructor of the cell).

Related

Multiline label in UIView vs UITableViewCell

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:

How to insert a random view beneath any UICollectionViewCell

Sorry for the influx of UI and GUI questions but I was recently looking at an application called Ganna and in this pic below I was wondering how you would get that random Ganna Around The Globe banner beneath the Recently Played horizontal collection view and above the Top Charts horizontal collection view.
The way it's organized is that there is a main collection view with multiple horizontal collection views such as the Recently Played and Top Charts horizontal collection view.
I am thinking if the indexPath is 0 (which means you are at the Recently Played horizontal collection view) then you would insert that Ganna Around the Globe banner underneath that collection view. But this would be difficult since I would have to return different heights of the cell depending on whether that banner exists or not. And this banner appears 3 times beneath 3 different horizontal collection views.
Do you guys have any ideas? Thank you!
I can't say if this is how that particular example is implemented. But the way I would approach this is fairly simple.
You would have a single TableView with dynamic cells that covers the entire screen from nav bar to tab bar. Make two custom cells, the first custom cell contains a collectionView. The second cell would be laid out in whatever manner you want for displaying that banner.
In your cellForRow method of the tableViewDelegate you set the delegate of the collectionView inside the cell to whatever it needs to be.
The only thing left after that is a tiny piece of logic in the heightForRow method. Something like
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if (indexPath.row == kBannerCellIndex) {
return showBanner ? kBannerHeight : 0
}
// the rest of any height logic
return defaultCellHeight
}

How to correctly modify iOS table view cells at runtime (add/remove subviews?)

Apologies in advance if this has been answered somewhere. I've looked everywhere and I'm still unsure what to do. (And answers that use Objective C are almost completely worthless to me.) I'm somewhat new to iOS.
I have a UITableView which serves as a newsfeed of sorts, displaying a series of posts. (a la a twitter newsfeed, for instance.) The cells in this table view are (currently) derived from a single cell prototype, created in xcode's interface builder. Each cell contains subviews to display things like username, profile image thumbnail, title, message, date, location, another image, etc.
The problem is, depending on what data a particular post contains, many of these subviews either should or should not be shown -- if a post doesn't contain an image, then that cell's image view should not be shown; if a post doesn't have a date and/or location, one or both of these views should not be shown. Not only should the unused fields be empty, but they shouldn't take up any space in the cell.
I read in Using Auto Layout in UITableView for dynamic cell layouts & variable row heights (under "2. Determine Unique Table View Cell Reuse Identifiers". Wonderful answer by #smileyborg, btw.) that for each different layout of subviews that could be in a cell, a different prototype cell and reuse identifier should be used. But this would require me to have a cell prototype for every single possible combination of data items in a post, even if the difference is single label! Surely there must be a better way.
What is the safe and correct way to do what I need to do? Is there perhaps a way to remove subviews from cells at runtime (and have the layout adjust its spacing accordingly) without completely screwing up cell recycling?
Ill assume you know everything you want to know about your layout when you see the cell.
So its basic tableview cell layout. Dequeue and decorate.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(MyIdentifier, forIndexPath: indexPath) as! MyCustomCellClass
let data:MyDataClass = myDataAtIndexPath(indexPath)
decorateCell(cell,data:data)
return cell
}
func decorateCell(cell:MyCustomCellClass,data:MyDataClass) {
//here is where you arrange/change your constraints and hide/reveal views
//depending on the data
}
but also what you need is to allow the cell to set its height properly
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return someEstimatedNumberWhichIsClose;
}
but most crucially in your XIB, the cell MUST have a continuous line of constraints from top to bottom which describe the height.
So this cell will auto-set its height correctly.
While this one will not.
NB - in the upper example , if you leave the height constraint off the text-field and make it infinite line count the cell will grow/shrink appropriately.
So to summarise you can use one multipurpose cell as long as you keep your height constraints coherent.
As a general rule - Swiss Army CellsĀ® which do lots of things may get a bit unwieldy so you do need think about how many use cases you wish to support with one cell and then maybe start creating multiple types/identifiers.

UITableViewController scrolling optimisations for "Messenger App"-like functions

I am developing a small app, that includes messenger functionality. When showing a chat thread obviously I used a tableView, populate it with the messages in small bubbles and at some point before viewDidAppear scroll to the bottom, so the most recent messages are shown.
There is a popular solution to scrolling to the bottom of a tableView with scrollToCellAtIndexPath or scrollRectToVisible. These kinda work with little data, and constant cell heights. BUT in my case there is a lot of message data and I use UITableViewAutomaticDimensions for rowHeight. This results in slow loading, not scrolling to the bottom and crazy error messages.
Here's why (IMO): To scroll to the bottom, the tableView has to load all cells because they have automatic dimensions, and can only scroll down after he knows how far it is. Auto layout is another problem. Sometimes it doesn't scroll all the way down, because auto layout didn't finish yet and rowHeight is at the initial value still.
What I tried: putting the scrolling in didLayoutSubviews: solves the problem but loads very slowly, and scrolling is called multiple times (+ some crazy error message)
I guess an upside down tableView would solve the problem, because then the first cell could be the latest message, and no scrolling would be needed.
For optimization, you should use following method:
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
It is designed for large data sets, because it provides estimates for calculations instead of pixel-perfect size of cells (you also have to provide normal method for getting height - obviously).
Second optimization that you can do is when you have cells that does not change height with time (that is, they have the same height every time), just store already calculated height for each NSIndexPath and then use this value instead of calculating it again. To get height of the cell with AutoLayout, you can use this:
// Configure it
self.configureCell(cell!, indexPath: path)
// Update constraints
cell!.setNeedsUpdateConstraints()
cell!.updateConstraintsIfNeeded()
cell!.contentView.setNeedsLayout()
cell!.contentView.layoutIfNeeded()
// Calculate height of cell
let height : CGFloat = cell!.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height

UITableView row height

I have a subclass of a UITableViewCell. Its got some additional items on it for a total of 3 labels and up to 2 buttons. When creating the call in cellForRowAtIndexPath I call a method resizeToFitSubviews that resizes labels, and moves buttons around to the right position, it will remove unused buttons, etc. At this point I know the final height of the cell.
I tried to store the height in a NSMutableArray in the UITableViewController so I could return it in the heightForRowAtIndexPath call but for some reason heightForRowAtIndexPath is called before cellForRowAtIndexPath. And it seams its only called once as far as I can tell.
How do I go about knowing the height, when the cell isn't yet created?
I would love to know a better solution to this, but I think after you calculate the height of the cell in cellForRowAtIndexPath you have to let the table know to reload the data in the cell, so then you get to tell it the correct height in heightForRowAtIndexPath. Of course you should make sure that you don't loop yourself by doing it again when you are in cellForRowAtIndexPath after the reload. Sucky, but works.

Resources