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

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

相关推荐
Tockm2 小时前
mac 下安装Rust Toolchain(Nightly)
开发语言·macos·rust
学而知不足~3 小时前
MAC程序签名遇到的问题
macos
Kila_17 小时前
【iOS(swift)笔记-11】App版本升级时本地数据库sqlite更新逻辑
数据库·ios·swift
佛曰_不可说1 天前
xcode手动安装iOS Simulator Runtime
ios·cocoa·xcode
GameTomato1 天前
【IOS】【OC】【应用内打印功能的实现】如何在APP内实现打印功能,连接本地打印机,把想要打印的界面打印成图片
macos·ios·objective-c·xcode·游戏开发·cocos2d
咔咔库奇1 天前
对接 uniapp 通过中间层(JSBridge)集成零信任 原生androiid和ios SDK
ios·uni-app·cocoa
Digitally2 天前
如何轻松地将文件从 iPhone 传输到 PC
ios·iphone
handsome09162 天前
构建版本没mac上传APP方法
macos