I'm trying to animate a table view cells on the initial load. The animation works fine for all the cells, except for the last one at the bottom of the table which refuses to animate.
This is because the UITableView.visibleCells does not return this cell at the end. (it returns cells 0-12, the last cell is at index 13 and is clearly visible)
Here is the code. Is there anything I can do to ensure all the cells get animated?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
let cells = tableView.visibleCells
let tableHeight: CGFloat = clubsTable.bounds.size.height
for i in cells {
let cell: UITableViewCell = i as UITableViewCell
cell.transform = CGAffineTransformMakeTranslation(0, tableHeight)
}
var index = 0
for a in cells {
let cell: UITableViewCell = a as UITableViewCell
UIView.animateWithDuration(1.5, delay: 0.05 * Double(index), usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
cell.transform = CGAffineTransformMakeTranslation(0, 0);
}, completion: { (complete) in
})
index += 1
}
}
That is because the height of your table is less than the height of 13 cells. Thus it animates 12 cells. What you can do is to make the table height bigger in 30px (or any px until it contains 13 cells), and after the animation is done, change the height of the tableView back to normal.
after you call let cells = tableView.visibleCells can't you just do something like
let indexPath = NSIndexPath(forRow: cells.count, inSection: 0)
cells.append(tableView.cellForRowAtIndexPath(indexPath))
Going off memory on those function signatures but you get the point.
Related
I have a problem while scrolling my collectionView
after choosing a cell, the last cell is not available for choosing
func changeViewSize(from: CGFloat, to: CGFloat, indexPath: IndexPath) {
UIView.transition(with: myCollection, duration: 0.5, options: .beginFromCurrentState, animations: {() -> Void in
let cell = self.myCollection.cellForItem(at: indexPath)!
let cellCenter = CGPoint(x: cell.center.x, y: cell.center.y + 50)
if cell.bounds.height == from {
let sizee = CGRect(x: cell.center.x, y: cell.center.y, width: cell.frame.width, height: to)
self.myCollection.cellForItem(at: indexPath)?.frame = sizee
self.myCollection.cellForItem(at: indexPath)?.center = cellCenter
for x in self.indexPathss {
if x.row > indexPath.row {
print("this is cells")
print(self.myCollection.visibleCells)
let cell = self.myCollection.cellForItem(at: x)!
cell.center.y += 100
}
}
}
}, completion: {(_ finished: Bool) -> Void in
print("finished animating of cell!!!")
})
}
It looks like you're changing cell sizes and positions manually. The UICollectionView won't be aware of these changes and therefore its size is not changing. The last cells simply move out of the visible area of the collection view.
I haven't done something like this before, but have a look at performBatchUpdates(_:completion:). I'd try to reload the cell that you want to make bigger using that method. Your layout will have to supply the correct attributes, i.e. size.
From the screenshots it looks like maybe you could use a UITableView instead? If that's a possibility it might simplify things. It also has a performBatchUpdates(_:completion:) method, and the UITableViewDelegate would then have to provide the correct heights for the rows.
Because of spacing of cell we used collectionView and I think changing height of tableviewcell is easier than collectionviewcell if you have an idea for spacing of tablviewcell wi will be glad to have your idea.
I am making an app using swift.
I have a UITableView consisting of 5 different rows.
I have a UILabel on each row of the table. (This label provides the title of the row)
This UILabel is animated so it enters the screen from left to right on each row.
My problem is that when I run my animation all 5 UILabels enter their row at the same time. I want to put a delay between the each UILabels animation. That is I want the UILabel on the first row to enter the screen first, then 1 second later I want the UILabel on the second row to enter the screen, then 1 second later the 3rd UILabel ...etc.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell!
let imageView = cell.viewWithTag(1) as! UIImageView
imageView.image = UIImage(named: imageArray[indexPath.row])
let imageText = cell.viewWithTag(2) as! UILabel
imageText.text = sectionName[indexPath.row]
imageText.textAlignment = .Center
//starting position for animation of imageText
imageText.center.x = self.view.frame.width - 500
//animating UILables comming into screen
UIView.animateWithDuration(3, delay: 0.0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [], animations: ({
imageText.center.x = ((self.view.frame.width) / 1)
}), completion: nil)
return cell
}
Add a delay based on the index path. The 1 in the delay is the number of seconds you want between animations.
let delay = 1 * indexPath.row
UIView.animateWithDuration(3, delay: delay, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [], animations: ({
imageText.center.x = ((self.view.frame.width) / 1)
}), completion: nil)
I'm assuming that the 5 rows are all that the table view will need to display and that all are on screen at all times, meaning, you don't need to consider how to handle the animation as the rows scroll. If scrolling is a consideration, then this will likely need to change to only animate in when the table view is initially displayed.
I'm adding simple stack animation to my tableViewCell which animates like cells are being add to a stack. When I tap the UIButton that segues me to the tableView I first see a static cell which has my values and then after a small gap my animation works. I don't know why my tableView shows that cell before animation ? Here is my code for the VC :
override func viewDidAppear(_ animated: Bool) {
animateTable()
}
func animateTable() {
let cells = tableView.visibleCells
let tableHeight: CGFloat = tableView.bounds.size.height
for i in cells {
let cell: UITableViewCell = i as UITableViewCell
cell.transform = CGAffineTransform(translationX: 0, y: tableHeight)
}
var index = 0
for a in cells {
let cell: UITableViewCell = a as UITableViewCell
UIView.animate(withDuration: 1, delay: 0.05 * Double(index), usingSpringWithDamping: 0.9, initialSpringVelocity: 0, options: .curveEaseInOut, animations: {
cell.transform = CGAffineTransform(translationX: 0, y: 0);
}, completion: nil)
index += 1
}
}
When your view appears, it would have the UITableView data already populated via a call to reloadData. You do the animation only after the view appears. So at that point, you'll see the existing data for a fraction of a second and then the animation would kick in.
If you don't want the data to appear at all before the animation runs, you might want to not show data as soon as the view loads by doing something like the following - this might not be the best solution, but it is the easiest to implement:
1: Add a new variable to indicate whether you've run the animation or not.
var wasAnimated = false
2: Check this variable in numberOfRowsInSection and return 0 if the animation has not run yet.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if !wasAnimated {
return 0
}
// Rest of the original code
}
3: Set the flag at the beginning of animateTable and then reloadData before you execute the rest of the animation code.
func animateTable() {
wasAnimated = true
tableView.reloadData()
// The rest of the original code
}
That should get you the behaviour you wanted :)
I am trying ot troubleshoot a tableView header (pink) that is animating a collapse. As the tableViewHeader height is shrinking the table view cells should pull up with the top of their tableView (orange). The beginning and end states are correct, but somehow the table view cells are animating up at a different rate. Something is clearly wrong here, I just can't seem to pinpoint what it is.
It appears to have something to do with the fact that I am using self sizing table view cells and tableView.rowHeight = UITableViewAutomaticDimension. If I use fixed height cells everything is fine.
Beginning State:
Middle State (Note cells already sliding under header):
Final State (Final state of layout is correct):
Here is the code that animates the collapse.
func collapseHeader() {
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn, animations: {
self.tableView.beginUpdates()
if let header = self.tableView.tableHeaderView as? TopicTableHeaderView {
header.setHeaderState(state: .collapsed)
}
self.sizeHeaderToFit()
self.view.layoutIfNeeded()
self.tableView.endUpdates()
}) { (bool) in
print("collapse completed")
}
}
func sizeHeaderToFit() {
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
var frame = headerView.frame
frame.size.height = height
if headerView.frame.height != height {
headerView.frame = frame
tableView.tableHeaderView = headerView
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
}
}
}
And the problem was simply where I was calling self.tableView.beginUpdates(). I moved that to the line directly above self.tableView.endUpdates() and that solved the problem.
I haven't been able to find any examples of this online. How can I achieve the following effect? Instead of the standard slide-to-left effect when tapping a table row, I'd like the view controller transition animation to look like the following:
User taps a cell in the table
The cell starts growing to fill the screen, pushing other rows above and below it "offscreen".
As the cell grows, cells elements (text, images, etc.) cross-fade into the new view's contents until the new view completely fills the screen.
I'd like to also be able to interactively transition back into the table view by dragging up from the bottom edge, such that the reverse of the above is achieved. i.e. view starts shrinking back into a normal table view cell as the "offscreen" cells animate back into position.
I've thought about taking a snapshot of the table and splitting it up at the points above and below the cell, and animating these snapshots offscreen as part of a custom view controller transition. Is there a better way? Ideally I'd like to not take snapshots, as I may want to have animations, etc., still happening in the table view rows as they fade offscreen.
First thing first, i have seen your post today and his is written in swift programming language.
the follwing code is to expand the selected cell to the full screen, where the subviews will fade out and background image will expands.
First complete cellForRowAtIndexPath, then in didSelectRowAtIndexPath,
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
isSelected = true
selectedCellIndex = indexPath.row
tableView.beginUpdates()
var cellSelected: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
cellSelected.frame = tableCities.rectForRowAtIndexPath(indexPath)
println("cell frame: \(cellSelected) and center: \(cellSelected.center)")
let newCell = cellSelected.frame.origin.y - tableOffset
println("new cell origin: \(newCell)")
var tempFrame: CGRect = cellSelected.frame
variableHeight = cellSelected.frame.origin.y - tableOffset
tempFrame.size.height = self.view.frame.height
println("contentoffset: \(tableView.contentOffset)")
let offset = tableView.contentOffset.y
println("cell tag: \(cellSelected.contentView.tag)")
let viewCell: UIView? = cellSelected.contentView.viewWithTag(cellSelected.contentView.tag)
println("label: \(viewCell)")
viewCell!.alpha = 1
UIView.animateWithDuration(5.0, delay: 0.1, options: UIViewAnimationOptions.BeginFromCurrentState, animations: {
tableView.setContentOffset(CGPointMake(0, offset + self.variableHeight), animated: false)
tableView.contentInset = UIEdgeInsetsMake(0, 0, self.variableHeight, 0)
tableView.endUpdates()
cellSelected.frame = tempFrame
viewCell!.alpha = 0
println("contentoffset: \(tableView.contentOffset)")
}, completion: nil)
}
then update your heightForRowAtIndexPath, as
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if isSelected && (selectedCellIndex == indexPath.row) {
return self.view.frame.height
}else {
return 100
}
}
Ignore this if already solved. and this might be helpful to others.