UITableView.ScrollRectToVisible() doesn't scroll to bottom - uitableview

I have a table view containing a variable amount of sections and custom cells. On some occasions, a cell may resize within RowSelected(). Whenever that happens, I'd like to also make sure the cell is completely visible after resizing (enlarging) it.
I have that working in another table that just modifies the underlying data so that the table view source will provide a larger cell. I then reload the cell and scroll it visible like so:
// Modify data
//...
// Reload cell
tableView.ReloadRows(new NSIndexPath[] { indexPath }, UITableViewRowAnimation.None);
tableView.ScrollRectToVisible(tableView.CellAt(indexPath).Frame, true);
The problem arises in a table view where resizing may not only be triggered by RowSelected(), but also by events on UI elements within the cells.
The events then call a method to reload the cell:
void updateCell() {
if (cell.Superview != null) {
UITableView tableView = (UITableView)cell.Superview;
tableView.ReloadRows(new NSIndexPath[] { indexPath }, UITableViewRowAnimation.None);
// Get the new (possibly enlarged) frame
RectangleF frame = tableView.CellAt(indexPath).Frame;
Console.WriteLine("This really is the new large frame, height: {0}", frame.Height);
// Try to scroll it visible
tableView.ScrollRectToVisible(frame, true);
}
}
This scrolls fine for all cells but the bottom-most. It only makes the old frame of that cell visible. I double-checked that it really provides the new cell frame to ScrollRectToVisible().
So it seems ScrollRectToVisible() is bound to the old content size of the table - even after reloading rows. I tried to work around that by providing a new content size with the calculated difference in height. That does work but feels really hackish to me.
Is there some cleaner way to do things?
Thanks in advance

Instead using this:
tableView.ScrollRectToVisible(_: animated: )
Use it to scroll UITableView to bottom row:
tableView.scrollToRow(at: at: animated: )

Related

How to add unique insets to specific UITableViewCells?

I have a UITableView whose insetsContentViewsToSafeArea property is set to false - this makes the tableView span the width of the screen.
The amount and different types of cells are driven by the server, so there's no way of knowing the content of the tableView.
What I'd like to to:
Assign unique insets to certain cells only.
I can't post a screenshot, so I'll try to make a quick doodle:
These three cells are all in the same UITableView:
|[This is one cell that goes edge-to-edge]|
|[Here's another one]|
|-----This cell needs its own insets-----|
Question:
What's the best way to achieve this?
What I've tried:
Overriding layoutMarginsDidChange
Trying to add layoutMargins directly in cellForRowAtIndexPath...
Create a UITableViewCell subclass for the cell with custom insets.
Then you have 2 options --
Add a subview to cell's contentView with constraints -- these will correspond to your desired insets. This view will act as your " pseudo contentView" (add cell content to it)
Play around with contentView's insets. (not sure if it'll work)
I hope this will get you started.
You can use the cell subclass for all cell cases too if you want -- with an enum style which you can set in cell for row.
enum Style {
case edgeToEdge
case withInset(inset: UIEdgeInset)
}
inside cell subclass
var insetStyle: Style = .edgeToEdge {
didSet {
//update the constraints of the pseudo content view
}
}

Show entire table view inside cell

I need to display a dynamic table view inside a cell (of a static table). Using sections instead will not be enough for me. But I don't want this table to be scrollable, so the entire table must appear at once. The problem is that this table size [and rows count, and each row size] varies according to the content being shown. How can I associate the cell (which holds the table) autoresizing property with a table inside that must show all content at once?
Currently I have the tableView inside the cell, and constraints bonds it to all the 4 sides. The first table (not the one inside the cell) rowHeight property is set to UITableViewAutomaticDimension, but the table inside the cell doesn't appear entirely.
If I set the static cell height to a value greater than the tableView(inside cell) height, the whole table appears, and also an extra space beneath it (as the table is bounded to 4 sides of the cell)
So, any ideas on how to show this entire table inside a cell that dynamically has the perfect size for it ?
ps: I tried using collection view inside the cell. Unfortunately it doesn't serve my purpose.
Thanks!
Update
I tried to create a class for the inner table and use (as pointed by iamirzhan) contentSize didSet, like so:
override var contentSize:CGSize {
didSet {
self.invalidateIntrinsicContentSize()
}
}
I then used this method to call a function that resizes the cell that holds the table: self.frame.size.height = table.contentSize.height. The function is on this cell's own class.
This worked, the table now appears entirely. The problem is that it overlaps the cell underneath it, so i'm still looking for a solution.
I don't see why this requires 2 tableviews. The static portion should be uitableviewheaderfooters or just a UIStackView. But to answer your question simply query your child tableView for its size and return this in tableView:heightForRowAtIndexPath: for the outer tableview. The height of the child tableView is simply the sum of the hieght of all of its children (including any headers/footers). This is usually not difficult to calculate unless you are using something like a webview where you need to actually load the content and get the size asynchronously. You can calculate the size of elements that are based on their intrinsic content size with UIView.sizeThatFits(_:). The other elements should have fixed sizes or constants in the storyboard that you can sum up.
For inner tableView you should write such implementation.
Firstly, disable the scrollEnable flag
Then you should override the intrinsicContentSize method of tableView
Your inner tableView custom class:
class IntrinsicTableView: UITableView {
override var contentSize:CGSize {
didSet {
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return self.contentSize
}
}
Now, delete the height constraint, and the height will be calculating by its content.

Insert in a tableView set to Autoresize cause a scroll issue

I've an issue for few days and really I can't explain why it goes like that.
I'm doing a chat, set up with a tableView, printing message into cell. These cells are designed with prototypes, there are 3 different type (but anyway it doesn't matter). Text is typed, message is send, cell is inserted in table and then we scroll to the bottom of the tableView.
As you know, in a chat view the container of the message has to fit this text (which is a view), and then the cell has to fit to this container (Label in orange, container in purple).
This container has a variable height and grow along the text, changing cell height.
I've set many constraint for auto-layout display but I didn't have different cell height than the height I initially set for it in the project (no adaptative behaviour). Chat Message were cut.
So, I've tried to set each rowHeight by myself using the method heightForRowAtIndexPath, calculating constraint along text size but it create a real bad behaviour (changing cells when scrolling for example). The size was often wrong calculated/recalculated.
That's why I'm finally using estimatedRowHeight set to 44 combine with UITableViewAutomaticDimension. Here it does the trick ! Waouh ! But it's not as good as expected..
When the table view appears all is good. Containers fit to their label, rows' height fit to their container and it's beautiful. The problem appears during an insert.
After many tests, I notice that this bad behaviour only appears when multilines label remains in the table view.
During an insert, each cell seems to resize to 44 before adapt its height to content, and then create a gap compared to previous state which create a strange scroll of the table view :
If I change the estimatedRowHeight it made this worst.
I can show my code if you want, but I don't think it will be very useful here because I'm not using complicated function.. only insert, automatic height for cells and scroll down which are functions delegate for tableView.
Can you help me please ? Really I don't know how to change that.. every chat application does the trick but I can't found out the way.
Thank you for answer, excuse my english level I'm a poor french student..
If you need some code comment I'll give it.
I think you can cache height value for every cell in cellForRowAtIndex method and in HeightForRowAtIndex set the corresponding height for that cell.
You can just do this way :
var cellCached = Dictionary<Int,AnyObject>()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
table.rowHeight = yourCell.yourLabel.frame.origin.y + yourCell.yourLabel.frame.height + 10
cellCached[indexPath.row] = yourTable.rowHeight
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if(tableView == yourTable)
{
if(cellCached[indexPath.row] != nil)
{
return cellCached[indexPath.row] as! CGFloat
}
else
{
return 200
}
}
else
{
return UITableViewAutomaticDimension
}
}

