Swift封裝-滑出式導航欄

效果圖.gif

前言:
本文將會創建以下幾個主類:

DWContainerViewController:這包含了左視圖,中視圖和右視圖控制器的視圖,并處理動畫和滑動等操作。
DWCenterViewController:中央面板。
DWSidePanelViewController:用于左側和右側面板。

創建storyboard,如圖:


image.png

并且創建DWCenterViewControllerDWStarCellDWSidePanelViewController,關聯上圖中的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.swiftactorsTapped點擊方法調用協議方法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
    }
}

//左邊的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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容