UICollectionView trigger cell animation when bottom of cell is visible - ios

Using a UICollectionView, I am trying to trigger in each cell an animation that would add some views in it. The thing is that I need to trigger these animations only when the bottom of the cell passes the bottom of the screen.
Of course, iOS provides methods that can tell me when a cell is about to be displayed, but I want to trigger my animation really when the cell is fully visible on screen (otherwise user won't see the animation)
Here is what I do and that actually works but there is probably a better option :
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let cells = self.collectionView.visibleCells
for cell in cells {
if cell is PublicationSingleMediaCollectionViewCell {
guard let indexPath = collectionView.indexPath(for: cell) else {
return
}
guard let frame = self.collectionView.layoutAttributesForItem(at: indexPath)?.frame else {
return
}
if self.collectionView.bounds.contains(frame) {
let cell = cell as! PublicationSingleMediaCollectionViewCell
cell.animate()
}
}
}
}

Related

DidSelect highlight changing view color is not working

I have one collection view, where in each cell i have one background view. So whenever user select any cell that particular cell view background color will change.
But now the problem is its background color is changing...but if i select another cell the previous selected cell view background color should be change to normal color.That is not happening.
the previous cell view background color also still as selected state
here is my vc didselectmethod :
let cell = chartCollectionView.cellForItem(at: indexPath) as? UserDataVC
var data = [String: Any]()
data["selectedCell"] = true
cell?.set(dataSource: data)
my collectionview cell :
class userCell: CollectionViewCell {
override func set(data: [String : AnyObject]) {
if let selectedCell = data["selectedCell"] as? Bool {
if selectedCell {
mainView.backgroundColor = UIColor.red
}
}
}
}
Any solution would be helpful
What's mainView?
You should generally change either the cell's contentView or assign a backgroundView and selectedBackgroundView to a cell.
Additionally and a bit unrelated, I would use a different method for changing content in response to events:
I would never change a cell directly by down casting it.
Instead, I would change the data I use to initialize the cells, and then reload the cells I want to change, and then re-create (actually, it would probably be re-used) the cells as needed using collectionView(_:cellForItemAt:).
This way, you never need to down cast.
Your current implementation is very poor.
I'd suggest saving the highlighted cell's index as a property, and accessing it to highlight or unhighlight when it dequeues. For example:
// Your CollectionView Delegate class
var currentHighlightedCellIndex: Int?
Then in your collectionView(_:cellForItemAt:):
// Dequeue the cell
...
if let selectedIndex = self.currentHighlightedCellIndex, selectedIndex == indexPath.row {
cell.selectedCell = true
} else {
cell.selectedCell = false
}
// Return the cell
...
In your collectionView(_:didSelectItemAt:):
guard let cell = collectionView.cellForItem(at: indexPath) as? YourCustomCellClass else { fatalError() }
cell.selectedCell = true
if let previouslySelectedIndex = self.currentHighlightedCellIndex {
// Here we get the index paths for each visible cell and check wether it's selected.
let indexPaths = collectionView.visibleCells.compactMap { collectionView.indexPath(for: $0) }
for index in (indexPaths.map { $0.item }) {
if index == previouslySelectedIndex {
// We found a cell that is highlighted and visible, get it in deselect it.
guard let cell = collectionView.cellForItem(at: IndexPath(item: index, section: 0)) else { fatalError() }
cell.selectedCell = false
}
}
}
self.currentHighlightedCellIndex = indexPath.item
You can set an observed property in your custom collection view cell class that will control the background color, like so:
// Custom cell class
var selectedCell: Bool = false {
didSet {
self.mainView.backgroundColor = selectedCell ? .red : .white
}
}

How to find the index of the top cell of the table view Even when it is scrolling in swift

I have a table view. Now I need to know the index of the top of the table view cell.
For example when we scroll down the topmost table view cell changes. I always want the top of the table view cell is returned.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let visiableCells = tableView.visibleCells
if let cell = visiableCells.first as? InfoTableViewCell {
print(cell.textLabel?.text)
}
}
Where InfoTableViewCell is Custom Cell
Use this property of UITableView
var visibleCells: [UITableViewCell] { get }
to get the first visible cell
let firstVisibleCell = yourTableView.first
Read more from Appledoc

cell stored as var using tableView.cellForRow(at: indexPath) is changing (iOS)

