前言: 本文将会创建以下几个主类:
DWContainerViewController
:这包含了左视图,中视图和右视图控制器的视图,并处理动画和滑动等操作。DWCenterViewController
:中央面板。DWSidePanelViewController
:用于左侧和右侧面板。
创建storyboard,如图:
并且创建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的可选@objcprotocol 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,代码如下:
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)复制代码
实现协议方法(添加左侧容器一起动画的发生代码):
extension DWContainerViewController: DWCenterViewControllerDelegate { }复制代码
在协议方法中,添加
func toggleLeftPanel() { //如果当前状态:左边为展开 let notAlreadyExpanded = (currentState != .leftPanelExpanded) if notAlreadyExpanded { addLeftPanelViewController() //添加左边容器 } //左边容器展开的动画 animateLeftPanel(shouldExpand: notAlreadyExpanded)}//折叠侧边容器func collapseSidePanels() { switch currentState { case .leftPanelExpanded: toggleLeftPanel() default: break }}//左边的VCfunc 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)}//右边的VCfunc 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 recognizerextension 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