Multiple touch when scrolling in TableView (change speed or direction) - swift

I'm in the latest step of my TableView and I have a very simple problem: I would like to scroll (for example) direction to top and when I touch again the table (direction to bottom) it change the direction of the scroll.
At the moment, the problem is when I scroll to the bottom I can't scroll to top until the animation is finished.
Question:
How can I enable multi touch in my table view in order to change the direction of the scroll while the animation isn't finished?
Thanks in advance,
Regards.

All you need to do to stop an animation in progress is the following:
table.layer.removeAllAnimations()
Then you can start the new animation! Easy as pie!

If someone is having the same problem, the solution is modify the animateWithDuration in the willDisplayCell:
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
// Define the initial state (Before the animation)
cell.alpha = 0.25
// Define the final state (After the animation)
UIView.animateWithDuration(1.0, delay: 0.0, options: UIViewAnimationOptions.AllowUserInteraction, animations: { cell.alpha = 1 }, completion: nil)
}
The problem was the animation in my table cell.
Regards.

Related

How I can sync animations in Table View with Swift

I have a table view with animation in each cell, the animation is a circle blinking but when made scroll the new row blink in different time. The idea is all circles blink at same time.
Thanks by any help. I had 2 days try different things. The last is make my own animation with a timer and save the time in global variable. But i think that core animation should have something for do that.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myClassCell", for: indexPath) as! MyClassCell
cell.circleBlinking.alpha = 0
View.animate(withDuration: 1, delay: 0, options: [.repeat, .allowUserInteraction, .curveLinear], animations: {
cell.circleBlinking.alpha = 1} )
}
return cell
}
Finally i found a solution for the requirement. I made the animation with a timer.
I created a global timer that change a global variable each 1/24 secs from 0 to 1 and from 1 to 0
When pass by cellForRowAt I create other timer just for that cell but the function is just give the value alpha from my global variable that is doing modified by the main timer. So that way I have all circles sync
If somebody know how do that with animate welcome with your answer.
Already I uploaded the code to Github with solution.
https://github.com/wilmanro77/TableViewWithAnimationInCells

Make the last row's bottom be the tableview's bottom (if enough rows)

Is there any way to avoid the tableview keep scrolling when reaching the bottom cell?
So far I managed to do this:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.row == dataArray.count - 1 {
tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
}
}
and that allows me only to stop the scroll when the user "rolled" the table to bottom. But after that the user can scroll again. Only if I could disable the "down" scrolling I could achieve it. Enabling it again as soon as it moved.
But it seems a lot of work just to do that, maybe I'm missing some property that fixes the last cell bottom to tableview's bottom.
Any ideas?
There are 3 things you need to do.
tableView.footerView = UIView()
tableView.bounces = false
And after the tableView has been reloaded.
tableViewHeight(your constraint).constant = tableView.contentSize.height
This will cause the tableView to only display the populated cells, you will restrict the height to be as high as the content and you can't bounce the content as you want it to stop at last cell.
Remember that an UITableView is a subclass of UIScrollView
Starting here, you have 2 options :
1 - Disable the bounces effect : tableView.bounces = false
2 - Prevent scroll further than the bottom of your tableView :
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y + scrollView.bounds.size.height > scrollView.contentSize.height {
scrollView.contentOffset.y = scrollView.contentSize.height - scrollView.bounds.size.height
}
}
You can do by disabling it directly from Storyboard:
Disable the Bounce on scroll to Untick
Also if u want to disable the lines coming at bottom when data is less you can use:
tableView.footerView = UIView()

UICollectionView inside a tableviewcell: scrollToIndex not working

I have created a simple UICollectionView inside of a tableviews, UITableviewCell. The collection view is set to scroll horizontally. I am trying to scroll to a certain element within the collection view using scrollToIndex at:, I have placed the scroll request in the tableviewcell's layoutSubviews method. When the collection view is displayed, no scrolling takes place.
Here is the tableviewcell's layoutSubviews code:
override func layoutSubviews() {
super.layoutSubviews()
let indexPath = IndexPath(item: 1, section: 0)
self.collectionView.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition.left , animated: false)
}
It's quite simple, so really not sure why this is not working?
Many thanks for any help!

Cell loaded in cellForItemAt, but not in visibleCells

