▤ SideMenu
如果您喜欢 SideMenu,请在此页面的右上角给它一个 ★。
发送电子邮件给我。
SideMenu 需要您的帮助!如果您是一位熟练的 iOS 开发者,想要帮助维护此库并回答社区提出的问题,请你好,我是 Jon Kent,我是 iOS 设计师、开发者和移动战略家。我喜欢咖啡,也打鼓。
概述
SideMenu是一个简单而通用的侧边菜单控件,用Swift编写。
- 它可以通过Storyboard实现而不需要任何代码。
- 提供8种标准动画风格供您选择(如果您想变得奇怪,甚至还有视差效果)。
- 高度可定制,无需编写大量的自定义代码。
- 支持通过单个手势在两侧连续滑动切换侧边菜单。
- 全局菜单配置。一次性设置,适用于所有屏幕。
- 菜单可以像其他视图控制器一样展示和消失,因为这个控件使用了自定义转场动画。
- 动画使用您的视图控制器,而不是快照。
- 正确处理屏幕旋转和来电时状态栏高度变化。
查看示例项目以查看其实际效果!
预览示例
滑动退出 | 滑动进入 | 溶解 | 滑动进入 + 退出 |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
要求
- Xcode 11。
- Swift 5。
- iOS 10 或更高版本。
安装
CocoaPods
CocoaPods 是 Cocoa 项目的依赖管理器。您可以使用以下命令进行安装
$ gem install cocoapods
要使用 CocoaPods 将 SideMenu 集成到您的 Xcode 项目中,请在您的 Podfile
中指定它
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!
pod 'SideMenu'
# For Swift 5 use:
# pod 'SideMenu', '~> 6.0'
# For Swift 4.2 (no longer maintained) use:
# pod 'SideMenu', '~> 5.0'
然后,运行以下命令
$ pod install
Carthage
Carthage 是一个去中心化的依赖管理器,它构建您的依赖关系并提供二进制框架。
您可以使用以下命令通过 Homebrew 安装 Carthage
$ brew update
$ brew install carthage
要使用 Carthage 将 SideMenu 集成到您的 Xcode 项目中,请在您的 Cartfile
中指定它
github "jonkykong/SideMenu" "master"
Swift 包管理器
Swift 包管理器 是用于自动分发 Swift 代码的工具,并结合了 swift
编译器。它目前处于早期开发阶段,但 SideMenu 在受支持的平台上支持其使用。
一旦您已经设置了 Swift 包,添加 SideMenu 作为依赖关系就像将其添加到 Package.swift
的 dependencies
值一样简单。
dependencies: [
.package(url: "https://github.com/jonkykong/SideMenu.git", from: "6.0.0")
]
使用方法
无代码 Storyboard 实现
-
为侧边菜单创建一个导航控制器。在身份检查器中将导航控制器的
自定义类
设置为SideMenuNavigationController
。将模块
设置为SideMenu
(如果已经手动将SideMenu添加到项目中,则忽略此步骤)。为导航控制器创建一个根视图控制器(下面显示为UITableViewController)。在该视图控制器中设置您想要的任何触发转换。 -
如果您希望侧边菜单从屏幕左侧弹出,请将
SideMenuNavigationController
的左侧
属性设置为开启;如果希望从右侧弹出,则设置为关闭/默认。 -
将一个UIButton或UIBarButton添加到您希望显示菜单的视图控制器中。设置该按钮的触发转换行为为以模态方式显示步骤1中的导航控制器。
就这样。 注意:您只能通过代码启用手势。
代码实现
首先
import SideMenu
从一个按钮,进行如下操作
// Define the menu
let menu = SideMenuNavigationController(rootViewController: YourViewController)
// SideMenuNavigationController is a subclass of UINavigationController, so do any additional configuration
// of it here like setting its viewControllers. If you're using storyboards, you'll want to do something like:
// let menu = storyboard!.instantiateViewController(withIdentifier: "RightMenu") as! SideMenuNavigationController
present(menu, animated: true, completion: nil)
要程序性关闭菜单,进行如下操作
dismiss(animated: true, completion: nil)
要使用手势,必须使用SideMenuManager
。在您的AppDelegate
中,进行如下操作
// Define the menus
let leftMenuNavigationController = SideMenuNavigationController(rootViewController: YourViewController)
SideMenuManager.default.leftMenuNavigationController = leftMenuNavigationController
let rightMenuNavigationController = SideMenuNavigationController(rootViewController: YourViewController)
SideMenuManager.default.rightMenuNavigationController = rightMenuNavigationController
// Setup gestures: the left and/or right menus must be set up (above) for these to work.
// Note that these continue to work on the Navigation Controller independent of the view controller it displays!
SideMenuManager.default.addPanGestureToPresent(toView: self.navigationController!.navigationBar)
SideMenuManager.default.addScreenEdgePanGesturesToPresent(toView: self.navigationController!.view)
// (Optional) Prevent status bar area from turning black when menu appears:
leftMenuNavigationController.statusBarEndAlpha = 0
// Copy all settings to the other menu
rightMenuNavigationController.settings = leftMenuNavigationController.settings
这样就完成了。
自定义
SideMenuManager
SideMenuManager
支持以下功能
/// The left menu.
open var leftMenuNavigationController: SideMenuNavigationController?
/// The right menu.
public var rightMenuNavigationController: SideMenuNavigationController?
/**
Adds screen edge gestures for both left and right sides to a view to present a menu.
- Parameter toView: The view to add gestures to.
- Returns: The array of screen edge gestures added to `toView`.
*/
@discardableResult public func addScreenEdgePanGesturesToPresent(toView view: UIView) -> [UIScreenEdgePanGestureRecognizer]
/**
Adds screen edge gestures to a view to present a menu.
- Parameter toView: The view to add gestures to.
- Parameter forMenu: The menu (left or right) you want to add a gesture for.
- Returns: The screen edge gestures added to `toView`.
*/
@discardableResult public func addScreenEdgePanGesturesToPresent(toView view: UIView, forMenu side: PresentDirection) -> UIScreenEdgePanGestureRecognizer
/**
Adds a pan edge gesture to a view to present menus.
- Parameter toView: The view to add a pan gesture to.
- Returns: The pan gesture added to `toView`.
*/
@discardableResult public func addPanGestureToPresent(toView view: UIView) -> UIPanGestureRecognizer
SideMenuNavigationController
SideMenuNavigationController
支持以下功能
/// Prevents the same view controller (or a view controller of the same class) from being pushed more than once. Defaults to true.
var allowPushOfSameClassTwice: Bool = true
/// Forces menus to always animate when appearing or disappearing, regardless of a pushed view controller's animation.
var alwaysAnimate: Bool = true
/// The animation options when a menu is displayed. Ignored when displayed with a gesture.
var animationOptions: UIView.AnimationOptions = .curveEaseInOut
/**
The blur effect style of the menu if the menu's root view controller is a UITableViewController or UICollectionViewController.
- Note: If you want cells in a UITableViewController menu to show vibrancy, make them a subclass of UITableViewVibrantCell.
*/
var blurEffectStyle: UIBlurEffect.Style? = nil
/// Duration of the remaining animation when the menu is partially dismissed with gestures. Default is 0.35 seconds.
var completeGestureDuration: Double = 0.35
/// Animation curve of the remaining animation when the menu is partially dismissed with gestures. Default is .easeIn.
var completionCurve: UIView.AnimationCurve = .curveEaseInOut
/// Duration of the animation when the menu is dismissed without gestures. Default is 0.35 seconds.
var dismissDuration: Double = 0.35
/// Automatically dismisses the menu when another view is presented from it.
var dismissOnPresent: Bool = true
/// Automatically dismisses the menu when another view controller is pushed from it.
var dismissOnPush: Bool = true
/// Automatically dismisses the menu when the screen is rotated.
var dismissOnRotation: Bool = true
/// Automatically dismisses the menu when app goes to the background.
var dismissWhenBackgrounded: Bool = true
/// Enable or disable a swipe gesture that dismisses the menu. Will not be triggered when `presentingViewControllerUserInteractionEnabled` is set to true. Default is true.
var enableSwipeToDismissGesture: Bool = true
/// Enable or disable a tap gesture that dismisses the menu. Will not be triggered when `presentingViewControllerUserInteractionEnabled` is set to true. Default is true.
var enableTapToDismissGesture: Bool = true
/// The animation initial spring velocity when a menu is displayed. Ignored when displayed with a gesture.
var initialSpringVelocity: CGFloat = 1
/// Whether the menu appears on the right or left side of the screen. Right is the default. This property cannot be changed after the menu has loaded.
var leftSide: Bool = false
/// Width of the menu when presented on screen, showing the existing view controller in the remaining space. Default is zero.
var menuWidth: CGFloat = 240
/// Duration of the animation when the menu is presented without gestures. Default is 0.35 seconds.
var presentDuration: Double = 0.35
/// Enable or disable interaction with the presenting view controller while the menu is displayed. Enabling may make it difficult to dismiss the menu or cause exceptions if the user tries to present and already presented menu. `presentingViewControllerUseSnapshot` must also set to false. Default is false.
var presentingViewControllerUserInteractionEnabled: Bool = false
/// Use a snapshot for the presenting vierw controller while the menu is displayed. Useful when layout changes occur during transitions. Not recommended for apps that support rotation. Default is false.
var presentingViewControllerUseSnapshot: Bool = false
/// The presentation style of the menu.
var presentationStyle: SideMenuPresentStyle = .viewSlideOut
/**
The push style of the menu.
There are six modes in MenuPushStyle:
- defaultBehavior: The view controller is pushed onto the stack.
- popWhenPossible: If a view controller already in the stack is of the same class as the pushed view controller, the stack is instead popped back to the existing view controller. This behavior can help users from getting lost in a deep navigation stack.
- preserve: If a view controller already in the stack is of the same class as the pushed view controller, the existing view controller is pushed to the end of the stack. This behavior is similar to a UITabBarController.
- preserveAndHideBackButton: Same as .preserve and back buttons are automatically hidden.
- replace: Any existing view controllers are released from the stack and replaced with the pushed view controller. Back buttons are automatically hidden. This behavior is ideal if view controllers require a lot of memory or their state doesn't need to be preserved..
- subMenu: Unlike all other behaviors that push using the menu's presentingViewController, this behavior pushes view controllers within the menu. Use this behavior if you want to display a sub menu.
*/
var pushStyle: MenuPushStyle = .default
/// Draws `presentationStyle.backgroundColor` behind the status bar. Default is 0.
var statusBarEndAlpha: CGFloat = 0
/// The animation spring damping when a menu is displayed. Ignored when displayed with a gesture.
var usingSpringWithDamping: CGFloat = 1
/// Indicates if the menu is anywhere in the view hierarchy, even if covered by another view controller.
var isHidden: Bool
侧边菜单显示样式
存在8个预定义的 侧边菜单显示样式
选项
/// Menu slides in over the existing view.
static let menuSlideIn: SideMenuPresentStyle
/// The existing view slides out to reveal the menu underneath.
static let viewSlideOut: SideMenuPresentStyle
/// The existing view slides out while the menu slides in.
static let viewSlideOutMenuIn: SideMenuPresentStyle
/// The menu dissolves in over the existing view.
static let menuDissolveIn: SideMenuPresentStyle
/// The existing view slides out while the menu partially slides in.
static let viewSlideOutMenuPartialIn: SideMenuPresentStyle
/// The existing view slides out while the menu slides out from under it.
static let viewSlideOutMenuOut: SideMenuPresentStyle
/// The existing view slides out while the menu partially slides out from under it.
static let viewSlideOutMenuPartialOut: SideMenuPresentStyle
/// The existing view slides out and shrinks to reveal the menu underneath.
static let viewSlideOutMenuZoom: SideMenuPresentStyle
侧边菜单导航控制器代理
当从视图控制器显示菜单时,要收到通知,请使其遵守 侧边菜单导航控制器代理
协议
extension MyViewController: SideMenuNavigationControllerDelegate {
func sideMenuWillAppear(menu: SideMenuNavigationController, animated: Bool) {
print("SideMenu Appearing! (animated: \(animated))")
}
func sideMenuDidAppear(menu: SideMenuNavigationController, animated: Bool) {
print("SideMenu Appeared! (animated: \(animated))")
}
func sideMenuWillDisappear(menu: SideMenuNavigationController, animated: Bool) {
print("SideMenu Disappearing! (animated: \(animated))")
}
func sideMenuDidDisappear(menu: SideMenuNavigationController, animated: Bool) {
print("SideMenu Disappeared! (animated: \(animated))")
}
}
注意:在 SideMenuNavigationController
上设置 sideMenuDelegate
属性是可选的。如果您的视图控制器遵守协议,则将自动调用方法。
高级
点击查看详细信息
多个侧边菜单管理器
为了简化,SideMenuManager.default
作为主实例存在,因为大多数项目只会在所有屏幕中需要一个菜单。如果需要使用手势显示不同的侧边菜单,例如从之前的侧边菜单中展示的模态视图控制器,请按照以下步骤操作
- 声明一个包含您的自定义
SideMenuManager
实例的变量。如果您想在多屏幕上使用菜单,可能想要在应用的代理中定义和配置它。
let customSideMenuManager = SideMenuManager()
- 使用您的自定义实例设置和展示菜单,与使用
SideMenuManager.default
实例相同。 - 如果使用 Storyboards,请将您的
SideMenuNavigationController
实例子类化,并将其sideMenuManager
属性设置为您的自定义实例。这必须在调用viewDidLoad
之前完成
class MySideMenuNavigationController: SideMenuNavigationController {
let customSideMenuManager = SideMenuManager()
override func awakeFromNib() {
super.awakeFromNib()
sideMenuManager = customSideMenuManager
}
}
或者,您可以从将跳转到您的 SideMenuNavigationController 的视图控制器中设置 sideMenuManager
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let sideMenuNavigationController = segue.destination as? SideMenuNavigationController {
sideMenuNavigationController.sideMenuManager = customSideMenuManager
}
}
重要:不支持直接在多个侧边菜单实例之间显示。使用 menuPushStyle = .subMenu
来显示多级菜单。
侧边菜单展示样式
如果您想创建自定义的展示样式,可以创建一个SideMenuPresentationStyle
的子类,并将您的菜单的presentationStyle
设置为此子类
class MyPresentStyle: SideMenuPresentationStyle {
override init() {
super.init()
/// Background color behind the views and status bar color
backgroundColor = .black
/// The starting alpha value of the menu before it appears
menuStartAlpha = 1
/// Whether or not the menu is on top. If false, the presenting view is on top. Shadows are applied to the view on top.
menuOnTop = false
/// The amount the menu is translated along the x-axis. Zero is stationary, negative values are off-screen, positive values are on screen.
menuTranslateFactor = 0
/// The amount the menu is scaled. Less than one shrinks the view, larger than one grows the view.
menuScaleFactor = 1
/// The color of the shadow applied to the top most view.
onTopShadowColor = .black
/// The radius of the shadow applied to the top most view.
onTopShadowRadius = 5
/// The opacity of the shadow applied to the top most view.
onTopShadowOpacity = 0
/// The offset of the shadow applied to the top most view.
onTopShadowOffset = .zero
/// The ending alpha of the presenting view when the menu is fully displayed.
presentingEndAlpha = 1
/// The amount the presenting view is translated along the x-axis. Zero is stationary, negative values are off-screen, positive values are on screen.
presentingTranslateFactor = 0
/// The amount the presenting view is scaled. Less than one shrinks the view, larger than one grows the view.
presentingScaleFactor = 1
/// The strength of the parallax effect on the presenting view once the menu is displayed.
presentingParallaxStrength = .zero
}
/// This method is called just before the presentation transition begins. Use this to setup any animations. The super method does not need to be called.
override func presentationTransitionWillBegin(to presentedViewController: UIViewController, from presentingViewController: UIViewController) {}
/// This method is called during the presentation animation. Use this to animate anything alongside the menu animation. The super method does not need to be called.
override func presentationTransition(to presentedViewController: UIViewController, from presentingViewController: UIViewController) {}
/// This method is called when the presentation transition ends. Use this to finish any animations. The super method does not need to be called.
override func presentationTransitionDidEnd(to presentedViewController: UIViewController, from presentingViewController: UIViewController, _ completed: Bool) {}
/// This method is called just before the dismissal transition begins. Use this to setup any animations. The super method does not need to be called.
override func dismissalTransitionWillBegin(to presentedViewController: UIViewController, from presentingViewController: UIViewController) {}
/// This method is called during the dismissal animation. Use this to animate anything alongside the menu animation. The super method does not need to be called.
override func dismissalTransition(to presentedViewController: UIViewController, from presentingViewController: UIViewController) {}
/// This method is called when the dismissal transition ends. Use this to finish any animations. The super method does not need to be called.
override func dismissalTransitionDidEnd(to presentedViewController: UIViewController, from presentingViewController: UIViewController, _ completed: Bool) {}
}
已知问题
- 问题 #258。使用
presentingViewControllerUseSnapshot
可以有助于保留体验。
感谢
向所有为使此库更好而贡献的人表示特别的感谢。您的支持值得感激!
许可
SideMenu在MIT许可下提供。更多信息请参阅LICENSE文件。