Independently auto fade UITableView rows starting with the bottom row - swift

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.

Related

Check table view cell fully visible

how are you
I need to know how can i determine table view cell fully visible for playing auto play movie in cell and also detect hiding the table view cell.
I have apply this code below in table view but not give me the correct solution.
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let cellRect = tableView.rectForRow(at: indexPath)
let isFullyVisible = tableView.bounds.contains(rectInFull)
if isFullyVisible {
// Play video
}
}
So please can you tell me how can i get the correct table view cell visible
cell.frame.origin.y >= tableview.contentOffset.y && cell.frame.origin.y + cell.frame.size.height <= tableview.contentOffset.y + tableview.bounds.size.height
You might want to implement the UIScrollViewDelegate method scrollViewDidScroll(_:) and in there you could do the check for visible cells in your table view:
func scrollViewDidScroll(_ scrollView: UIScollView) {
guard let tv = scrollView as? UITableView else { return }
tv.visibleCells.forEach { $0.playVideo() }
}
Of course assuming that method on the cell (playVideo()) exists and it takes care of being called multiple times when the video is already playing (i.e. must ignore the call if is already playing).
Otherwise if you need the indexPaths of visible cells or any fine tuning use either the property indexPathsForVisibleRows or the method indexPathForRows(in:) on the table view, then those index paths you'll optionally obtain inside an array will point to the model's objects (which might implement the play video logic).

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

How to tap on item in one controller and have it show in a different uicollectionviewcontroller class

I'm trying to tap on a collection view cell in one collection view controller and have it dynamically added to another collection view controller on same screen that when filled will turn into horizontal scrolling list of items. Similar to what is seen in the attached image of chickfila app when wanting to add pickles or bacon to order..
I have two different collection view controllers, one for the horizontal effect, shown on top of screen, and another controller showing the items that can be selected below. When I tap on items below I think I can get the array appended, but despite all kinds of reloadData calls, I can't get it to show this item in the horizontal controller above.. It doesn't seem to be calling didSelectItemAt again no matter what I do. I'm trying to do this just using the didSelectItemAt method.
Am I going about this totally wrong perhaps? I'm relatively new to the world of iOS programming. Thanks for any suggestions or help!!
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("tapped to add extra item somehow..")
let selectedItem = indexPath.item
if selectedItem >= 0 && indexPath.item < extras.count {
super.extras.append(Extras(imageName: "bacon", calories: 100, price: 1, quantity: 1))
}
indexPath.index only gives you the index number in an array of element, and not the element(object) you are looking for.
You might wanna try something like this:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(arrayHoldingYourObjects[indexPath.item].id)
collector.append(arrayHoldingYourObjects[indexPath.item])
print("This is your colector \(collector)")
}
var collector: [Any] = []

Checking If every row in table view have image hidden or not

Is there any way to check inside a function : func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) If rest of the rows ( instead of the first one ) have image hidden or not.
If images of rows which are greater then indexPath.row - 0 are all hidden or not.
So basically I would like to get the first row ( image ) of the table view hidden when the rest one are hidden as well. The simple check boxes.
This how I'm hiding them :
let row = indexPath.row
if row > 0 {
UIView.animate(withDuration: 0.5, animations: {
let currentCell = self.engineStatusTableView.cellForRow(at: indexPath) as! DropDownViewCell
if !currentCell.checkMark.isHidden {
currentCell.checkMark.isHidden = true
} else {
currentCell.checkMark.isHidden = false
}
})
}
Thanks in advance!
It's generally not a good idea to keep your state inside of cells. Cells are reused and somewhat expensive to create, so you want to let UITableView control the creation and manage their reuse.
But, hopefully, your datasource knows if the checkmark is supposed to be hidden or not and you can ask it without having to create a cell to do that.

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/