I am attempting to animate a tab bar to move from below the bottom of the screen to the top while simultaneously adjusting a view's height to shrink by the height of the tab bar. Essentially, I have a "hidden" tab bar that when it unhides should animate into view and the displayView should adjust for the space the tab bar now takes up.
However, the animation is jumpy for the display view. It seems that the display view animates fine, but the subviews automatically adjust their height without any animation. Any direction on fixing this would be appreciated.
I will accept aid in either objective-c or swift, as the translation is fairly easy.
//Displays tab bar with slide up animation. If animated is false, all other params are unused
func displayTabBar(animated:Bool, duration:NSTimeInterval = 0.5, delay:NSTimeInterval = 0, options:UIViewAnimationOptions = UIViewAnimationOptions.CurveLinear, completion:((Bool) -> Void)? = nil){
if(animated){
UIView.animateWithDuration(duration, delay: delay, options: options, animations: {
self.adjustTabBarDisplayed()
}, completion: completion)
UIView.animateWithDuration(duration, delay: delay, options: options, animations: {
self.adjustDisplayViewTabDisplayed()
}, completion: nil)
}
else{
self.adjustTabBarDisplayed()
self.adjustDisplayViewTabDisplayed()
}
}
//Adjusts frame of tab bar to display tab bar
private func adjustTabBarDisplayed(){
self.tabBar.frame = CGRectMake(0,UIScreen.mainScreen().bounds.height - self.tabBar.bounds.height, self.tabBar.bounds.width, self.tabBar.bounds.height)
}
//Adjusts frame of display view to match displayed tab bar
private func adjustDisplayViewTabDisplayed(){
self.displayView.frame = CGRectMake(0,0,self.displayView.bounds.width, UIScreen.mainScreen().bounds.height - self.tabBar.bounds.height)
}
When you modify a view's size, it doesn't lay out its subviews immediately. Instead, it sets a flag indicating that it needs layout. Later, after the system has finished dispatching the event that ended up calling displayTabBar, it runs the display refresh code. The display refresh code finds views that have the needs-layout flag set and tells them to lay themselves out (by sending them layoutSubviews).
Here, you are changing your display view's size inside an animation block. Therefore change to your display view's frame will be animated. But the frames of its subviews are changing outside the animation block; they're changing later during the layout phase. You need to make them change inside the animation block.
Lucky for you, that's easy. Just call self.displayView.layoutIfNeeded() inside the animation block. Also, you only need one animation block, since all of the animation parameters are identical:
func displayTabBar(animated:Bool, duration:NSTimeInterval = 0.5, delay:NSTimeInterval = 0, options:UIViewAnimationOptions = UIViewAnimationOptions.CurveLinear, completion:((Bool) -> Void)? = nil){
if(animated){
UIView.animateWithDuration(duration, delay: delay, options: options, animations: {
self.adjustTabBarDisplayed()
self.adjustDisplayViewTabDisplayed()
// ADD THIS LINE
self.displayView.layoutIfNeeded()
}, completion: completion)
}
else{
self.adjustTabBarDisplayed()
self.adjustDisplayViewTabDisplayed()
}
}
Use the below line of code in animation block
scrollView.layoutIfNeeded()
Related
I need to implement slot-machine animation according to provided design and timings.
It should perform infinite scroll, until some event will be triggered. After that animation, it should slow down and stop on defined position
For this task I have used next solution:
UITableView with fixed-height cell. It is the same cell with the only difference - icon or text (depends on indexPath.row)
Scroll is only down-to-up that's why I'm using last cell as start point in resetScrollPosition method
If first element reached, scroll position resets to start point
Animation performed as contentOffset change with linear option. In completion block, if animation is still needed, it's called again. If don't needed - slowing animation with easeOut option started
var isRolling: Bool = false
func startScroll() {
isRolling = true
UIView.animate(
withDuration: 0.05,
delay: 0,
options: .curveLinear,
animations: {
self.tableView.contentOffset.y -= self.rowHeight
},
completion: { _ in
if self.isRolling {
self.startScroll()
} else {
self.resetScrollPosition
UIView.animate(
withDuration: 0.7,
delay: 0,
options: .curveEaseOut,
animations: {
self.tableView.contentOffset.y -= 8 * self.rowHeight
},
completion: nil
)
}
})
}
private func resetScrollPosition() {
tableView.reloadData()
tableView.contentOffset.y = startOffset
tableView.reloadData()
}
func stopScroll() {
isRolling = false
}
The problems:
After calling resetScrollPosition, in animations completion block, tableviews contentOffset.y value is updated but tableView stays on the same position. I have tried to change direct contentOffset changing to setContentOffset, scrollToRow, scrollToRect, wrap it in main queue - no changes
Slowing animation should scroll 8 items. It's performed but first 6 items aren't visible during animation, only the last two.
Check the issue gif (jump 2 -> 11 is ok):
Replaced UITableView with UIScrollView
Uploaded code to gist - https://gist.github.com/OlesenkoViktor/76845c5448b421ead0a2303af2b1161d
Thanks #Paulw11 for his idea
The goal is to have the first table view cell content move left for a time and then back again.
The bigger goal is that we will bounce the cell's content view slightly to the left and bounce in a red box then return the cell to normal.
Although similar to another SO question, the answer does not reveal how to do this. Plus, this question would apply to anyone who wants to animate moving the cell content to the left temporarily and then back again. Thus, that's why it's a separate question.
The environment is iOS 11+ and iPhone app.
I have a new Table View project created that is animating the contentView moving via transform. However, it doesn't seem to remotely start in the normal position and then move as desired. The content starts off centered and then moves into place instead.
How can I get the contentView to animate moving a little to the left and then back again into its normal position?
Project: https://github.com/mikefinney/peekabooswipe
I rewrote your code a little. You were applying a transform to the content view and the documentation suggests instead animating the center property when shifting the location.
func applyCellAnimations() {
let originalCenter = contentView.center
let offsetCenter = originalCenter.applying(.init(translationX: -44, y: 0))
animateToCenter(offsetCenter) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
self.animateToCenter(originalCenter)
})
}
}
private func animateToCenter(_ center: CGPoint, completionHandler: #escaping () -> Void = { }) {
layoutIfNeeded()
UIView.animate(
withDuration: 1,
delay: 0,
options: [.curveEaseInOut],
animations: {
self.contentView.center = center
}, completion: { didComplete in
if didComplete { completionHandler() }
})
}
I want to have my tableview increase in size and moves up when scrolled down while also keeping the constraint to the bottom layout. I thought I would try CGAffineTransform.
func MoveUP() {
// pop up login screen
let bottom = CGAffineTransform(translationX: 0, y: -30)
UIView.animate(withDuration: 0.7, delay: 0.2, options: [], animations: {
// Add the transformation in this block
// self.container is your view that you want to animate
self.numberOfProperties.transform = bottom
self.tableview.transform = bottom
}, completion: nil)
}
The problem with this is that it moves up the tableview but does not keep the proper constraints to the bottom. Many apps seems to have this behavior, what tool am I missing in order to achieve this result?
How can I animate a stack view to slide up starting from x=0 up to y=500, I have the following method in the viewDidLoad() which does a growing effect.
StackView.transform = CGAffineTransformMakeScale(0.0, 0.0)
And then I added a growing effect in the viewDidAppear() method
UIView.animateWithDuration(0.4, delay: 0.0, options: [], animations: {
self.StackView.transform = CGAffineTransformIdentity
}, completion: nil)
After the viewDidLoad method executes, the stack view is minimized. When the viewDidLoad method completes, the viewDidAppear method is invoked, and the animation begins and the stack view begins to grow. The animation stops when the stack view reaches it's original size.
Although is a nice effect that's not what I want to accomplish, I want the animation to slide up from x = 0 and stops at y = 500 I tried to add the following code in the viewDidLoad to accomplish this effect, but I still get the same growing effect. Any suggestions on how to accomplish this?
StackView.transform = CGAffineTransformMakeTranslation(0, 500)
You´re almost there just make a few changes
// These values depends on the positioning of your element
let left = CGAffineTransformMakeTranslation(-300, 0)
let right = CGAffineTransformMakeTranslation(300, 0)
let top = CGAffineTransformMakeTranslation(0, -300)
UIView.animateWithDuration(0.4, delay: 0.0, options: [], animations: {
// Add the transformation in this block
// self.container is your view that you want to animate
self.container.transform = top
}, completion: nil)
I'm trying to build my own slide out menu in swift, but I'm having some troubles.
Now, I coded a function that change the view.frame.origin.x so that I got my view to slide on the right.
Now, I added a subview at index: 0 and I want to make it show when I slide the main view (content) out.
let navView: NavigationViewController = self.storyboard?.instantiateViewControllerWithIdentifier("navView") as NavigationViewController
view.insertSubview(navView.view, atIndex: 0)
addChildViewController(navView)
navView.didMoveToParentViewController(self)
And that's what happen when I click on the menu logo (at the top-left corner):
#IBAction func showMenu(sender: AnyObject) {
println("showMenu")
if !self.menuIsOpen{
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0, options: .CurveEaseInOut, animations: { () -> Void in
self.view.frame.origin.x = 150
}) { (isHappen: Bool) -> Void in
if isHappen {
println("OpenedUp!")
self.menuIsOpen = true
}
}
} else {
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0, options: .CurveEaseInOut, animations: { () -> Void in
self.view.frame.origin.x = 0
}) { (isHappen: Bool) -> Void in
if isHappen {
println("Closed!")
self.menuIsOpen = false
}
}
}
}
So, now, this works but I suppose that the subview slides together with the rest of the main view.
How can I make the navigationView not to hide? Or better, to stay there (I mean with the origin.x: 0)
Could you have a navigation view, associated with a view controller (1), and then have the slide out menu view controller (2) inside that view controller (vc1.addSubview(vc2.view)? Then you could just keep view controller (1)'s navigation bar up and the view controller (2) would be inside it so it does not slide view controller (1)'s navigation bar.
I know I'm a bit late, but in case anyone else has the same problem: I've implemented a similar mechanic on an app I'm currently working on. To achieve it I used custom interactive transitions, which were introduced in iOS7. I would recommend checking out the following links:
See Custom Transitions Using View Controllers https://developer.apple.com/videos/wwdc/2013/
http://www.thinkandbuild.it/ios7-custom-transitions/ In this tutorial, the presented view is rotated to appear on screen; but it's not to much work to adapt the code to silde your menu in from the side and only fill part of the screen.
http://www.appcoda.com/custom-view-controller-transitions-tutorial/ This gives many examples of the kind of effects you can achieve using custom transitions.
Hope that helps.