效果图.gif
前言: 本文将会创建以下几个主类:
DWContainerViewController
:这包含了左视图,中视图和右视图控制器的视图,并处理动画和滑动等操作。DWCenterViewController
:中央面板。DWSidePanelViewController
:用于左侧和右侧面板。
创建storyboard,如图:
image.png
并且创建DWCenterViewController
、DWStarCell
、DWSidePanelViewController
,关联上图中的storyboard
DWCenterViewController
为滑出式导航的类,代码:
class DWCenterViewController: UIViewController {
var delegate: DWCenterViewControllerDelegate?
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var creatorLabel: UILabel!
@IBAction func actorsTapped(_ sender: Any) {
//左边点击事件
}
}
DWStarCell
代码:
class DWStarCell: UITableViewCell {
@IBOutlet weak var animalImageView: UIView!
@IBOutlet weak var imageNameLabel: UILabel!
@IBOutlet weak var imageCreatorLabel: UILabel!
}
创建DWStar.swift
模型,并且初始化cell显示的数据,代码如下:
//结构体
struct DWStar {
let title: String
let creator: String
let image: UIImage?
//重写init方法
init(title: String, creator: String, image:UIImage?) {
self.title = title
self.creator = creator
self.image = image
}
static func allActors() -> [DWStar] {
return [
DWStar(title: "林志玲", creator: "Dwyane", image: UIImage(named: "ID-100113060")),
DWStar(title: "张歆艺", creator: "Dwyane", image: UIImage(named: "ID-10022760")),
DWStar(title: "李连杰", creator: "Dwyane", image: UIImage(named: "ID-10091065")),
DWStar(title: "周润发", creator: "Dwyane", image: UIImage(named: "ID-10047796")),
DWStar(title: "舒淇", creator: "Dwyane", image: UIImage(named: "ID-10092572")),
DWStar(title: "鹿晗", creator: "Dwyane", image: UIImage(named: "ID-10041194")),
DWStar(title: "黄晓明", creator: "Dwyane", image: UIImage(named: "ID-10017782")),
DWStar(title: "李赛凤", creator: "Dwyane", image: UIImage(named: "ID-10091745")),
DWStar(title: "赵丽颖", creator: "Dwyane Ratcliff", image: UIImage(named: "ID-10056941")),
DWStar(title: "周星驰", creator: "Dwyane", image: UIImage(named: "ID-10019208")),
DWStar(title: "杜海涛", creator: "Dwyane", image: UIImage(named: "ID-10011404"))
]
}
创建DWCenterViewControllerDelegate
,并且创建协议方法:
//创建协议 optional:类似oc的可选
@objc
protocol DWCenterViewControllerDelegate {
@objc optional func toggleLeftPanel() //切换左边的容器
@objc optional func collapseSidePanels() //折叠侧边的容器
}
在DWCenterViewController.swift
的actorsTapped
点击方法调用协议方法toggleLeftPanel
,如下:
@IBAction func actorsTapped(_ sender: Any) {
//左边点击事件
delegate?.toggleLeftPanel?()
}
创建DWSidePanelViewControllerDelegate.swift
,并创一个协议
protocol DWSidePanelViewControllerDelegate {
func didSelectAnimal(_ animal: DWStar) //选择的动物
}
在DWCenterViewController.swift
实现DWSidePanelViewControllerDelegate
的协议方法:
// MARK: - DWCenterViewController delegate
//在该类实现delegate的方法
extension DWCenterViewController: DWSidePanelViewControllerDelegate {
func didSelectAnimal(_ animal: DWStar) { //实现协议方法
imageView.image = animal.image
titleLabel.text = animal.title
creatorLabel.text = animal.creator
delegate?.collapseSidePanels?() //折叠侧容器
}
}
创建DWContainerViewController.swift
,并定义一些属性:
//枚举 滑动状态
enum SlideOutState {
case bothCollapsed //侧容器折叠
case leftPanelExpanded //左容器展开
case rightPanelExpanded //右容器展开
}
//定义属性
var centerNavigationController: UINavigationController!
var centerViewController: DWCenterViewController!
//当前状态
var currentState: SlideOutState = .bothCollapsed {
didSet { //在属性值改变后触发didSet
let shoulShowShadow = currentState != .bothCollapsed
}
}
var leftViewController: DWSidePanelViewController?
var centerPanelExpandedOffset: CGFloat = 60 //该值是中央视图控制器在屏幕外动画显示后左侧可见的宽度(以点为单位)
扩展UIStoryboard,方便取得VC,代码如下:
代码语言:javascript复制private extension UIStoryboard {
static func mainStoryboard() -> UIStoryboard {
return UIStoryboard(name: "Main", bundle: Bundle.main)
}
static func centerViewController() -> DWCenterViewController? {
return mainStoryboard().instantiateViewController(withIdentifier: "DWCenterViewController") as? DWCenterViewController
}
static func leftViewController() -> DWSidePanelViewController? {
return mainStoryboard().instantiateViewController(withIdentifier: "LeftViewController") as? DWSidePanelViewController
}
}
在viewDidLoad
添加如下:
//添加中间控制器并显示
centerViewController = UIStoryboard.centerViewController()
centerViewController.delegate = self
//将centerViewController包装在导航控制器中
centerNavigationController = UINavigationController(rootViewController: centerViewController)
//加入centerViewcontroller的视图
view.addSubview(centerNavigationController.view)
//加入centerViewcontroller的视图控制器
addChildViewController(centerNavigationController)
centerNavigationController.didMove(toParentViewController: self)
实现协议方法(添加左侧容器一起动画的发生代码):
代码语言:javascript复制extension DWContainerViewController: DWCenterViewControllerDelegate {
}
在协议方法中,添加
代码语言:javascript复制func toggleLeftPanel() {
//如果当前状态:左边为展开
let notAlreadyExpanded = (currentState != .leftPanelExpanded)
if notAlreadyExpanded {
addLeftPanelViewController() //添加左边容器
}
//左边容器展开的动画
animateLeftPanel(shouldExpand: notAlreadyExpanded)
}
//折叠侧边容器
func collapseSidePanels() {
switch currentState {
case .leftPanelExpanded:
toggleLeftPanel()
default:
break
}
}
//左边的VC
func addLeftPanelViewController() {//guard语句判断其后的表达式布尔值为false时,才会执行之后代码块里的代码,如果为true,则跳过整个guard语句
guard leftViewController == nil else { return }
if let vc = UIStoryboard.leftViewController() {
vc.animals = DWStar.allActors()
addChildSidePanelController(vc)
leftViewController = vc
}
}
func addChildSidePanelController(_ sidePanelController: DWSidePanelViewController) {
sidePanelController.delegate = centerViewController
view.insertSubview(sidePanelController.view, at: 0)
addChildViewController(sidePanelController)
sidePanelController.didMove(toParentViewController: self)
}
//右边的VC
func addRightPanelViewController() {
}
func animateLeftPanel(shouldExpand: Bool) {
if shouldExpand {
currentState = .leftPanelExpanded
animateCenterPanelXPosition(targetPosition: centerNavigationController.view.frame.width - centerPanelExpandedOffset)
} else {
animateCenterPanelXPosition(targetPosition: 0, completion: { (_) in
self.currentState = .bothCollapsed
self.leftViewController?.view.removeFromSuperview()
self.leftViewController = nil
})
}
}
//检查是否被告知展开或折叠侧面板。如果它应该展开,那么它将设置当前状态以指示左侧面板展开,然后为中央面板设置动画,以便打开。否则,它将关闭中央面板,然后移除其视图,并设置当前状态以指示其关闭。
func animateCenterPanelXPosition(targetPosition: CGFloat, completion: ((Bool) -> Void)? = nil) {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .curveEaseOut, animations: {
self.centerNavigationController.view.frame.origin.x = targetPosition
}, completion: completion)
}
func showShadowForCenterViewController(_ shouldShowShadow: Bool) {
if shouldShowShadow {
centerNavigationController.view.layer.shadowOpacity = 0.8
} else {
centerNavigationController.view.layer.shadowOpacity = 0.0
}
}
}
添加手势,更改DWCenterViewController
的导航栏x坐标
// 手势
// MARK: Gesture recognizer
extension DWContainerViewController: UIGestureRecognizerDelegate {
@objc func handlePanGesture(_ recognize: UIPanGestureRecognizer) {
let gestureIsDraggingFromLeftToRight = (recognize.velocity(in: view).x > 0)
switch recognize.state {
case .began:
if currentState == .bothCollapsed {
if gestureIsDraggingFromLeftToRight {
//左边
addLeftPanelViewController()
} else {
//右边
addRightPanelViewController()
}
showShadowForCenterViewController(true)
}
case .changed:
if let rview = recognize.view {
rview.center.x = rview.center.x recognize.translation(in: view).x
recognize.setTranslation(CGPoint.zero, in: view)
//translationInView:方法获取View的偏移量 setTranslation:方法设置手势的偏移量
}
case .ended: //根据不同的方向移动左或右
if let _ = leftViewController,
let rview = recognize.view {
let hasMovedGreaterThanHalfway = rview.center.x > view.bounds.size.width
animateLeftPanel(shouldExpand: hasMovedGreaterThanHalfway)
}
default:
break
}
}
}
代码传送门 注意:
1、自己添加tableView,需要手动添加dataSource 和 delegate 2、调节tableView的row height