前言:
本文將會(huì)創(chuàng)建以下幾個(gè)主類:
DWContainerViewController
:這包含了左視圖,中視圖和右視圖控制器的視圖旁涤,并處理動(dòng)畫和滑動(dòng)等操作蝇刀。
DWCenterViewController
:中央面板搓萧。
DWSidePanelViewController
:用于左側(cè)和右側(cè)面板毛甲。
創(chuàng)建storyboard,如圖:
并且創(chuàng)建DWCenterViewController
哈扮、DWStarCell
纬纪、DWSidePanelViewController
,關(guān)聯(lián)上圖中的storyboard
DWCenterViewController
為滑出式導(dǎo)航的類滑肉,代碼:
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) {
//左邊點(diǎn)擊事件
}
}
DWStarCell
代碼:
class DWStarCell: UITableViewCell {
@IBOutlet weak var animalImageView: UIView!
@IBOutlet weak var imageNameLabel: UILabel!
@IBOutlet weak var imageCreatorLabel: UILabel!
}
創(chuàng)建DWStar.swift
模型包各,并且初始化cell顯示的數(shù)據(jù),代碼如下:
//結(jié)構(gòu)體
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: "周潤(rùn)發(fā)", 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"))
]
}
創(chuàng)建DWCenterViewControllerDelegate
赦邻,并且創(chuàng)建協(xié)議方法:
//創(chuàng)建協(xié)議 optional:類似oc的可選
@objc
protocol DWCenterViewControllerDelegate {
@objc optional func toggleLeftPanel() //切換左邊的容器
@objc optional func collapseSidePanels() //折疊側(cè)邊的容器
}
在DWCenterViewController.swift
的actorsTapped
點(diǎn)擊方法調(diào)用協(xié)議方法toggleLeftPanel
髓棋,如下:
@IBAction func actorsTapped(_ sender: Any) {
//左邊點(diǎn)擊事件
delegate?.toggleLeftPanel?()
}
創(chuàng)建DWSidePanelViewControllerDelegate.swift
,并創(chuàng)一個(gè)協(xié)議
protocol DWSidePanelViewControllerDelegate {
func didSelectAnimal(_ animal: DWStar) //選擇的動(dòng)物
}
在DWCenterViewController.swift
實(shí)現(xiàn)DWSidePanelViewControllerDelegate
的協(xié)議方法:
// MARK: - DWCenterViewController delegate
//在該類實(shí)現(xiàn)delegate的方法
extension DWCenterViewController: DWSidePanelViewControllerDelegate {
func didSelectAnimal(_ animal: DWStar) { //實(shí)現(xiàn)協(xié)議方法
imageView.image = animal.image
titleLabel.text = animal.title
creatorLabel.text = animal.creator
delegate?.collapseSidePanels?() //折疊側(cè)容器
}
}
創(chuàng)建DWContainerViewController.swift
惶洲,并定義一些屬性:
//枚舉 滑動(dòng)狀態(tài)
enum SlideOutState {
case bothCollapsed //側(cè)容器折疊
case leftPanelExpanded //左容器展開
case rightPanelExpanded //右容器展開
}
//定義屬性
var centerNavigationController: UINavigationController!
var centerViewController: DWCenterViewController!
//當(dāng)前狀態(tài)
var currentState: SlideOutState = .bothCollapsed {
didSet { //在屬性值改變后觸發(fā)didSet
let shoulShowShadow = currentState != .bothCollapsed
}
}
var leftViewController: DWSidePanelViewController?
var centerPanelExpandedOffset: CGFloat = 60 //該值是中央視圖控制器在屏幕外動(dòng)畫顯示后左側(cè)可見的寬度(以點(diǎn)為單位)
擴(kuò)展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包裝在導(dǎo)航控制器中
centerNavigationController = UINavigationController(rootViewController: centerViewController)
//加入centerViewcontroller的視圖
view.addSubview(centerNavigationController.view)
//加入centerViewcontroller的視圖控制器
addChildViewController(centerNavigationController)
centerNavigationController.didMove(toParentViewController: self)
實(shí)現(xiàn)協(xié)議方法(添加左側(cè)容器一起動(dòng)畫的發(fā)生代碼):
extension DWContainerViewController: DWCenterViewControllerDelegate {
}
在協(xié)議方法中,添加
func toggleLeftPanel() {
//如果當(dāng)前狀態(tài):左邊為展開
let notAlreadyExpanded = (currentState != .leftPanelExpanded)
if notAlreadyExpanded {
addLeftPanelViewController() //添加左邊容器
}
//左邊容器展開的動(dòng)畫
animateLeftPanel(shouldExpand: notAlreadyExpanded)
}
//折疊側(cè)邊容器
func collapseSidePanels() {
switch currentState {
case .leftPanelExpanded:
toggleLeftPanel()
default:
break
}
}
//左邊的VC
func addLeftPanelViewController() {//guard語句判斷其后的表達(dá)式布爾值為false時(shí)膳犹,才會(huì)執(zhí)行之后代碼塊里的代碼恬吕,如果為true,則跳過整個(gè)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
})
}
}
//檢查是否被告知展開或折疊側(cè)面板须床。如果它應(yīng)該展開铐料,那么它將設(shè)置當(dāng)前狀態(tài)以指示左側(cè)面板展開,然后為中央面板設(shè)置動(dòng)畫豺旬,以便打開钠惩。否則,它將關(guān)閉中央面板族阅,然后移除其視圖篓跛,并設(shè)置當(dāng)前狀態(tài)以指示其關(guān)閉。
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
}
}
}
添加手勢(shì)坦刀,更改DWCenterViewController
的導(dǎo)航欄x坐標(biāo)
// 手勢(shì)
// 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:方法設(shè)置手勢(shì)的偏移量
}
case .ended: //根據(jù)不同的方向移動(dòng)左或右
if let _ = leftViewController,
let rview = recognize.view {
let hasMovedGreaterThanHalfway = rview.center.x > view.bounds.size.width
animateLeftPanel(shouldExpand: hasMovedGreaterThanHalfway)
}
default:
break
}
}
}
代碼傳送門
注意:
1愧沟、自己添加tableView,需要手動(dòng)添加dataSource 和 delegate
2蔬咬、調(diào)節(jié)tableView的row height