iOS QQ抽屉式导航的实现

QQ个人中心的侧滑功能(通常称为"抽屉式导航")可以通过以下几种方式在iOS中实现:

主要实现方案

  1. 使用第三方库

最快速的方式是使用成熟的第三方库:

  • SWRevealViewController:最流行的侧滑菜单库
  • MMDrawerController:另一个功能丰富的抽屉式导航库
  • KYDrawerController:轻量级的实现

示例代码(SWRevealViewController):

swift 复制代码
// 初始化
let revealViewController = SWRevealViewController(rearViewController: MenuViewController(), frontViewController: MainViewController())
 
// 设置为主窗口根视图 
window?.rootViewController = revealViewController
 
// 添加手势 
revealViewController?.tapGestureRecognizer()
revealViewController?.panGestureRecognizer()
  1. 自定义实现

如果需要完全自定义,可以自己实现:

swift 复制代码
class DrawerController: UIViewController {
    private var mainViewController: UIViewController!
    private var menuViewController: UIViewController!
    private var isMenuOpen = false 
    private let menuWidth: CGFloat = 300
    
    init(mainViewController: UIViewController, menuViewController: UIViewController) {
        self.mainViewController = mainViewController 
        self.menuViewController = menuViewController
        super.init(nibName: nil, bundle: nil)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        addChild(mainViewController)
        view.addSubview(mainViewController.view)
        mainViewController.didMove(toParent: self)
        
        addChild(menuViewController)
        view.insertSubview(menuViewController.view, at: 0)
        menuViewController.didMove(toParent: self)
        
        menuViewController.view.frame = CGRect(x: -menuWidth, y: 0, width: menuWidth, height: view.bounds.height)
        
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
        view.addGestureRecognizer(panGesture)
    }
    
    @objc private func handlePanGesture(_ recognizer: UIPanGestureRecognizer) {
        let translation = recognizer.translation(in: view)
        
        switch recognizer.state {
        case .changed:
            let newX = mainViewController.view.frame.origin.x + translation.x 
            if newX >= 0 && newX <= menuWidth {
                mainViewController.view.frame.origin.x = newX 
                menuViewController.view.frame.origin.x = newX - menuWidth 
            }
            recognizer.setTranslation(.zero, in: view)
            
        case .ended:
            let velocity = recognizer.velocity(in: view)
            if velocity.x > 0 {
                openMenu()
            } else {
                closeMenu()
            }
            
        default: break
        }
    }
    
    func openMenu() {
        UIView.animate(withDuration: 0.3) {
            self.mainViewController.view.frame.origin.x = self.menuWidth
            self.menuViewController.view.frame.origin.x = 0
        }
        isMenuOpen = true 
    }
    
    func closeMenu() {
        UIView.animate(withDuration: 0.3) {
            self.mainViewController.view.frame.origin.x = 0
            self.menuViewController.view.frame.origin.x = -self.menuWidth 
        }
        isMenuOpen = false
    }
}

实现要点

  1. 视图层级:主视图在上,菜单视图在下
  2. 手势处理:需要处理滑动手势(UIPanGestureRecognizer)
  3. 动画效果:使用UIView.animate实现平滑过渡
  4. 边缘手势:可以添加UIScreenEdgePanGestureRecognizer实现从边缘滑出的效果
  5. 遮罩效果:QQ通常会在主视图上添加半透明黑色遮罩

高级优化

  1. 性能优化:使用UIViewPropertyAnimator实现更流畅的交互
  2. 阴影效果:为主视图添加阴影增加层次感
  3. 状态保存:记住菜单的打开/关闭状态
  4. 交互阻断:菜单打开时阻断主视图的交互

SwiftUI实现

如果你使用SwiftUI,可以这样实现:

swift 复制代码
struct ContentView: View {
    @State private var offset: CGFloat = 0 
    @State private var lastOffset: CGFloat = 0
    @GestureState private var dragOffset: CGFloat = 0
    
    var body: some View {
        let drag = DragGesture()
            .updating($dragOffset) { value, state, _ in
                state = value.translation.width
            }
            .onEnded { value in 
                withAnimation {
                    if value.translation.width > 100 {
                        offset = 300
                    } else if value.translation.width < -100 {
                        offset = 0 
                    } else {
                        offset = lastOffset 
                    }
                }
                lastOffset = offset 
            }
        
        return ZStack(alignment: .leading) {
            MenuView()
                .frame(width: 300)
            
            MainView()
                .offset(x: max(0, offset + dragOffset))
                .gesture(drag)
        }
    }
}

选择哪种实现方式取决于你的项目需求和技术栈。第三方库可以快速实现,自定义实现则更加灵活可控。

相关推荐
YungFan10 小时前
iOS26适配指南之UINavigationController
ios·swift
木叶丸12 小时前
Flutter 生命周期完全指南
android·flutter·ios
一只帆記13 小时前
Mac中Minicom串口调试基础使用
macos
摆烂工程师14 小时前
(5千字总结)国内如何安装和使用 Claude Code 的保姆级教程 - 支持Mac和Windows用户
windows·macos·claude
Keya15 小时前
解决 pod install 警告信息显示不完全的方法
ios·xcode·cocoapods
随笔记17 小时前
uniapp开发的小程序输入框在ios自动填充密码,如何欺骗苹果手机不让自动填充
前端·ios·app
蓝纹绿茶18 小时前
【Mac】实现Docker下载安装【正在逐步完善】
macos·docker·容器
unicrom_深圳市由你创科技19 小时前
Unity开发如何解决iOS闪退问题
unity·ios·蓝桥杯
TE-茶叶蛋19 小时前
iOS打包流程
ios