I'm using a longPress gesture recognizer to rearrange cells by creating a snapshot of the cell and detecting the snapshot's Y position, and rearranging the table accordingly when the long press ends. Everything works great.
One issue however, is that when I scroll down off the screen, the cell that I stored with tableView.cellForRow(at: method seems to change. I know this is because the cell is being dequeued b/c it is a reusuable cell, but how do I create a reference to this which doesnt change?
fileprivate var cellBeingMoved: MediaCell? //var that is changing
func longPressGestureRecognized(_ gestureRecognizer: UIGestureRecognizer) {
switch state {
case UIGestureRecognizerState.began:
if indexPath != nil {
if let cell = tableView.cellForRow(at: indexPath!) as? MediaCell {
//This variable is changing when the cell goes off the screen
self.cellBeingMoved = cell
self.createScreenshotOfCell(cell)
}
}
case UIGestureRecognizerState.changed:
//detect Y position
default:
//rearrange table,
}
}

In Swift, how do I force a UITextView in a UITableViewCell to update with correct margins on first load?

Using Xcode 7.3 here, wanting to adjust my UITextView margins, but it's not working on all instances when my view loads. Only when scrolling table rows off-screen, then back on, do the UITextViews correct themselves.
I'm using this code in a configureCell function for a custom PostCell:
self.movieSummary.setContentOffset(CGPointMake(5.0, 10.0), animated: false)
...but it only gets applied to the top row's UITextView on first load!?
Here are two screenshots which show the problem with the UITextView highlighted in red...
1) On first load: Only the top row is OK. Other rows are misaligned.
2) After scrolling down and back up: All rows are correctly aligned.
How can I force all the rows/cells/UITextViews to update on first load?
EDIT 1: Here's my code for cellForRowAtIndexPath:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let post = moviePosts[indexPath.row]
if let cell = tableView.dequeueReusableCellWithIdentifier("PostCell") as? PostCell {
cell.request?.cancel()
var posterImg: UIImage?
if let url = post.moviePosterImgPath {
posterImg = FeedVC.imageCache.objectForKey(url) as? UIImage
}
var bgImg: UIImage?
if let url = post.movieBgImgPath {
bgImg = FeedVC.imageCache.objectForKey(url) as? UIImage
}
cell.configureCell(post, posterImg: posterImg, bgImg: bgImg)
return cell
} else {
return PostCell()
}
}

UITableView jumpy on scroll after changing cell height

So I have a tableView where I want to change the height of a cell when tapped. Well, actually, I am replacing it with a bigger cell.
On tap, I call:
tableView.beginUpdates()
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
tableView.endUpdate()
And then I modify my cellForRowAtIndexPath to return the right new cell and height. The cell's height is being automatically calculated by overriding sizeThatFits in the cell's implementation:
override func sizeThatFits(size: CGSize) -> CGSize {
return CGSizeMake(size.width, myHeight)
}
Oddly enough, after I do this, scrolling downwards is fine, but when I scroll upwards, the table jumps 5 or so pixels every second until I reach the top. After I reach the top of the table, the problem is gone and there is no jumping going in either direction. Any idea why this is happening? I imagine it has something to do with the new cell height displacing the other cells, but I can't see why the tableView is not taking care of this. Any help is appreciated!
Thanks!
EDIT: Added code from cellForRowAtIndexPath:
if self.openedCellIndex != nil && self.openedCellIndex == indexPath {
cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as ListCell
(cell as ListCell).updateWithDetailView(dayViewController!.view)
} else {
cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as ListCell
(cell as ListCell).updateWithData(eventDay: store.events![indexPath.row], reminderDay: store.reminders![indexPath.row])
}
return cell
You should include your code for cellForRow and heightForRow, but I will give it a blind shot.
When a cell is tapped in cellForRow you should store the index of that cell, then reload the data or just that cell. Then in heightForRow use if(yourTappedCell){return preferredHeight;}
Unfortunately, there does not seem to be a simple answer to this. I have struggled with it on multiple iOS apps.
The only solution I have found is to programmatically scroll to the top of your UITableView once it appears again.
[self.tableView setContentOffset:CGPointMake(0, 0 - self.tableView.contentInset.top) animated:YES];
OR
self.tableView.contentOffset = CGPointMake(0, 0 - self.tableView.contentInset.top);
Hope this an acceptable work around while still being able to use dynamic cell heights =)
func refreshCellState(indexPath:IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath) as? ListCell else {
return
}
if self.openedCellIndex != nil && self.openedCellIndex == indexPath {
cell.updateWithDetailView(dayViewController!.view)
} else {
cell.updateWithData(eventDay: store.events![indexPath.row], reminderDay: store.reminders![indexPath.row])
}
self.tableView.beginUpdates()
self.tableView.endUpdates()
if #available(iOS 11.0, *) {
self.tableView.performBatchUpdates {
} completion: { complete in
if complete {
self.tableView.scrollToRow(at: indexPath, at: .none, animated: true)
}
}
} else {
// Fallback on earlier versions
self.tableView.reloadData()
}
}
Here, Dont reload that cell entirely, But take that cell when clicked and provide data to cell from there.
Now call begin and endUpdated methods of tableview to update height.

Resources