6
struct ContentView: View {
    @State var hideNavigationBar: Bool = false
    
    var body: some View {
        NavigationView {
            ScrollView {
                VStack {
                    Rectangle().fill(Color.red).frame(height: 50)
                        .onTapGesture(count: 1, perform: {
                            withAnimation {
                                self.hideNavigationBar.toggle()
                            }
                        })
                    VStack {
                        ForEach(1..<50) { index in
                            HStack {
                                Text("Sample Text")
                                Spacer()
                            }
                        }
                    }
                }
            }
            .navigationBarTitle("Browse")
            .navigationBarHidden(hideNavigationBar)
        }
    }
}

When you tap the red rectangle it snaps the navigation bar away. I thought withAnimation{} would fix this, but it doesn't. In UIKit you would do something like this navigationController?.setNavigationBarHidden(true, animated: true).

Tested in xCode 12 beta 6 and xCode 11.7

Mark
  • 16,906
  • 20
  • 84
  • 117
  • I'm still learning animation in SwiftUI... at this current moment in time I understand that we must animate the parent view. Have you tried applying an `.animate()` view modifier to the `ScrollView`? (I'd suggest placing it beneath `.navigationBarHidden(hideNavigationBar)` – andrewbuilder Sep 04 '20 at 13:24

3 Answers3

8

You could try using

.navigationBarHidden(hideNavigationBar).animation(.linear(duration: 0.5)) instead of .navigationBarHidden(hideNavigationBar)

and also move self.hideNavigationBar.toggle() out of the animation block. That is not required if you use the above approach for hiding of navigation bar with animation.

Anjali Aggarwal
  • 611
  • 4
  • 8
  • Hey Andrew did you try your code in xCode because I changed the line into the animation like you said, but it still shows the snappy behavior when pressing the red rectangle. – Mark Sep 04 '20 at 14:18
  • I did try the code and it was working fine for me. What version of Xcode are you using. I checked with Xcode 12 – Anjali Aggarwal Sep 04 '20 at 15:33
  • Somehow I needed to reboot. I will mark it as an answer. – Mark Sep 04 '20 at 16:15
  • 3
    Is still really working? I tried and it does only animate the `toolbar` items but not the navigation bar itself. – Nicolas Jun 22 '21 at 17:35
  • It does work! Important to understand: the `animation` modifier must "hit" somehow the `NavigationView` or it's top level container (here the `ScrollView`), otherwise the change of `hideNavigationBar` is not animated. I suppose the `navigationBarHidden` modifier talks to the `NavigationView` using the SwiftUI preferences system, therefore any animation modifier applied to the `navigationBarHidden` modifier is somewhat irrelevant. The modifier applied to the `NavigationView`/top level container is important. – pd95 Aug 18 '21 at 12:39
2

I think, the only solution is to use a position function in SwiftUI 2

var body: some View {
    GeometryReader { geometry in
        NavigationView {
            ZStack {
                Color("background")
                    .ignoresSafeArea()
                
                // ContentView
            }
            .navigationBarTitleDisplayMode(.inline)
            .navigationBarItems(leading: logo, trailing: barButtonItems)
            .toolbar {
                ToolbarItem(placement: .principal) {
                    SearchBarButton(placeholder: LocalizedStringKey("home_vc.search_bar.placeholder"))
                        .opacity(isNavigationBarHidden ? 0 : 1)
                        .animation(.easeInOut(duration: data.duration))
                }
                
            }
        }
        .frame(height: geometry.size.height + (isNavigationBarHidden ? 70 : 0))
         // This is the key ⬇
        .position(x: geometry.size.width/2, y: geometry.size.height/2 - (isNavigationBarHidden ? 35 : 0))
        .animation(.easeInOut(duration: 0.38))
        .onTapGesture {
            isNavigationBarHidden.toggle()
        }
    }
}

enter image description here

Den
  • 3,179
  • 29
  • 26
0

I'm still learning animation in SwiftUI but at this stage, I understand that you must animate the parent view.

So your code would become...

struct ContentView: View {

    @State var hideNavigationBar: Bool = false
    
    var body: some View {
        NavigationView {
            ScrollView {
                VStack {
                    Rectangle().fill(Color.red).frame(height: 50)
                        .onTapGesture(count: 1) {
                            self.hideNavigationBar.toggle()
                        }
                    VStack {
                        ForEach(1..<50) { index in
                            HStack {
                                Text("Sample Text")
                                Spacer()
                            }
                        }
                    }
                }
            }
            .navigationBarTitle("Browse")
            .navigationBarHidden(hideNavigationBar)
            .animation(.spring()) // for example
        }
    }
}

Note that the last argument in any function call can be placed into a single closure.

So...

                    .onTapGesture(count: 1, perform: {
                        self.hideNavigationBar.toggle()
                    })

can become...

                    .onTapGesture(count: 1) {
                        self.hideNavigationBar.toggle()
                    }

Simpler syntax in my humble opinion.

andrewbuilder
  • 3,629
  • 2
  • 24
  • 46
  • Hey Andrew did you try your code in xCode because I copied and paste it, however the snappy behavior still exists. – Mark Sep 04 '20 at 14:17
  • @Mark now that you mention it... I've not actually been able to animate the navigation bar yet. This is a common complaint. I'll do some more research and update my answer - it remains a problem in my iOS 14 targets... One thing I've done successfully and can suggest is, while it is not a solution, to animate elements around the navigation bar. This takes the user's focus away form the nav bar. – andrewbuilder Sep 04 '20 at 14:23
  • Thanks for your input. I submitted it to Apple Feedback you never know. – Mark Sep 04 '20 at 14:31