I have a custom UICollectionView and the cells are loaded in cellForItemAt but when I try to get all the visible cells by using visibleCells I'm not getting all the cells.
For example, in cellForItemAt, I'm setting the alpha of the labels in the cells to 0. When panned, I want the alpha of those labels change to 1:
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
handleLabel(scrollView, active: true)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if pickerIsActive { handleLabel(scrollView, active: false) }
}
private func handleLabel(_ scrollView: UIScrollView, active: Bool) {
guard let pickerView = scrollView as? UICollectionView else { return }
let cells = pickerView.visibleCells.flatMap { $0 as? CustomCell }
panningIsActive = active
UIView.animate(duration: 0.3) {
cells.forEach { $0.label.alpha = $0.isSelected || active ? 1 : 0 }
}
}
And cellForItemAt:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CustomCell
cell.label.alpha = 0
return cell
}
What can I do to change all the "loaded" cells instead of just the "visible" cells?
The visibleCells are only the on screen cells. This used to be everything initialized in cellForItem:at: but as of iOS 10 UICollectionView now prefetches to improve scrolling performance (see WWD 2016 video) which maybe why you are having this problem. Anyways it sounds like all you want to do is animate the cells to fade in when they come on screen. You can either move your animation logic to willDisplayCell or subclass UICollectionViewCell. UIColectionViewCell inherits from UIView, so you can override didMoveToSuperView in your UICollectionViewCell and call your animation method there, which will cause the cell to animate as it appears.
I am using Xcode 11.4 and Swift 5, and I had the exactly the same issue: .visibleCells is not giving me all the loaded cells.
By reading #Josh Homann's answer and the comments below, I figured out 2 solutions.
The first solution is same as the solution you reached at: customize cell appearance in collectionView(_:willDisplay:_:) after it's loaded but before it's displayed on the screen.
Another quick and dirty solution is to simply uncheck UICollectionView's 'Prefetch' option in attributes inspector.
This fixes the issue because by disabling prefetching, UICollectionView will stop pre-loading cells that are not displayed on the screen, so .visibleCells are now all the loaded cells. This solution will work fine if you're simply loading static or small local data in the cells. If you're prefetching large data (e.g. images) from network for upcoming cells, you probably need Prefetching Enabled, then solution 1 is your go-to option.
It sounds like you might want to try using layoutAttributesForElements(in:).
You'll need to implement your own collection view layout subclass (rather than using the delegate methods) but I think it will be worth it in the long term.
Rather than manually managing the animations (via UIView.animateWithDuration) you use this method to tell the collection view what properties cells should have at different positions, and as people pan the collection view, the correct properties are automatically applied.
I tried to find a good Swift reference for this, but I could't, but here's a post in Objective-C that you can follow if you want to try this approach:
https://bradbambara.wordpress.com/2014/05/24/getting-started-with-custom-uicollectionview-layouts/

Independently auto fade UITableView rows starting with the bottom row

Examples of row deletion usually implement textFieldDidEndEditing or some other gesture / user input method.
My version of an auto-deletion feature is to use animateWithDuration with alpha. It's simple and looks good.
The challenge is I would like to do this automatically via NSTimer or animateWithDuration independently of any user scrolling and user input. Just a simple fade-out starting with the newest bottom row and have it stay alpha 0 as it moves up the table.
Currently you don't see any rows when you open the app because the rows have already faded out. This is good. The problem occurs every time a user sends a new posts all the "invisible" rows appear again starting at alpha 1 and then all fade out together.
I need the previous rows above to stay at alpha 0 with only the new bottom row having alpha 1 then it fades away to alpha 0.
Is it because I need to use cellForRowAtIndexPathinstead? Or do I need to use the dequeueReusableCellWithIdentifier method?
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = super.collectionView(collectionView, cellForItemAtIndexPath: indexPath) as! CHEFTALKRecipeViewCell
let recipe = recipes[indexPath.item]
if recipe.posterId == posterId {
cell.textView!.textColor = UIColor.blackColor()
// STEP 1
cell.contentView.alpha = 1
} else {
// do something
}
return cell
}
// STEP 2
override func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) {
UIView.animateWithDuration(2.0) {
cell.contentView.alpha = 0
}
}
As much as I understand your problem is that all cells animate. Even the ones at the top.
If so, you can retrieve -visibleCells from UICollectionView and start animation if only their position is at the bottom of the screen.