UICollectionView - scroll to the next page - ios

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!

Related

Reordering sections in UITableView

I have UITableView with sections and rows(https://imgur.com/a/hrYTEVR). I know how enable reordering for rows, but i dont know how implement reordering for sections.
I need add reordering control to sections(https://imgur.com/a/V5kU9Ew) and then when user touch this control, rows under section should flops. After that user can move section to new place.
After read topics on stackoverflow and other sites, i dont find any good idea how implement something like this.
I thought about implement sections through cells, but in this case i can't flop rows under section for further moving to new place.
If you have any idea how implement this – give me advice. Thanks!
There is no native functionality to achieve what you want. If I understand correctly you would want to collapse a whole section of rows and then start dragging the "header" around. If you want to do this on your own I would suggest starting with a pan gesture recognizer which triggers on the header button.
The gesture should be relatively obvious. After it starts on the header you need to track position using locationIn in your table view.
To collapse rows all you need to do is modify your table view cells with appropriate animation like:
tableView.beginUpdates()
tableView.deleteSections([myIndexPath], with: .top) // Maybe experiment with animation type
// Modify whatever you need to correspond this change in the data source
tableView.endUpdates()
Since you will be removing the section you will also be removing the view (header) which has the gesture recognizer. That means it might be better adding the gesture to the table view directly or its superview even. You will need to force it to trigger only when one of those buttons on headers is pressed. You can get some idea here about it. The rest is unaffected by this change.
At this point you will probably need to create an extra view which represents your section stack and follows your finger. This should be pretty easy if you add it as a subview and manipulate it's center with pan gesture recognizer locationIn in it's superview:
movableSectionView.center = panGestureRecognizer.location(in: movableSectionView.superview!)
So up to this point you should be able to grab a section which collapses all cells and be able to drag the "section stack" view around. Now you need to check where in table view your finger is to know where to drop the section. This is a bit painful but can be done with visibleCells and tableView.indexPath(for: ):
func indexPathForGestureRecognizer(_ recognizer: UIGestureRecognizer) -> IndexPath {
let coordinateView: UIView = tableView.superview! // This can actually be pretty much anything as long as it is in hierarchy
let y = recognizer.location(in: coordinateView).y
if let hitCell = tableView.visibleCells.first(where: { cell in
let frameInCoordinateView = cell.convert(cell.bounds, to: coordinateView)
return frameInCoordinateView.minY >= y && frameInCoordinateView.maxY <= y
}) {
// We have the cell at which the finger is. Retrieve the index path
return tableView.indexPath(for: hitCell) ?? IndexPath(row: 0, section: 0) // This should always succeed but just in case
} else {
// We may be out of bounds. That may be either too high which means above the table view otherwise too low
if recognizer.location(in: tableView).y < 0.0 {
return IndexPath(row: 0, section: 0)
} else {
guard tableView.numberOfSections > 0 else {
return IndexPath(row: 0, section: 0) // Nothing in the table view at all
}
let section = tableView.numberOfSections-1
return IndexPath(row: tableView.numberOfRows(inSection: section), section: section)
}
}
}
Once the gesture recognizer ends you can use this method to get the section you are dropping your items into. So just:
tableView.beginUpdates()
// Modify whatever you need to correspond this change in the data source
tableView.insertSections([indexPathForGestureRecognizer(panGestureRecognizer).section], with: .bottom)
tableView.endUpdates()
This should basically be enough for reordering but you might want to show in table view where the dragged section is. Like having a placeholder at the end of the section in which the stack will be dropped into. That should be relatively easy by simply adding and then moving an extra placeholder cell reusing indexPathForGestureRecognizer to get a position for it.
Have fun.

UITableView jumping on scrollToRow?

I am facing issue with UITableView in Swift. Table contains lyrics. Each cell is a lyric class. I know which index I have to focus on so I show the current lyric in red and the rest in black.
All okay so far. But when using scrollToRow, it's working well before table starts to scroll down to lyrics outside the view. Once the UITableView starts scrolling outside the view, it starts jumping.
Any idea how to make it scroll smoothly to the current cell without jumping?
Here is how I make the table go to the desired lyric:
let lcCell = IndexPath(row: (vc_lyrics.sharedInstance().lyric_time[seconds]?.index)!, section: 0)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
vc_lyrics.sharedInstance().tableView.scrollToRow(at: lcCell, at: .none , animated: false)
vc_lyrics.sharedInstance().tableView!.reloadData()
}
Or if I can keep the lyric cell in the center by scrolling the scrollview of the table, not its delegate scrollToRow.
Change this line to set animated to true:
vc_lyrics.sharedInstance().tableView.scrollToRow(at: lcCell, at: .none , animated: true)
This will cause the cells to scroll smoothly. I would also think about whether or not you need to reload the tableView data here because that can also cause scrolling issues as #rmaddy mentioned.

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.

UICollectionViewController Push Transitions

I'm trying to implement an interface where the user drills down through successive collection views of data, and would like to have each collection view animate to the next one as it's pushed. This has been very frustrating.
I've boiled it down to a very simple test case which I still can't get to work the way I think it ought to.
1- A UINavigationController has a UICollectionViewController which is hardwired to return a numberOfSections == 1, an itemsInSection = 8, and the backgroundColor of the cells is set to redColor in cellForItemAtIndexPath. The associated UICollectionViewFlowLayout.itemSize is set to 80, 80.
2 - When a cell is selected I push a new UICollectionViewController which is hardwired to return a numberOfSections == 1, an itemsInSection == 30, and the backgroundColor of the cells is set to blueColor in cellForItemAtIndexPath. The associated UICollectionViewFlowLayout.itemSize is set to 60, 60.
If I push the second controller with its useLayoutToLayoutNavigationTransitions property set to false, the controller slides in with the proper data display (30 blue cells). If I set useLayoutToLayoutNavigationTransitions to true before pushing, only the layout changes on push. The 8 red cells from the first controller are still displayed, but their size changes to 60, 60.
Apple's UICollectionViewController class reference says that when you set useLayoutToLayoutNavigationTransitions to true, "the navigation controller performs an animated layout change between the contents of the two collection view controllers instead of the traditional push animation", but I'm only getting the layout change.
My test code is stupid simple, so not sure what I might be doing wrong here. Does Apple's class reference misrepresent what the expected behavior should be? Is there any reason to just change layouts when you've pushed a new controller (and theoretically a new data source)? Is there an alternative way to animate to the next collection view?
For what it's worth, here is the only code in my test program other than returning appropriate values for numberOfSections and numberOfItems, and setting appropriate cell background colors.
override func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
let itemSize = (collectionViewLayout as! UICollectionViewFlowLayout).itemSize;
let theLayout = UICollectionViewFlowLayout()
theLayout.itemSize = CGSizeMake(itemSize.width - 20, itemSize.height - 20)
let toVC = SecondCollectionViewController(collectionViewLayout:theLayout)
toVC.useLayoutToLayoutNavigationTransitions = true
navigationController?.pushViewController(toVC, animated:true)
}

UITableView.ScrollRectToVisible() doesn't scroll to bottom

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: )

Resources