I've got a strange problem. In my app i've got a Table View with paging enabled and cells with full-screen height, only one cell is visible. Each cell has a button, and when the user taps it, scrollToRowAtIndexPath fires up, and table view scrolls to next cell.
Unfortunately, while scrolling from first to second cell, first cell behaves oddly - it firstly disappears ( content view drops all subviews ), then it appears from the bottom of the screen, flows all the way up, and after it second cell appears. This issue doesn't reproduce on other cells - only while transition from first to second cell. The code i'm using is below, any help would be appreciated.
let cells = tableView.indexPathsForVisibleRows()?.sorted{ first, second in ((first as NSIndexPath).row) < ((second as NSIndexPath).row) }
if let cells = cells {
if cells.count > 0 {
if let currentIndexPath = cells[0] as? NSIndexPath {
let nextIndexPath = NSIndexPath(forRow: currentIndexPath.row + 1, inSection: currentIndexPath.section)
// UIView.animateWithDuration(0.2, animations: { () -> Void in
println("table view scrolls")
if ( currentIndexPath.row + 1 >= self.tableView(self.tableView, numberOfRowsInSection: 0 ) )
{
return ;
}
self.tableView.scrollToRowAtIndexPath(nextIndexPath, atScrollPosition: UITableViewScrollPosition.Top , animated: true )
}
}
}
}
Related
I have custom layout with fullscreen cells. When removing cell from the left (it's not visible at the time), UICollectionView jumps to the next cell.
It's like current cell was at index 4 and when cell on the left removed the next cell has index 4 now and immediately scroll to the next cell.
Describing in 3 steps (A is cell that need to be fullscreen, x will be removed, o other cells, large letter is fullscreen):
ooooAoo
oooxAoo
oooaOo
But must keep this oooAoo
Here is my solution if it can help to anybody, did not found how to achieve desired offset natively, so just scrolling contentOffset to the desired position right after reloadData():
var currentCell: MyCollectionViewCell? {
return (visibleCells.sorted { $0.frame.width > $1.frame.width }.first) as? MyCollectionViewCell
}
//-----------------------------------------
//some model manipulating code, removing desired items here...
let currentList = currentCell?.parentList
reloadData()
if let list = currentList, let index = self.lists.firstIndex(of: list) {
self.scrollToItem(at: IndexPath(row: index, section: 0), at: .centeredHorizontally, animated: false)
}
currentCell is computed property, returns optional middle cell.
Detect which cell is the largest, because of custom flowlayout logic.
parentList is the model item, I can compare cell by it to make life
easier. I check which list was attached to the cell before
reloadData().
I have a collectionView and allow a user to dynamically add cells to the collectionView. The view controller only shows one cell at a time and I want the first textView (which is part of the cell) to become the firstResponder (and keep the keyboard visible at all times), which works fine when loading the view controller (as well as in one of the cases below).
I have created a method to detect the current cell, which I call every time in any of these cases: (1) user scrolls from one cell to another (method placed in scrollViewWillEndDragging), (2) user taps UIButtons to navigate from one cell to another, (3) user taps UIButton to create and append a new cell at the end of the array (which is used by the collectionView).
This is the method:
func setNewFirstResponder() {
let currentIndex = IndexPath(item: currentCardIndex, section: 0)
if let newCell = collectionView.cellForItem(at: currentIndex) as? AddCardCell {
newCell.questionTextView.becomeFirstResponder()
}
}
Now my problem is that this only works in case (1). Apparently I have no cell of type AddCardCell in cases (2) and (3). When I print the currentCardIndex, I get the same result, in all of the cases, which is very confusing.
Any hints why I wouldn't be able to get the cell yet in cases 2 and 3, but I am in case 1?
As a reference here are some of the methods that I am using:
//Update index and labels based on user swiping through card cells
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
//Update index based on targetContentOffset
let x = targetContentOffset.pointee.x
currentCardIndex = Int(x / view.frame.width)
setNewFirstResponder()
}
And the other method, from which it doesn't work (case 3):
//Method to add a new cell at end of collectionView
#objc func handleAddCell() {
//Inserting a new index path into tableView
let newIndexPath = IndexPath(item: autoSaveCards.count - 1, section: 0)
collectionView.insertItems(at: [newIndexPath])
collectionView.scrollToItem(at: newIndexPath, at: .left, animated: true)
currentCardIndex = autoSaveCards.count - 1
setNewFirstResponder()
}
Regarding case 2,3 i think that the cell is not yet loaded so if let fails , you can try to dispatch that after some time like this , also general note if the cell is not visible then it's nil
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
setNewFirstResponder()
}
Also it can work if you set animated:false to scrollToItem
I created a horizontal tableView, with cells that take up the whole view controller. Instead of scrolling with the default setting, I would like to scroll to the next cell using scrollView.scrollToItem(at: IndexPath method.
Meaning, each time the user scrolls right, it will automatically scroll to the next cell, and each time the user swipes left, it will automatically scroll to the previous cell. Whenever you scroll a direction, it should automatically scroll to the next or previous cell.
I tried intercepting it using scrollViewDidScroll delegate method, but I am running into a ton of problems, it is autoscrolling back and forth and glitching a ton. What am I doing wrong?
var previousOffset: CGFloat = 0.0
var allowHorizontalScroll: Bool = true
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if (allowHorizontalScroll){
let offset = scrollView.contentOffset.x
let diff = previousOffset - offset
previousOffset = offset
var currentBoardIndex = scrollView.indexPathForItem(at: CGPoint(x:offset, y:10))?.item
if currentBoardIndex != nil {
if diff > 0 {
//print("scroll left")
if (currentBoardIndex != 0){
currentBoardIndex = currentBoardIndex! - 1
allowHorizontalScroll = false
scrollView.scrollToItem(at: IndexPath(row: (currentBoardIndex!), section: 0), at: .left, animated: true)
}
}else{
//print("scroll right")
if (currentBoardIndex != ((boardVCDataSource?.fetchedResultsController.fetchedObjects?.count)! - 1)){
currentBoardIndex = currentBoardIndex! + 1
allowHorizontalScroll = false
scrollView.scrollToItem(at: IndexPath(row: (currentBoardIndex!), section: 0), at: .left, animated: true)
}
}
}
}
}
Whenever you scroll a direction, it should automatically scroll to the next or previous cell.
Why not throw out all that code, and instead set your scroll view's isPagingEnabled to true? A paging scroll view does what you describe, automatically.
using this code to scroll to bottom of collection view. however it scrolls to the second last one only.
private func scrollToBottom() {
let lastSectionIndex = (ChatCollectionView?.numberOfSections())! - 1
let lastItemIndex = (ChatCollectionView?.numberOfItemsInSection(lastSectionIndex))!-1
let indexPath = NSIndexPath(forItem: lastItemIndex, inSection: lastSectionIndex)
ChatCollectionView!.scrollToItemAtIndexPath(indexPath, atScrollPosition: UICollectionViewScrollPosition.Bottom, animated: false)
}
is anyone familiar with a bug like that
Your collection view's frame was under the visible area. You should set its bottom inside visible area.
I'm trying to build a custom UITableView that acts in a similar way to the reminders app.
Instead of the top-most visible cell scrolling off the display, I want it to be covered by the next cell so the cells appear to stack on top of one another as you scroll.
Currently I am using:
override func scrollViewDidScroll(scrollView: UIScrollView) {
let topIndexPath: NSIndexPath = tableView.indexPathsForVisibleRows()?.first as! NSIndexPath
let topCell = tableView.cellForRowAtIndexPath(topIndexPath)
let frame = topCell!.frame
topCell!.frame = CGRectMake(frame.origin.x, scrollView.contentOffset.y, frame.size.width, frame.size.height)
}
But the top cell is always above the second cell - causing the second cell to scroll under it.
Also if I scroll quickly this seems to misplace all of my cells.
EDIT: Have fixed this. Answer posted below for future reference.
For anybody searching this in the future.
Simply loop through all the visible cells and set their z position to their row number (so each cell stacks above the previous one).
The if statement tells the top cell to remain at the contentOffset of the scrollview and for all other cells to assume their expected position. This stops the other cells being offset if you scroll too quickly.
override func scrollViewDidScroll(scrollView: UIScrollView) {
// Grab visible index paths
let indexPaths: Array = tableView.indexPathsForVisibleRows()!
var i = 0
for path in indexPaths {
let cell = tableView.cellForRowAtIndexPath(path as! NSIndexPath)
// set zPosition to row value (for stacking)
cell?.layer.zPosition = CGFloat(path.row)
// Check if top cell (first in indexPaths)
if (i == 0) {
let frame = cell!.frame
// keep top cell at the contentOffset of the scrollview (top of screen)
cell!.frame = CGRectMake(frame.origin.x,
scrollView.contentOffset.y,
frame.size.width,
frame.size.height)
} else {
// set cell's frame to expected value
cell!.frame = tableView.rectForRowAtIndexPath(path as! NSIndexPath)
}
i++
}
}