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.
Related
I am trying to achieve an effect that when detail button is pressed, details will slide in to ALL cells of tableView. So far I have achieved this trough iterating through the visible cells of the table view. Each cell has a detail label that is off screen that slides in with animation. This is how it looks:
Before Detail Button Pressed
After detail button pressed
Of cours since I am iterating through the visible cells, the cells at the bottom dont have the details when scrolling down after having pressed the detail button. I am aware of the fact that the cells that are not shown on screen is not in memory, but maybe there is an another approach to this?
EDIT
Here is the code that executes when user presses "Details" button
let animationDuration = 0.4
for cell in myTableView.visibleCells {
let cell = cell as! MainCell
if !detailView {
sender.tintColor = UIColor.gray
detailView = true
UIView.animate(withDuration: animationDuration, animations: ({
cell.noteLbl.center.x = cell.noteLbl.center.x + 100
}))
UIView.animate(withDuration: animationDuration, animations: ({
cell.accessoryLbl.center.x = cell.accessoryLbl.center.x + 100
}))
UIView.animate(withDuration: animationDuration, animations: ({
cell.timeLbl.center.x = cell.timeLbl.center.x + 100
}))
} else {
detailView = false
sender.tintColor = UIColor.white
UIView.animate(withDuration: animationDuration, animations: ({
cell.noteLbl.center.x = cell.noteLbl.center.x - 100
}))
UIView.animate(withDuration: animationDuration, animations: ({
cell.accessoryLbl.center.x = cell.accessoryLbl.center.x - 100
}))
UIView.animate(withDuration: animationDuration, animations: ({
cell.timeLbl.center.x = cell.timeLbl.center.x - 100
}))
}
}
Code for the cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainCell") as! MainCell
let shiftForRow = shifts[indexPath.section][indexPath.row]
cell.noteLbl.text = shiftForRow.note
cell.dateLbl.text = shiftForRow.date
cell.accessoryLbl.text = calcTotalHours(STField: shiftForRow.startingTime!, ETField: shiftForRow.endingTime!, lunchTime: shiftForRow.lunchTime!)
cell.timeLbl.text = shiftForRow.startingTime! + " - " + shiftForRow.endingTime!
return cell
}
As you scroll a table view (and when initially displayed), the cellForRowAt method is called for each cell that needs to be displayed.
At the moment your cellForRowAt only shows your cells in their "normal" layout as if the detail button has never been pressed. You need to update your cellForRowAt method so it configures the cell based on whether details should be shown or not.
You probably need a flag to track whether details are currently being shown or not. Toggle that flag based on the detail button being pressed.
Then, based on that state of that flag, your cellForRowAt method needs to setup the cell appropriately.
I have a uitableview cell and it will animate when the cell is displayed. These cells are in tableviews and these tableviews are tabs of a uitabbarcontroller. But if a user presses one of the four tabs for the first time then the cells will animate, but if a user clicks a new tab and goes back to the previous tab the cells will not animate. How can I make it so that the cells animate everytime the tableview is presented. Here is my code, can someone please show me how i can make the cell animate each time the view is presented and not only when the cell is presented.
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let rotationTransform = CATransform3DScale(CATransform3DIdentity, 10, 10, 0)
cell.layer.transform = rotationTransform
UIView.animate(withDuration: 0.6, delay: 0, usingSpringWithDamping: 0.2, initialSpringVelocity: 0.1, options: .curveEaseOut, animations: {
cell.layer.transform = CATransform3DIdentity
}, completion: nil)
}
Reload the data on view will appear controller method:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableview.reloadData()
}
Also, you may need to reset to the starting state before you animate again. why? because when you scroll your table view the cells that appears will not animate. they will have the final state after animation (reusable cells).
Another thing that you may scoll and the top cell will disappear before completing the animation. I am not sure what would happen. maybe it will not take its final state. and when you get back to it or that cell get reused that cell will not have the final state. but, if you reset to the first state before you start animating again that will solve all the problems.
reset animation:
cell.layer.transform = nil
// you may need to layout the cell. not sure. after this.
// then the rest of your code that animates the cell.
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.
I can't figure out why animation of tableViewcell frizzes when tableView is pulled out out of screen or stoppes when you start to close menu.
To let you better understand the problem, here is a gif
I implement Tap Gesture Recognizer at custom UITableViewCell Class
let tap = UITapGestureRecognizer(target: self, action: "tapAction")
self.addGestureRecognizer(tap)
func tapAction() {
let animationWidth = leftMenuWidth * 0.27
UIView.animateWithDuration(0.75, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .AllowUserInteraction, animations: {
self.colorIndicator.frame.size.width += animationWidth
}) { (true) in
UIView.animateWithDuration(0.75, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .AllowUserInteraction, animations: {
self.colorIndicator.frame.size.width -= animationWidth
}, completion: { (true) in
print("Animation Complete")
})
}
Also i implement sliding menu by using this cocoaPods - https://github.com/jonkykong/SideMenu
Thanks.
The cells are likely being reloaded which is causing the animations to reset.
Try tracking the state of whether or not a cell has been tapped so that when it's reloaded you can show the animation as already complete. You can do this by with a simple dictionary.
At the top of your view controller, define:
private var tapped = [Int : Bool]()
Next, in cellForRowAtIndexPath: for your tableView check:
if let isSet = tapped[view.hashValue] where isSet == true {
// display animation complete. You probably don't want to re-animate
// it if it's scrolled back into view, so just get it to the completed state.
}
Finally, switch your tap action from using a gesture in the cell itself to didSelectRowAtIndexPath: inside your tableView:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// remember the cell has been tapped
tapped[view.hashValue] = true
// call your method to display the animation on the cell,
// something like cell.showAnimation(). It shouldn't animate if
// already displaying the completed animation state.
}
Also, I wouldn't recommend using += or -= operators when calculating the frame since calling it multiple times will keep growing or shrinking it. Use explicit values instead, like = animationWidth or = 0.
I'm facing a bugging issue concerning the insertRowsAtIndexPaths function and precisely it's withRowAnimation parameter.
With the help of the stack community i've been able to modify the effect of insertion from fast fade to a longer fade (by overriding the function), but the whole effect completely disappears if i scroll the table view upwards, hide the cells from the view and scroll back again.
Because the cells are being reused the effect is rendered totally useless.
I've tried using scrollToRowAtIndexPath function (doesn't work in this case - it initiates after the cells have already loaded, only then it scrolls down and the effect has already gone away) as well as setting the content offset to tableView.contentSize.height - the result is somewhat satisfactory, but again doesn't prevent vanishing of the insertion effect when the user scrolls.
Also willDisplayCell is not an option, because it animates the cell every single time the user scrolls and i need the animation to run just once. Urrgh)
So far i'm out of ideas of how to prevent this issue from happening.
UPDATE:
Added the suclass code
class CustomTable: UITableView{
override func insertRowsAtIndexPaths(indexPaths: [NSIndexPath], withRowAnimation animation: UITableViewRowAnimation) {
self.endUpdates()
self.beginUpdates()
for indexPath:AnyObject in indexPaths{
let cell:UITableViewCell? = (super.cellForRowAtIndexPath(indexPath as! NSIndexPath));
if cell != nil {
cell!.alpha = 0
let animationBlock = { () -> Void in
cell!.alpha = 1;
}
if UIView.respondsToSelector(Selector("animateWithDuration(duration: , delay: , usingSpringWithDamping dampingRatio: , initialSpringVelocity velocity: , options: , animations: , completion: ")){
UIView.animateWithDuration(3, delay: 3.0, usingSpringWithDamping: 1.5, initialSpringVelocity: 0.0, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: animationBlock, completion: nil)
}else{
UIView.animateWithDuration(3.3, delay: 5.0, options:UIViewAnimationOptions.TransitionCrossDissolve, animations: animationBlock, completion: nil)
}
}
}
}
}