QQ个人中心的侧滑功能(通常称为"抽屉式导航")可以通过以下几种方式在iOS中实现:
主要实现方案
- 使用第三方库
最快速的方式是使用成熟的第三方库:
- SWRevealViewController:最流行的侧滑菜单库
- MMDrawerController:另一个功能丰富的抽屉式导航库
- KYDrawerController:轻量级的实现
示例代码(SWRevealViewController):
swift
// 初始化
let revealViewController = SWRevealViewController(rearViewController: MenuViewController(), frontViewController: MainViewController())
// 设置为主窗口根视图
window?.rootViewController = revealViewController
// 添加手势
revealViewController?.tapGestureRecognizer()
revealViewController?.panGestureRecognizer()
- 自定义实现
如果需要完全自定义,可以自己实现:
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
}
}
实现要点
- 视图层级:主视图在上,菜单视图在下
- 手势处理:需要处理滑动手势(UIPanGestureRecognizer)
- 动画效果:使用UIView.animate实现平滑过渡
- 边缘手势:可以添加UIScreenEdgePanGestureRecognizer实现从边缘滑出的效果
- 遮罩效果:QQ通常会在主视图上添加半透明黑色遮罩
高级优化
- 性能优化:使用UIViewPropertyAnimator实现更流畅的交互
- 阴影效果:为主视图添加阴影增加层次感
- 状态保存:记住菜单的打开/关闭状态
- 交互阻断:菜单打开时阻断主视图的交互
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)
}
}
}
选择哪种实现方式取决于你的项目需求和技术栈。第三方库可以快速实现,自定义实现则更加灵活可控。