UICollectionView - scroll to the next page

Is there any chance to scroll in the UICollectionView to the wanted item using . scrollToItemAtIndexPath and snap not to the item itself, but to the page the item is part of? (I got paging enabled.)
Cheers
You need to create NSIndexPath than scroll to that index.
//Mark: - Move to cell when view did appear
overload viewDidAppear() {
let scrollIndex: NSIndexPath = NSIndexPath(forItem: dayIndex, inSection: monthSection)
if (// Check you are in range of UIcollectionView's count) {
//Scroll collectionview according to your requirement i.e UICollectionViewScrollPositionCenteredHorizontally or UICollectionViewScrollPosition.Left
self.YourCollectionView.scrollToItemAtIndexPath(scrollIndex, atScrollPosition: UICollectionViewScrollPositionCenteredHorizontally, animated: true)
}
}
I did end up using offsetContent property;
I knew the width of every so called page.
I wrote a code to get the indexPath for current day
I retrieved the section of the indexPath
I multiplied the section by the width of the view and named it "offset"
I set my UICollectionView.contentOffset.x to "offset" and it works fine.
I also tried using .scrollRectToVisible and the offset, and it worked amazingly, but I also wanted to updated something that was based on the contentOffset of the UICollectionView, and the .scrollRectToVisible seems not to update this property - it was updated only after I dragged the view a little.
Thanks for all your help!

Animate a slide-in details view within a UITableViewCell

I have a UITableViewCell that is using auto-layout. When a user selects the cell, I want to display extra information in a details view that slides downwards, extending the bottom of the cell.
I'm having trouble figuring out the correct way to do this.
The transitionFromView:toView:duration:options:completion: method does not seem appropriate, because I am not replacing one view with another.
The transitionWithView:duration:options:animations:completion: method seemed like it should do the trick, but I can't figure out the correct way to achieve my goals using it.
This is what I tried initially (C#, but Objective-C/Swift equivalents should be obvious):
private void AnimateDetailsIntoView()
{
this.detailsView.Hidden = true;
this.ContentView.ConstrainLayout(() =>
this.detailsView.Top() == this.nameLabel.Bottom() &&
this.detailsView.Bottom() == this.ContentView.Bottom() - Layout.StandardSiblingViewSpacing &&
this.detailsView.Left() == this.nameLabel.Left() &&
this.detailsView.Right() == this.nameLabel.Right());
this.AddSubview(this.detailsView);
UIView.Transition(
this,
0.2,
UIViewAnimationOptions.ShowHideTransitionViews | UIViewAnimationOptions.TransitionFlipFromBottom,
() => { },
() => { });
}
This results in the table cell "spinning around", but the details view does not actually show. Even if I remove the line that hides detailsView, I still don't see it after the animation.
I suspect this is layout related, but am unsure how to "grow" the cell vertically.
Can anyone tell me what I'm doing wrong?
PS. I realize there's more to do once I get the initial animation working. I'll have to animate any previously selected cell up and remove its details panel.
As per the OP wants to expand and collapse the cell, following are the procedure to achieve it:-
Design your whole detail cell in a single prototype cell.
Now tableView heightForRowAtIndexPath: specify 2 sizes one for collapse cell and another size for (expanded)detailed cell,by giving a condition something like BOOL variable"isExpanded".
Now on didSelect of tableView or in the button's action if you are using button in cell, just store the value of indexpath.row to determine which row to expand and add this code to reload the row, this will easily do the animation trick of expand
[tbl beginUpdates];
[tbl reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationBottom];
[tbl endUpdates];
Same goes for collapsing a cell, just some logic tweaks, which you can easily do.

Resources