I have a UITableView that I resize when the keyboard shows.
// MonoTouch code
// Resize the table so it's not hidden by the keyboard
var keyboardFrame = UIKeyboard.BoundsFromNotification(notification);
var tableFrame = myTable.Frame;
tableFrame.Height = theView.Frame.Height - keyboardFrame.Height;
myTable.Frame = tableFrame;
// Bring the current cell back in view
this.InvokeOnMainThread(()=>{
if(currentCell != null) myTable.ScrollRectToVisible(currentCell.Cell.Frame, true);
});
However, when I try to perform an action on that cell that is sensitive to that cell's current position on the screen (such as attaching a UIPopoverController), I find that the frame of that cell is showing the original location prior to the table resize and scrolling.
I've tried the following all yielding the same results of the frame still pointing to the old location (a lot of these were experimenting to see if they did anything not knowing what would work and what wouldn't):
Using SetNeedsLayout on the cell, the subviews of the table, the table.
Using LayoutIfNeeded on the cell, the subviews of the table, the table and the view containing the table
Multiple combinations of SetNeedsLayout and LayoutIfNeeded
Spawning a new thread, sleeping on that thread for a ridiculous amount of time (to allow time for the UI to perform a redraw/update/whatever) and then looking at the Frame
Spawning a new thread, sleeping for that ridiculous amount of time and then calling the different layout functions
I'm not certain if it's the resize or the scrolling that is where the breakdown is happening.
When you scroll the table using a gesture, the cell frames seem to be updated properly because I can always show a popover view in the correct location relative to that cell I'm attaching it to. So I don't understand why resizing the table and scrolling it programmatically wouldn't do the same.
Does anyone know how to force a UITableView to update it's cells' frames without doing a ReloadData (ReloadData causes many unwanted effects in this case)?
This is behavior is expected. The UITableView inherits from UIScrollView. The UIScrollView, apart from its frame, has another property, ContentSize. The two are separate. While the frame represents the position and size in relation to its parent view, the ContentSize defines the area of the content of the scroll view, so that the object will "know" by what amount to scroll each time.
Changing the frame or bounds of the UITableView, or any view for that matter, does not necessarily change the frame(s) of its subviews. This behavior depends on the superview's ContentMode property.
Anyway, the table cell's frames do not change, because they are relevant to the ContentSize property. Since it is working correctly before resizing the table view, I can only assume that the ContentSize and Frame of the table view match at that point. After resizing the table view, the ContentSize remains intact so you have inconsistency between the two values.
Instead, you should use either the ConvertRectToView or ConvertRectFromView methods to get the rectangle of the cell in relation to the tableview's frame.
Related
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.
Custom UITableViewCell's subviews added in code using auto layout works (verified). However the whole point of doing this was to not have to calculate the height of each tableview cell and the delegate method heightForRowAtIndexPath expects a height while drawing the tableview.
How can I figure out this height based on content using the auto-layout (visual format language based addition in code already added and working) and return it to this heightForRowAtIndexPath?
Also I'm I can't really do this on a background thread (or can I?) and therefore if I have a UITableView using this backed by a list of say 1000 records, this will take forever, is that right?
Autolayout in this case just means that you don't need to calculate the frame sizes of your subviews within each cell. It's got nothing to do with the heightForRowAtIndexPath method - this is used by the table view to define the cell's frame, which will then inform the layout of the subviews.
Using Autolayout to determine the heights would likely be pretty slow, and you can't do it on a background thread. If you have 1000 rows then I'd consider a hierarchical structure instead of a single table as that will be pretty tedious to scroll through. You could also consider calculating the heights at the point of setting or creating the data.
You may be able to set up an offscreen view using your constraints, populate it with your data for each item, then record and cache the height. However you'd have to do this at the data end rather than in the height method, as it would be far too slow.
I'm trying to create a UICollectionView where the cells are varied sizes based on the contents within them and how those contents play with the constraints set on them. Basically, I need to examine the frame size but this size isn't updated on the cell until after the subviews have been laid out. Unfortunately, this isn't done until the view is attached to the window hierarchy it seems. I can't find any series of calls to trigger this without adding it to the window hierarchy, yet I don't want to add it to the hierarchy except through this UICollectionView. And, of course, the collection view wants the size of it before it requests and adds the cell.
I can't get to size without adding it to the window hierarchy (I think), and I can't get it added to the window hierarchy (i.e. the collection view) without having the size. What am I missing here?
Just call these on the cell, and it should act as though it was added to the view hierarchy:
setNeedsLayout()
layoutIfNeeded()
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.
I've created a UITableVIew and it is a subview of a UIView. There are three issues that i'm having and it is only occurring on iOS 4 devices:
1) The table doesn't bounce when the view hits either the top or bottom of the table while scrolling. The vertical bar doesn't shrink either, it feels sticky and it is very much acting like an Android table view. I've tried enabling the bounce property but that doesn't make any difference.
2) The horizontal scroll bar appears when the view is scrolled down to the bottom of the table. This shouldn't appear since the table view's contentSize has been set correctly. It does eventually disappear when the contentSize is set 20 pixels less than what it should be.
3) I can't scroll to the bottom of the footer view, and only half the footer view is visible.
I've added a UITableView as a subview to other views throughout my project and this has never occurred, and so i've copied the way that I create other UITableViews, but still no luck.
Any suggestions would be greatly appreciated.
Thanks,
Ankur
I worked out the problem. The problem is a little strange, although i had a feeling that the way i was coding was a little messy.
I am subclassing a View, and the parent view has a layoutSubview method, which is only calculating and setting the frame for a table view subview. In the subclass, i had to override layoutSubview for the same reason as the values for the table view's frame need to be different. This means that the table view's frame was being laid out twice, once by the parent class and a second time by the subclass. It seems that iOS 4 doesn't like this, and i should only set the frame once per subview per layoutSubview call.
Now i've created a layoutTableView method, which is called from the parents layoutSubview, and i've overridden layoutTableView in the subclass. Therefore the table view's frame is only being set once.