UITableView jumping on scrollToRow? - ios

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.

Related

Slide view together with UICollectionView cell

i have this structure. Scrollview -> UICollectionview + Label
This viewcontroller has array of items (BlockItem). On scrollViewDidScroll i change current test label (description)
func scrollViewDidScroll(_ scrollView:UIScrollView)
{
let midX:CGFloat = scrollView.bounds.midX
let midY:CGFloat = scrollView.bounds.midY
let point:CGPoint = CGPoint(x:midX, y:midY)
guard
let indexPath:IndexPath = collectionView.indexPathForItem(at:point)
else
{
return
}
let currentPage:Int = indexPath.item
if let found = items.first(where: {$0.id == String(block[currentPage])}) {
description.text = found.description
}
}
The main issue that i want my description label will be moving together when i move my collectionview cell and appear from the next cell. In other words, if I scroll to the left my description should move along with the cell, i.e. go to the left and appear on the right of the screen.
How can i do this? I know i could make a big collectionview but i need that only image part should be scrollable, not the entire block.
Here is what i want to achieve
Here is video example: scroll works only if i swipe on the image area, and doesn't work when i scroll down the page
https://drive.google.com/file/d/1kl1GYgXvK4bL3toTfOvpxF2WqS56pQO9/view?usp=sharing
You exactly said this:
"The main issue that I want my description label will be moving together when I move my collection view cell and appear from the next cell."
If you want your description label moving together, just include them into your collection cell. It's very clear from your point there
I don't understand why u need to separate it, but you want it to slide from new cell.
If you insisted that you want to archive the label, not in a scroll view, then use 2 label
One label for this cell and one label will come from scroll direction, u can archive this by creating manually. This is a simple code for a swipe left to scroll
UIView.animate(withDuration: 0.5) {
//insert your label animation
//current label moved with the scroll to left
//create a new label from the outer right of the current view, scroll it to the middle of your view
}
But it will be hard work for good animation or scroll support.

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.

3D Touch Peek in transform-inverted table view cell

I have a chat application where I am using the inverted table view technique (table view is scaled to (1, -1) with a transform, and all the cells themselves are scale to (1, -1) as well, neutralizing the effect, thus effectively inverting the table view to display the "first" cell at the bottom) to "start" the table view from bottom. Everything works fine, except that I need to implement peek and pop gesture. I've connected a button inside my table view cell to the target view controller, and enabled peek and pop as shown:
The gesture works, though when I 3D touch partially and hold enough to stand out the touched item (but not pop the preview view controller) I am getting inverted view:
How can I make the view pop out with correct transform, while still using the inverted table view?
The reason why it shows the view upside down after a light press is not because it removes the transform from the cell. In fact the transform on the cell stays as it is but the segue pulls the cell view out of the context of the tableView. Therefore the transform on the cell is not counteracted by the transform on the table anymore.
I believe you could fix this by subclassing UIStoryboardSegue but it would probably be much easier to switch your method of starting the table at the bottom. Check out the accepted answer on this question.
I used a slightly modified version which accounts for the navigation bar and status bar:
func updateTableContentInset() {
let numRows = tableView(self.tableView, numberOfRowsInSection: 0)
var contentInsetTop = self.tableView.bounds.size.height - (self.navigationController?.navigationBar.frame.size.height)! - 20
for i in 0..<numRows {
let rowRect = self.tableView.rectForRow(at: IndexPath(item: i, section: 0))
contentInsetTop -= rowRect.size.height
if contentInsetTop <= 0 {
contentInsetTop = 0
}
}
self.tableView.contentInset = UIEdgeInsetsMake(contentInsetTop, 0, 0, 0)
}
Just add this to your UITableViewController and call it on viewDidLoad() and every time a new row is added to the table.
After adding a new row and then calling updateTableContentInset() you will have to scroll to the bottom like this:
tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
Since there is nothing upside down here you will not have problems with your Peek segue.

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!

Custom table view scrolling animation

In my app I'm trying to implement custom table view scrolling. I'm trying to achieve something similar to Volvo ocean race app, which does it very well.
Basically when user scrolls, middle cell gets higher and current high cell gets smaller
My original idea was playing with cell's frame in scrollViewDidScroll method for example
func scrollViewDidScroll(scrollView: UIScrollView) {
if(self.tblViewPhotos.contentOffset.y > 0)
{
var thisCell = self.tblViewPhotos.cellForRowAtIndexPath(selectedIndexPath) as PhotoCell
var offset = self.tblViewPhotos.contentOffset.y;
thisCell.frame = CGRectMake(thisCell.frame.origin.x, thisCell.frame.origin.y, thisCell.frame.size.width, 250-offset);
}
}
however I'm getting very buggy results (frame changing suddenly when swiping fast etc). I would be glad, if someone can point me to right direction

Resources