Swift封裝-滑出式導(dǎo)航欄

效果圖.gif

前言:
本文將會(huì)創(chuàng)建以下幾個(gè)主類:

DWContainerViewController:這包含了左視圖,中視圖和右視圖控制器的視圖旁涤,并處理動(dòng)畫和滑動(dòng)等操作蝇刀。
DWCenterViewController:中央面板搓萧。
DWSidePanelViewController:用于左側(cè)和右側(cè)面板毛甲。

創(chuàng)建storyboard,如圖:


image.png

并且創(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.swiftactorsTapped點(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市沐寺,隨后出現(xiàn)的幾起案子林艘,更是在濱河造成了極大的恐慌,老刑警劉巖混坞,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狐援,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡究孕,警方通過查閱死者的電腦和手機(jī)咕村,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚊俺,“玉大人懈涛,你說我怎么就攤上這事∮锯” “怎么了批钠?”我有些...
    開封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)得封。 經(jīng)常有香客問我埋心,道長(zhǎng),這世上最難降的妖魔是什么忙上? 我笑而不...
    開封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任拷呆,我火速辦了婚禮,結(jié)果婚禮上疫粥,老公的妹妹穿的比我還像新娘茬斧。我一直安慰自己,他們只是感情好梗逮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開白布项秉。 她就那樣靜靜地躺著,像睡著了一般慷彤。 火紅的嫁衣襯著肌膚如雪娄蔼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天底哗,我揣著相機(jī)與錄音岁诉,去河邊找鬼。 笑死跋选,一個(gè)胖子當(dāng)著我的面吹牛涕癣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播野建,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼属划,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼恬叹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起同眯,我...
    開封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤绽昼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后须蜗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體硅确,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年明肮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菱农。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柿估,死狀恐怖循未,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情秫舌,我是刑警寧澤的妖,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站足陨,受9級(jí)特大地震影響嫂粟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜墨缘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一星虹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧镊讼,春花似錦宽涌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至嚼松,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锰扶,已是汗流浹背献酗。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坷牛,地道東北人罕偎。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像京闰,于是被迫代替她去往敵國(guó)和親颜及。 傳聞我的和親對(duì)象是個(gè)殘疾皇子甩苛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容