I've been trying to figure out how to make my collection view of 3 cells load with the 2nd cell, and I finally figured it out after looking through StackOverFlow. However, the code that I came across is a bit confusing to me. Would anyone be able to explain why this code below works in making my collection view cell (that covers the whole screen) start with the 2nd of 3 cells? (this is the effect I wanted to achieve all along, but I want to learn more about why this code works exactly.
In this block of code, there's a bool variable and an if statement, why are they needed? When I took out the boolean variable and if statement, the collection view was unable to scroll.
How does this block of code work exactly?
Thank you.
var onceOnly = false
internal override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if !onceOnly {
let indexToScrollTo = IndexPath(item: 1, section: 0)
self.collectionView.scrollToItem(at: indexToScrollTo, at: .left, animated: false)
onceOnly = true
}
}
1- This scrollToItem in Docs
let indexToScrollTo = IndexPath(item: 1, section: 0)
self.collectionView.scrollToItem(at: indexToScrollTo, at: .left, animated: false)
makes the collectionView scroll to the second item in the list
2-
When I took out the boolean variable and if statement, the collection view was unable to scroll
Because willDisplay is called for every cell display , so when for example you scroll to 3rd cell willdisplay is called and causes the collectionView to go to the second cell , so it makes it stuck in the second item all the time ( and this seems like no scroll but the scroll happens and you won't notice that as it happens instantly ) , so the boolean var is needed to make that scroll action happens once which is the scroll to the specified index
Related
[EDIT after replies: solution at the end of the question]
I have a UICollectionView where I initially load 4 views. I would like that when the user scrolls until the last one (4th view) and try to scroll again, I update the data of my model, reload the UICollectionView and moves back to the 1st view so that the user can again scroll 3 times till the 4th view, etc.
The issue is that when I load 4 views initially, the user can't scroll after he reached the last view since there is nothing after. So I created an empty cell as my 5th view and now I load 5 views in my initial UICollectionView. But this 5th view is never fully displayed and I use "willDisplay" method to reload the data and force the move back to the first view. This is not elegant but I couldn't find another way to do it.
This method is working fine but here is the problem: it seems that when willDisplay is called, I indeed reload the data and moves back to the first view but only then the scroll that was supposed to take me to the 5th view is taken into account which means that after I moved back to the first view, the app shows me actually the second view (it did move back to the first view but then the scroll that I initiated to go from 4th to 5th view is applied, moving me to the 2nd view).
I hope this is clear...
So here is the code I am using:
override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
// if this is the last view
if((indexPath.row == self.dataModel.getNumberOfData()) && self.dataModel.getNumberOfData() > 0)
{
// I update the model here
self.dataModel.resetData()
// Then I reload the UICollectionView
self.collectionView.reloadData()
// Then I move back to the first view
self.collectionView.selectItem(at: IndexPath(item: 0, section: 0), animated: false, scrollPosition: .centeredHorizontally)
}
}
Of course if you have another solution, more elegant, to avoid that 5th view, I am happy to consider it. My data is not static so when I need to reload the new data, I query a database to retrieve the new information so I don't know about the new data when I run the app.
Thanks.
[EDIT with a solution using scrollViewDidScroll. I show for example how to detect the end (horizontally scrolling and delete the first view...
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
// if reached the end of the UICollectionView...
if (scrollView.contentOffset.x == scrollView.contentSize.width - scrollView.frame.size.width) {
print("Reached the end...")
self.dataModel.removeNFirst(n: 1) // remove first item from model
// delete the first view of the UICollectionView
self.collectionView.performBatchUpdates({
self.collectionView.deleteItems(at: [IndexPath(item: 0, section: 0)])
}) { (finished) in
self.collectionView.reloadData()
}
}
}
The ScrollToRow is not working as I want.
The function I wrote seems to be working fine, but I want it to scroll down to last row only when its added like it happens in chat. When you get a new message, your tableview scrolls to the bottom to read new message.
However, my tableview seems to reload every time before performing scrollToLastRow function (what I mean is every time before scrolling to bottom... it first goes back to top first cell and starts scrolling to bottom from there Every time when a new cell is created).
My code is simple for the scroll:
func scrollToLastRow()
{
let indexPath = IndexPath(row: messages.count - 1, section: 0)
self.chat.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
To check my code:
here's a link to my repository
ChatApplication
the ViewController's used for chat is ---> chatVC
the ViewController used to make chat is -> chatCell
I have tried anything I could get all over I could find.
Tried dispatch (without it the scroll function wasn't working at all)
Used the function on many different places (including viewDidAppear)
Used dispatch.asyncAfter for delay.....
but nothing worked.
#Fahad, you are reloading the tableview before the scrollToLast function is getting called. Please check and please verify at your end.
Note please update your GitHub repository url in your question properly as it not clickable.
Use this function for scrolling to the bottom.
func scrollToBottom() {
let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height + contentInset.bottom)
setContentOffset(bottomOffset, animated: true)
}
use tableview.scrollToBottom()
Use tableView.insertRows(at: [IndexPath], with: UITableView.RowAnimation) for adding object to end of the tableView.
From the problem you say and looking in to your code, the animation issues are due to reloading the entire tableView after a new item is added to dataSource array,
What you have to do is to use Call tableView.insertRows(at: [IndexPath], with: UITableView.RowAnimation) with the correct indexPath on your tableView and the tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) will be invoked.
I have class UIViewCollection. I need will block UIViewCollection and unlock after terminating scroll animation UIViewCollection. This method work good but while when i touch to screen it's not complete scrolling
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
Since CollectionView inherits from UIScrollView you can set the property
collectionView.isScrollEnabled to true or false as you see fit, however in the code you shared I see that you want to scroll to each item in your collection like it was pages in a book, right? to achieve that there is another property called paging, and you can set it to true or false as well, I believe it would be something like collectionView.isPagingEnabled = true or false as you wish, hope this helps
So I have been struggling with this for a while now. I have a UICollectionView that I am using as a menu. The cells are the options to switch to another page. The menu functions exactly how it should except that when you press a cell, say cell 0, it should pop to the next view. What I am finding is that the cell is registering the touch but when I try and determine which cell was pressed is when it falls apart. I have tried debugging it and to me it looks like indexPath has no value! I am using the didSelectItemAtIndexPath function, no it is not didDeselect (I checked that already from my searches on how to fix this). I will post the code but this one has really stumped me. Any help would be greatly appreciated!
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
NSLog("Pressed Cell")
if(indexPath == 0)
{
self.navigationController?.popToViewController(profileViewController, animated: true)
}
}
NSIndexPath comprises both a section and an item, which you can access as indexPath.item and indexPath.section. Assuming you have only one section (so its value is irrelevant), you can change your code to:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
NSLog("Pressed Cell")
if(indexPath.item == 0)
{
self.navigationController?.popToViewController(profileViewController, animated: true)
}
}
I am sure normally call the delegate method but if have add any Gesture to the view. Make sure before you add your gesture recognizers to you're view, make sure the boolean property "cancelTouchesInView" is set to false. This could be because the gesture is recognized so it ignores passing touches to the view causing the cell selection method not to be called. Link to property.
I have a static UITableView with several sections. In one table row I have a UIDatePicker. On touch the table cell expands and I can select the date. Fine so far. But if the table row is on the bottom of the page I need to manually scroll up to select a date. How can I ensure the datepicker to be in view like the calendar app does? Can you please point me into the right direction?
You can use this function:
func scrollToRowAtIndexPath(indexPath: NSIndexPath, atScrollPosition scrollPosition: UITableViewScrollPosition, animated: Bool)
Use it in
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// ...
var indexPathToJump = NSIndexPath(forRow: 0, inSection: 5)
tableView .scrollToRowAtIndexPath( indexPathToJump, atScrollPosition: .None, animated: true)
}
}
Use scrollToRowAtIndexPath:atScrollPosition:animated::
self.tableView.scrollToRowAtIndexPath(myIndexPath, scrollPosition: .None, animated: true)
I haven't tested this syntax, please let me know if it needs improved, but I know that the method is right. You want to use UITableViewScrollPosition.None so that it move the table view just enough that the row in question is in view:
UITableViewScrollPositionNone
The table view scrolls the row of interest to be fully visible with a minimum of movement. If the row is already fully visible, no scrolling occurs. For example, if the row is above the visible area, the behavior is identical to that specified by UITableViewScrollPositionTop. This is the default.
Available in iOS 2.0 and later.