4

I am designing an iOS app in swift, and I am having some difficulty with animations during a controller transition. Specifically, I've implemented a UINavigationControllerDelegate, to listen for when a certain view is pushed. When this view is pushed, I want to hide a bar at the bottom of the screen. My code is working almost perfectly, however whenever I begin an animation on the height of the navigation controller, the current view (which is being removed) animates its height correctly, but the new controller which is being pushed already has the new height from the animation. To put some code to it, the following function is called from my UINavigationControllerDelegate's willShow viewController function:

func animatePlayerVisibility(_ visible: Bool) {

    if visible == showingPlayer {
        return
    }
    showingPlayer = visible

    let height: CGFloat = visible ? 56.0 : 0.0
    self.view.layoutIfNeeded()
    UIView.animate(withDuration: 0.35) {
        self.playerHeight.constant = height
        self.viewBottom.constant = height
        self.view.layoutIfNeeded()
    }
}

'playerHeight' is an IBOutlet to a constraint on the height of the player container view. 'viewBottom' is also an IBOutlet constraint between the bottom of the top container view and the bottom of the screen. Essentially, as long as these two constraints are animated together, it should look nice.

To help visualize the graphical bug, I edited this line

self.viewBottom.constant = height

to

self.viewBottom.constant = height * 2.0

I have created an imgur album of the actual wrong behavior in action: http://imgur.com/a/znAim

As you can see, the old view controller animates properly, when the new controller already has the new animated size.

Here is the layout of my storyboard: Storyboard

Any help would be really appreciated. I've been trying to fix this for a while with no success.

EDIT: The view of the animation without the *2 applied. https://imgur.com/a/2a5Sw

Jtvd78
  • 4,135
  • 5
  • 20
  • 21
  • Don't know if I'm understanding your question correctly but I would use UIViewControllerAnimatedTransitioning: hide the navigation bar of the view controller you're transitioning from and to, recreate it and add it to the context, animate its height as needed and when animation ends, remove it from context and unhide the target controller's navigation bar. It will all seem seamless in the eyes of the user – cyril May 17 '17 at 16:08
  • So, I am not trying to do anything with the navigation bar, and since I am only pushing to a navigation controller, the new view does not have a navigation bar. The problem that I am having is that when a UINavigationController's height is animated just before a view is pushed to it, the height of the view being pushed to the navigation controller has the future animated height instead of being constrained to the bottom of its containing navigation controller. I am not transitioning away from this storyboard, simply pushing a view to the nav controller in it. – Jtvd78 May 17 '17 at 22:42

2 Answers2

0

Have you thought about not using UINavigationController? Maybe it will be easier to use ChildViewControllers mechanism. Then with it you can use a powerful autolayouts and have more control over animation (in your case height)

More info about this here

Kamil Harasimowicz
  • 4,684
  • 5
  • 32
  • 58
  • Do you think you could provide a bit more detail on how you'd go about this? Most guides on this are for non-default looking transition behavior, but I was intending for the transition to still look 'stock' – Jtvd78 May 15 '17 at 17:50
  • What do you mean 'stock'? – Kamil Harasimowicz May 15 '17 at 17:52
  • The incoming view simply slides left to right. Right now all I want to do is change the height of the UINavigationController, but when I activate the height animation just before the transition, the incoming view has the new height, even though the animation has not completed. I don't need any fancy transition. – Jtvd78 May 16 '17 at 00:37
  • Sorry. I misunderstood your question. I updated my answer, maybe it helps. – Kamil Harasimowicz May 16 '17 at 14:05
0

I've created a nice little sample project you can find here!

There are a number of things that could be going wrong, and since I haven't looked over your project personally it's likely I organized things very differently in my sample, but hopefully you will understand it. I think the big thing is that I added a constraint in storyboard to the navigationController's container to the bottom of the root viewController. I don't adjust the height of this container at all when I animate.

mmd1080
  • 1,812
  • 2
  • 17
  • 29
  • I agree this would work, but then each view that I try to put into the UINavigationController would have to implement the code to determine if it should become smaller. It really just seems like the default transition for the UINavigationController sets the height of the new pushed controller to its future animated height, instead of constraining the pushed view to the bottom of the navigation controller's view. Is this something I could modify with UIViewControllerAnimatedTransitioning? – Jtvd78 May 17 '17 at 22:34
  • I just want to clarify that I am not moving from one storyboard to another. I am simply pushing a view to the UINavigationController. I know my storyboard is not usual, as I came from developing on Android, but I don't think this problem pertains to a storyboard itself, but simply the default behavior of a UINavigationController. I've updated the main post with a non-exaggerated version of the animation. Thank you for your help so far – Jtvd78 May 17 '17 at 22:43
  • Hmm That second example does help a bit now that I notice your root controller is a pageController. Let me see if I can whip up a quick example, I'll check back later – mmd1080 May 17 '17 at 23:23
  • @Jtvd78 check out the new sample and explanation- let me know if you have questions! – mmd1080 May 18 '17 at 00:20
  • @Jtvd78 Let me know if you've had a chance to check this out, would like to know what you think – mmd1080 May 20 '17 at 15:09
  • 1
    Hey, my apologies for not getting back, but I really appreciate the sample code you've given. I see how you've set it up, and it definitely makes sense. I am actually taking a trip out of the country so I haven't had time to look at the response until now. It looks good to me, and I'll definitely let you know when I begin working an a solution inspired by your samples. Thanks again – Jtvd78 May 21 '17 at 00:15
  • I have implemented your solution, but I still have a problem. If, for example in your sample project, you added a label at the bottom of either viewController1 in the storyboard, the mini player will be on top of the label. In my app, this often cuts off the last item list in the embedded tableview. If you add a label like this to your example, you can see what the new problem is. https://imgur.com/a/A7xsd – Jtvd78 May 30 '17 at 14:54
  • Yes, that is to be expected. All you need to do is animate the bottom constraint on your tableView (or any other element at the bottom) at the same time as the miniplayer. In the case of a tableView, this should be invisible to the user but will prevent any scrolling issues. – mmd1080 May 30 '17 at 17:28
  • I was able to solve my final problem by subclassing UINavigationController and overriding pushViewController. In pushViewController, I wrapped the incoming controller in a 'ContainerController', which has a single UIVIew which I then resize. Each ViewController now does not need to implement anything related to the mini-player – Jtvd78 Jun 01 '17 at 16:04