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)
        }
    }
}

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

相关推荐
2501_915921432 小时前
抓包鹰 Traceeagle 解除证书绑定,SSL Pinning 解除
网络·网络协议·网络安全·ios·adb·https·ssl
做萤石二次开发的哈哈8 小时前
开发者如何调用 ERTC iOS API 完成 SDK 初始化配置?
macos·objective-c·cocoa
AUV110711 小时前
Mac 录屏自动缩放怎么设置才不乱跳:自动片段、固定区域与鼠标跟随调试
macos·计算机外设
人月神话Lee11 小时前
我做了个不要账号、不要定位权限的旅行 App,聊聊那些「不做」的决定
ios·ai编程·产品
程序员良辰12 小时前
【Mac快速切换JDK】Mac 使用 Homebrew + jenv 快速切换 JDK 版本
java·开发语言·macos
2501_916007471 天前
Bundle ID 注册与管理 App ID 创建指南 命名规则 权限开关与删除注意事项
android·ios·小程序·https·uni-app·iphone·webview
coding4u1 天前
Mac 外接显示器完全指南:从插上线到调好每个参数
macos·计算机外设·文件管理·显示器·策略模式·quick look扩展
zhyf1191 天前
(Mac)Homebrew部署Obsidian+Claudian+Excalidraw
macos
chuan.bai1 天前
Java RAG 实战(第 3 篇):从交互式聊天到多轮上下文
java·人工智能·macos·ai