已实现Uber式侧边菜单,求实现类Twitter侧边导航抽屉(Side Menu),不依赖第三方库
Hey there! Since you’ve already built an Uber-style overlay navigation drawer, switching to Twitter’s slide-out-main-content version is totally doable without any third-party libraries or CocoaPods. Let’s walk through the exact steps to replicate that Twitter feel.
The core difference between Twitter’s and Uber’s drawer is:
Twitter slides the main view controller off-screen to reveal the menu behind it, while Uber slides the menu over the top of the main view controller.
We’ll use a container view controller to manage both the main and menu views—this keeps your code clean and gives full control over animations.
1. Set Up the Container Controller
First, create a container controller to act as your app’s root. It will hold both the menu and main view controllers.
class DrawerContainerViewController: UIViewController { // Replace these with your actual menu and main view controllers private let menuViewController = YourMenuViewController() private let mainViewController = YourMainViewController() private var isMenuOpen = false override func viewDidLoad() { super.viewDidLoad() setupChildViewControllers() addPanGestureToMainView() } private func setupChildViewControllers() { // Add menu first (it sits behind the main view) addChild(menuViewController) view.addSubview(menuViewController.view) menuViewController.view.frame = view.bounds menuViewController.didMove(toParent: self) // Add main view on top of the menu addChild(mainViewController) view.addSubview(mainViewController.view) mainViewController.view.frame = view.bounds mainViewController.didMove(toParent: self) } private func addPanGestureToMainView() { let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:))) mainViewController.view.addGestureRecognizer(panGesture) } }
2. Handle Pan Gesture & Core Animations
This is where the Twitter-style magic happens. We’ll translate the main view with the user’s pan, and auto-complete the open/close animation when the gesture ends.
@objc private func handlePanGesture(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: view) let progress = translation.x / view.bounds.width let maxSlideDistance = view.bounds.width * 0.7 // Adjust based on your desired menu width switch gesture.state { case .began: mainViewController.view.layer.removeAllAnimations() // Clear any pending animations case .changed: // Move main view with the gesture, cap it at max slide distance let newMainViewX = max(0, min(translation.x, maxSlideDistance)) mainViewController.view.frame.origin.x = newMainViewX // Add subtle menu scaling/opacity for depth (Twitter-like polish) let menuScale = 1 - (progress * 0.1) menuViewController.view.transform = CGAffineTransform(scaleX: menuScale, y: menuScale) menuViewController.view.alpha = 0.8 + (progress * 0.2) case .ended, .cancelled: // Decide to open or close based on a threshold let openThreshold = view.bounds.width * 0.3 if mainViewController.view.frame.origin.x > openThreshold { openMenu() } else { closeMenu() } default: break } } private func openMenu() { let targetX = view.bounds.width * 0.7 UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.5) { self.mainViewController.view.frame.origin.x = targetX self.menuViewController.view.transform = CGAffineTransform(scaleX: 0.9, y: 0.9) self.menuViewController.view.alpha = 1.0 } completion: { _ in self.isMenuOpen = true } } private func closeMenu() { UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.5) { self.mainViewController.view.frame.origin.x = 0 self.menuViewController.view.transform = .identity self.menuViewController.view.alpha = 0.8 } completion: { _ in self.isMenuOpen = false } }
3. Polish for Twitter’s Exact Feel
- Navigation Bar Handling: If your main view has a navigation bar, make sure it moves with the main view. Avoid using a standalone
UINavigationController—instead, embed the navigation bar directly in your main view controller. - Menu Item Actions: Add a method in the container to close the menu when a menu item is tapped. Call this from your menu view controller (you can use a delegate or closure to communicate between them).
- Screen Rotation: Update the frames of both views in
viewWillTransition(to size:with coordinator:)to keep the drawer working correctly on rotation. - Status Bar Adjustments: If needed, override
preferredStatusBarStylein the container to match the menu/open state.
内容的提问来源于stack exchange,提问作者Hasnain Kanji

