Reputation: 2848
I am trying to build a navigation drawer with slide animation for left menu content and opacity animation for the background of the menu.
The code below works fine for me except for the animation. I am not sure where exactly the animation went wrong and it's not working.
Here is my code.
struct LeftNavigationView:View {
@EnvironmentObject var viewModel:ViewModel
var body: some View {
ZStack {
Color.black.opacity(0.8)
.ignoresSafeArea()
.transition(.opacity)
.animation(.default)
VStack {
Button(action: {
self.viewModel.isLeftMenuVisible.toggle()
}, label: {
Text("Close Me")
})
}
.frame(maxWidth:.infinity, maxHeight: .infinity)
.background(Color.white)
.cornerRadius(10)
.padding(.trailing)
.padding(.trailing)
.padding(.trailing)
.padding(.trailing)
.transition(
.asymmetric(
insertion: .move(edge: .leading),
removal: .move(edge: .leading)
)
)
.animation(.default)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.animation(.default)
}
}
class ViewModel: ObservableObject {
@Published var isLeftMenuVisible:Bool = false
}
struct ContentView: View {
@StateObject var viewModel:ViewModel = ViewModel()
var body: some View {
ZStack {
NavigationView {
VStack(alignment:.leading) {
Button(action: {
self.viewModel.isLeftMenuVisible.toggle()
}, label: {
Text("Button")
})
}.padding(.horizontal)
.navigationTitle("ContentView")
}
if self.viewModel.isLeftMenuVisible {
LeftNavigationView()
}
}.environmentObject(self.viewModel)
}
}
Any help will be appreciated.
Upvotes: 2
Views: 2751
Reputation: 258057
You almost there. It is better to keep control over appearance/disappearance inside menu view. Find below fixed parts, places are highlighted with comments in code.
Tested with Xcode 12.5 / iOS 14.5
Note: demo prepared with turned on "Simulator > Debug > Slow Animations" for better visibility
struct LeftNavigationView:View {
@EnvironmentObject var viewModel:ViewModel
var body: some View {
ZStack {
if self.viewModel.isLeftMenuVisible { // << here !!
Color.black.opacity(0.8)
.ignoresSafeArea()
.transition(.opacity)
VStack {
Button(action: {
self.viewModel.isLeftMenuVisible.toggle()
}, label: {
Text("Close Me")
})
}
.frame(maxWidth:.infinity, maxHeight: .infinity)
.background(Color.white)
.cornerRadius(10)
.padding(.trailing)
.padding(.trailing)
.padding(.trailing)
.padding(.trailing)
.transition(
.asymmetric(
insertion: .move(edge: .leading),
removal: .move(edge: .leading)
)
).zIndex(1) // << force keep at top where removed!!
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.animation(.default, value: self.viewModel.isLeftMenuVisible) // << here !!
}
}
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
ZStack {
NavigationView {
VStack(alignment:.leading) {
Button(action: {
self.viewModel.isLeftMenuVisible.toggle()
}, label: {
Text("Button")
})
}.padding(.horizontal)
.navigationTitle("ContentView")
}
// included here, everything else is managed inside (!) view
LeftNavigationView()
}.environmentObject(self.viewModel)
}
}
Upvotes: 4
Reputation: 540
You should use this library KYDrawerController
Declare ContentView and MenuView in SceneDelegate:
import KYDrawerController
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
let drawerController = KYDrawerController(drawerDirection: .left, drawerWidth: 300)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
drawerController.mainViewController = UIHostingController(rootView: ContentView())
drawerController.drawerViewController = UIHostingController(rootView: LeftNavigationView()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
let vc = drawerController
vc.view.frame = window.bounds
window.rootViewController = vc
self.window = window
(UIApplication.shared.delegate as? AppDelegate)?.self.window = window
window.makeKeyAndVisible()
}
//...
}
//...
}
Upvotes: 1