UIKit框架(十三) —— 如何創(chuàng)建自己的側(cè)滑式面板導(dǎo)航(一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2019.04.02 星期二

前言

iOS中有關(guān)視圖控件用戶能看到的都在UIKit框架里面,用戶交互也是通過UIKit進(jìn)行的茧吊。感興趣的參考上面幾篇文章坟岔。
1. UIKit框架(一) —— UIKit動(dòng)力學(xué)和移動(dòng)效果(一)
2. UIKit框架(二) —— UIKit動(dòng)力學(xué)和移動(dòng)效果(二)
3. UIKit框架(三) —— UICollectionViewCell的擴(kuò)張效果的實(shí)現(xiàn)(一)
4. UIKit框架(四) —— UICollectionViewCell的擴(kuò)張效果的實(shí)現(xiàn)(二)
5. UIKit框架(五) —— 自定義控件:可重復(fù)使用的滑塊(一)
6. UIKit框架(六) —— 自定義控件:可重復(fù)使用的滑塊(二)
7. UIKit框架(七) —— 動(dòng)態(tài)尺寸UITableViewCell的實(shí)現(xiàn)(一)
8. UIKit框架(八) —— 動(dòng)態(tài)尺寸UITableViewCell的實(shí)現(xiàn)(二)
9. UIKit框架(九) —— UICollectionView的數(shù)據(jù)異步預(yù)加載(一)
10. UIKit框架(十) —— UICollectionView的數(shù)據(jù)異步預(yù)加載(二)
11. UIKit框架(十一) —— UICollectionView的重用、選擇和重排序(一)
12. UIKit框架(十二) —— UICollectionView的重用、選擇和重排序(二)

開始

首先看下寫作環(huán)境

Swift 4.2, iOS 12, Xcode 10

在本教程中展父,您將構(gòu)建一個(gè)滑出式面板導(dǎo)航,這是使用普通UINavigationControllerUITabBarController進(jìn)行應(yīng)用程序?qū)Ш降某S锰娲椒ā?滑出式導(dǎo)航面板允許用戶將內(nèi)容滑到屏幕或滑出屏幕。

打開已有的項(xiàng)目臣镣,下面的效果就是要達(dá)到的最終效果辅愿。

滑出導(dǎo)航面板設(shè)計(jì)模式允許開發(fā)人員為他們的應(yīng)用添加永久導(dǎo)航,而不會(huì)占用寶貴的屏幕空間忆某,因?yàn)橛脩艨梢噪S時(shí)選擇顯示導(dǎo)航点待,同時(shí)仍然可以看到他們當(dāng)前的上下文。

在本教程中弃舒,您將采用更少的方法癞埠,以便您可以相對(duì)輕松地將滑出式導(dǎo)航面板技術(shù)應(yīng)用于您自己的應(yīng)用程序。

從名為SlideOutNavigation.xcodeprojSlideOutNavigation-Starter文件夾中打開項(xiàng)目聋呢,并查看它是如何組織的苗踪。除了視圖控制器之外,還有一個(gè)名為Assets.xcassets的資產(chǎn)目錄削锰,其中包含您將在應(yīng)用程序中使用的所有可愛的小貓和小狗圖像通铲。

這是這個(gè)應(yīng)用程序的整體結(jié)構(gòu):

  • ContainerViewController是魔術(shù)發(fā)生的地方!這是視圖控制器器贩,可以處理動(dòng)畫和中心視圖控制器與左右面板之間的滑動(dòng)等操作颅夺。它負(fù)責(zé)保存對(duì)所有其他必要視圖控制器的引用。
  • CenterViewController是中心面板視圖控制器蛹稍。
  • SidePanelViewController用作左側(cè)和右側(cè)面板視圖控制器吧黄。

您可以在Main.storyboard中找到中央,左側(cè)和右側(cè)視圖控制器的視圖唆姐。所以拗慨,隨便看看整個(gè)項(xiàng)目。

現(xiàn)在您已經(jīng)熟悉了應(yīng)用程序的結(jié)構(gòu)奉芦,現(xiàn)在是時(shí)候從正方形 - 中心面板開始了赵抢。


Finding Your Center

第一項(xiàng)業(yè)務(wù)是將CenterViewController放在ContainerViewController中作為子視圖控制器。

打開ContainerViewController.swift声功。 找到viewDidLoad()并在其上方添加以下屬性:

var centerNavigationController: UINavigationController!
var centerViewController: CenterViewController!

這兩個(gè)屬性將同時(shí)包含centerViewController及其父導(dǎo)航控制器烦却。

注意:這些是隱式解包的選項(xiàng)(由!表示)减噪。 它們必須是可選的短绸,因?yàn)樗鼈兊闹翟?code>init()完成之前不會(huì)被初始化,但它們可以隱式解包筹裕,因?yàn)槟阒浪鼈儠?huì)在你使用它們時(shí)被初始化醋闭。 如果不是,那么這是一個(gè)程序員錯(cuò)誤朝卒,你想在測(cè)試應(yīng)用程序時(shí)知道它证逻。

在文件的底部,您將看到UIStoryboard的類擴(kuò)展抗斤,其中包含一些靜態(tài)方法囚企,可以更方便地從應(yīng)用程序的故事板中加載特定的視圖控制器丈咐。 您將利用這些方法填充剛剛創(chuàng)建的屬性。

在對(duì)super的調(diào)用下面的viewDidLoad()中添加以下代碼:

// 1
centerViewController = UIStoryboard.centerViewController()
// 2
centerViewController.delegate = self

// 3
centerNavigationController = UINavigationController(rootViewController: centerViewController)
view.addSubview(centerNavigationController.view)
addChild(centerNavigationController)

// 4
centerNavigationController.didMove(toParent: self)

不要擔(dān)心第二行上的編譯器錯(cuò)誤龙宏,你很快就會(huì)解決這個(gè)問題棵逊。

這個(gè)簡(jiǎn)短的方法有一些有趣的東西。這是你正在做的事情:

  • 1) 通過從故事板中拉出它來獲取centerViewController银酗。
  • 2) 將當(dāng)前視圖控制器設(shè)置為中心視圖控制器的委托辆影,以便中心視圖控制器可以通知其容器何時(shí)顯示和隱藏左側(cè)和右側(cè)面板。
  • 3) 創(chuàng)建一個(gè)導(dǎo)航控制器以包含中心視圖控制器黍特,以便您可以將視圖推送到它并在導(dǎo)航欄中顯示條形按鈕項(xiàng)目蛙讥。然后,將導(dǎo)航控制器的視圖添加到ContainerViewController的視圖中灭衷。
  • 4) 使用addChild(_ :)didMove(toParent :)設(shè)置父子關(guān)系次慢。

真棒!現(xiàn)在要處理下錯(cuò)誤翔曲,修改此類以便它實(shí)現(xiàn)CenterViewControllerDelegate迫像。

將以下類擴(kuò)展添加到文件底部的UIStoryboard擴(kuò)展下面的ContainerViewController(這還包括一些您將在稍后填寫的空方法):

// MARK: CenterViewController delegate

extension ContainerViewController: CenterViewControllerDelegate {
  func toggleLeftPanel() {
  }

  func toggleRightPanel() {
  }

  func collapseSidePanels() {
  }
}

實(shí)現(xiàn)這些方法使此類符合CenterViewControllerDelegate

現(xiàn)在是檢查進(jìn)度的好時(shí)機(jī)部默。 構(gòu)建并運(yùn)行應(yīng)用程序侵蒙。 您應(yīng)該看到類似于以下屏幕的內(nèi)容:

是的造虎,頂部的那些按鈕最終將為您帶來小貓和小狗傅蹂。 創(chuàng)建滑動(dòng)導(dǎo)航面板有什么更好的理由? 但為了讓你更可愛算凿,你必須開始滑動(dòng)份蝴。 首先,向左氓轰!


Kittens to the Left of Me…

您已創(chuàng)建了中心面板婚夫,但添加左視圖控制器需要一組不同的步驟。

要展開左側(cè)面板署鸡,用戶將點(diǎn)擊導(dǎo)航欄中的Kitties按鈕案糙。 打開CenterViewController.swift開始實(shí)現(xiàn)它。

為了使本教程專注于重要的內(nèi)容靴庆,IBActionsIBOutlets在故事板中為您預(yù)先連接时捌。 但是,要實(shí)現(xiàn)DIY滑出式導(dǎo)航面板炉抒,您需要了解按鈕的配置方式奢讨。

請(qǐng)注意,已經(jīng)有兩種IBAction方法焰薄,每種方法對(duì)應(yīng)一個(gè)按鈕拿诸。 找到kittiesTapped(_ :)并將以下實(shí)現(xiàn)添加到其中:

delegate?.toggleLeftPanel()

如前所述扒袖,該方法已經(jīng)連接到Kitties按鈕。 僅當(dāng)delegate具有值時(shí)亩码,這使用可選鏈接來調(diào)用toggleLeftPanel()季率。

您可以在底部看到委托協(xié)議的定義。 正如您將看到的描沟,有一些方法叫做toggleLeftPanel()蚀同,toggleRightPanel()collapseSidePanels()。 如果您還記得啊掏,當(dāng)您先設(shè)置中心視圖控制器實(shí)例時(shí)蠢络,將其委托設(shè)置為容器視圖控制器。 是時(shí)候去實(shí)現(xiàn)toggleLeftPanel()了迟蜜。

注意:有關(guān)委托方法及其實(shí)現(xiàn)方式的更多信息刹孔,請(qǐng)參閱Apple’s Developer Documentation

打開ContainerViewController.swift并在ContainerViewController的頂部添加一個(gè)枚舉:

enum SlideOutState {
  case bothCollapsed
  case leftPanelExpanded
  case rightPanelExpanded
}

您將使用它來跟蹤側(cè)面板的當(dāng)前狀態(tài)娜睛,因此您可以判斷兩個(gè)面板是否都不可見髓霞,或者左側(cè)或右側(cè)面板中的一個(gè)是否可見。

接下來畦戒,在現(xiàn)有的centerViewController屬性下添加兩個(gè)屬性:

var currentState: SlideOutState = .bothCollapsed
var leftViewController: SidePanelViewController?

這些將保持側(cè)面板和左側(cè)面板視圖控制器本身的當(dāng)前狀態(tài):

您將currentState初始化為.bothCollapsed - 也就是說方库,當(dāng)應(yīng)用程序首次加載時(shí),兩個(gè)側(cè)面板都不可見障斋。 leftViewController屬性是可選的纵潦,因?yàn)槟鷮⒃诓煌瑫r(shí)間添加和刪除視圖控制器,因此它可能并不總是具有值垃环。

接下來邀层,添加toggleLeftPanel()的實(shí)現(xiàn):

let notAlreadyExpanded = (currentState != .leftPanelExpanded)

if notAlreadyExpanded {
  addLeftPanelViewController()
}

animateLeftPanel(shouldExpand: notAlreadyExpanded)

首先,此方法檢查左側(cè)面板是否已展開遂庄。 如果它尚未顯示寥院,則調(diào)用一個(gè)方法將面板添加到視圖層次結(jié)構(gòu)中。 然后涛目,它調(diào)用另一種方法秸谢,將其動(dòng)畫為open位置。 如果面板已經(jīng)可見霹肝,則它將面板設(shè)置為closed位置的動(dòng)畫估蹄。

接下來,您需要添加代碼以將左側(cè)面板添加到視圖層次結(jié)構(gòu)中阿迈。 在下面添加以下toggleLeftPanel()

func addLeftPanelViewController() {
  guard leftViewController == nil else { return }

  if let vc = UIStoryboard.leftViewController() {
    vc.animals = Animal.allCats()
    addChildSidePanelController(vc)
    leftViewController = vc
  }
}

此代碼首先檢查leftViewController屬性是否為nil元媚。 如果是,則創(chuàng)建一個(gè)新的SidePanelViewController并設(shè)置要顯示的動(dòng)物列表 - 在這種情況下,貓刊棕!

接下來炭晒,在擴(kuò)展的底部添加addChildSidePanelController(_ :)的實(shí)現(xiàn):

func addChildSidePanelController(_ sidePanelController: SidePanelViewController) {
  view.insertSubview(sidePanelController.view, at: 0)

  addChild(sidePanelController)
  sidePanelController.didMove(toParent: self)
}

此方法將子視圖控制器添加到容器視圖控制器。 這與先前添加中心視圖控制器相同甥角。 它只是插入它的視圖 - 在這種情況下网严,它插入z-index 0,這意味著它將在中心視圖控制器下面 - 并將其添加為子視圖控制器嗤无。

ContainerViewController頂部的其他屬性下面添加以下常量:

let centerPanelExpandedOffset: CGFloat = 90

此值是中心視圖控制器在屏幕上設(shè)置動(dòng)畫后可見的寬度(以磅為單位)震束。 90個(gè)點(diǎn)應(yīng)該可以了。

接下來当犯,返回CenterViewControllerDelegate擴(kuò)展垢村,在addLeftPanelViewController()下面添加以下內(nèi)容:

func animateLeftPanel(shouldExpand: Bool) {
  if shouldExpand {
    currentState = .leftPanelExpanded
    animateCenterPanelXPosition(
      targetPosition: centerNavigationController.view.frame.width 
        - centerPanelExpandedOffset)
  } else {
    animateCenterPanelXPosition(targetPosition: 0) { _ in
      self.currentState = .bothCollapsed
      self.leftViewController?.view.removeFromSuperview()
      self.leftViewController = nil
    }
  }
}

此方法首先檢查是否已告知它是否展開或折疊側(cè)面板。 如果它應(yīng)該展開嚎卫,則它設(shè)置當(dāng)前狀態(tài)以指示左面板已展開并調(diào)用方法為中心面板設(shè)置動(dòng)畫以使其打開嘉栓。 否則,它會(huì)動(dòng)畫中心面板關(guān)閉拓诸,移除其視圖并設(shè)置當(dāng)前狀態(tài)以指示它已關(guān)閉侵佃。

現(xiàn)在在addChildSidePanelController(_ :)上面添加這個(gè)方法:

func animateCenterPanelXPosition(
    targetPosition: CGFloat, 
    completion: ((Bool) -> Void)? = nil) {
  UIView.animate(
    withDuration: 0.5,
    delay: 0,
    usingSpringWithDamping: 0.8
    initialSpringVelocity: 0,
    options: .curveEaseInOut,
    animations: {
      self.centerNavigationController.view.frame.origin.x = targetPosition
    }, 
    completion: completion)
}

這是實(shí)際動(dòng)畫發(fā)生的地方。 它使用漂亮的彈簧動(dòng)畫將中心視圖控制器的視圖移動(dòng)到指定位置奠支。 該方法還采用可選的完成閉包馋辈,并將其傳遞給UIView動(dòng)畫。 如果要更改動(dòng)畫的外觀倍谜,可以嘗試調(diào)整持續(xù)時(shí)間和彈簧阻尼參數(shù)迈螟。

構(gòu)建并運(yùn)行應(yīng)用程序。

點(diǎn)擊導(dǎo)航欄中的Kitties枢劝。 中央視圖控制器應(yīng)該滑過 - 嗖井联! - 并顯示下面的Kitties菜單卜壕。 噢您旁,看看他們都很可愛。

但太多的可愛可能是一件危險(xiǎn)的事情轴捎! 再次點(diǎn)擊Kitties按鈕隱藏它們鹤盒!


Me and My Shadow

當(dāng)左側(cè)面板打開時(shí),請(qǐng)注意它是如何正對(duì)著中央視圖控制器的侦副。 如果它們之間有一點(diǎn)間隔侦锯,那就太好了。 添加陰影怎么樣秦驯?

仍然在ContainerViewController.swift中尺碰,將以下方法添加到擴(kuò)展的末尾:

func showShadowForCenterViewController(_ shouldShowShadow: Bool) {
  if shouldShowShadow {
    centerNavigationController.view.layer.shadowOpacity = 0.8
  } else {
    centerNavigationController.view.layer.shadowOpacity = 0.0
  }
}

這會(huì)調(diào)整導(dǎo)航控制器陰影的不透明度,使其可見或隱藏。 每當(dāng)currentState屬性更改時(shí)亲桥,您將實(shí)現(xiàn)didSet觀察器以添加或刪除陰影洛心。

滾動(dòng)到ContainerViewController.swift的頂部并將currentState聲明更改為:

var currentState: SlideOutState = .bothCollapsed {
  didSet {
    let shouldShowShadow = currentState != .bothCollapsed
    showShadowForCenterViewController(shouldShowShadow)
  }
}

只要屬性的值發(fā)生變化,就會(huì)執(zhí)行didSet閉包题篷。 如果任一面板可見词身,則顯示陰影。

再次構(gòu)建并運(yùn)行應(yīng)用程序番枚。 這次當(dāng)你點(diǎn)擊Kitties時(shí)法严,看看甜蜜的新影子! 看起來更好葫笼,對(duì)吧深啤?

接下來,為右側(cè)添加相同的功能路星,這意味著...小狗墓塌!


Puppies to the Right…

要添加右側(cè)面板視圖控制器,只需重復(fù)添加左側(cè)視圖控制器的步驟奥额。

打開ContainerViewController.swift并在leftViewController下面添加以下屬性:

var rightViewController: SidePanelViewController?

接下來苫幢,找到toggleRightPanel()并添加以下實(shí)現(xiàn):

let notAlreadyExpanded = (currentState != .rightPanelExpanded)

if notAlreadyExpanded {
  addRightPanelViewController()
}

animateRightPanel(shouldExpand: notAlreadyExpanded)

接下來,添加以下toggleRightPanel()

func addRightPanelViewController() {
  guard rightViewController == nil else { return }

  if let vc = UIStoryboard.rightViewController() {
    vc.animals = Animal.allDogs()
    addChildSidePanelController(vc)
    rightViewController = vc
  }
}

func animateRightPanel(shouldExpand: Bool) {
  if shouldExpand {
    currentState = .rightPanelExpanded
    animateCenterPanelXPosition(
      targetPosition: -centerNavigationController.view.frame.width 
        + centerPanelExpandedOffset)
  } else {
    animateCenterPanelXPosition(targetPosition: 0) { _ in
      self.currentState = .bothCollapsed
      self.rightViewController?.view.removeFromSuperview()
      self.rightViewController = nil
    }
  }
}

此代碼幾乎與左側(cè)面板的代碼完全相同垫挨,當(dāng)然韩肝,除了方法和屬性名稱以及方向的差異之外。 如果您對(duì)此有任何疑問九榔,可以隨時(shí)查看上一節(jié)中的說明哀峻。

與以前一樣,IBActionsIBOutlets已經(jīng)在故事板中為您連接哲泊。 與Kitties類似剩蟀,Puppies連接到名為puppiesTapped(_ :)IBAction方法。 此按鈕控制中央面板的滑動(dòng)以顯示右側(cè)面板切威。

切換到CenterViewController.swift并將以下行添加到puppiesTapped(_ :)

delegate?.toggleRightPanel()

同樣育特,這與kittiesTapped(_ :)相同,只是它切換右側(cè)面板而不是左側(cè)面板先朦。

是時(shí)候看一些小狗了缰冤!

再次構(gòu)建并運(yùn)行應(yīng)用程序以確保一切正常。 點(diǎn)擊小狗喳魏。 您的屏幕應(yīng)如下所示:

看起來不錯(cuò)吧棉浸? 但請(qǐng)記住,你不想讓自己暴露在幼犬的可愛中太長(zhǎng)時(shí)間刺彩,所以再次點(diǎn)擊該按鈕以隱藏它們迷郑。

您現(xiàn)在可以查看小貓和小狗枝恋,但是能夠查看每個(gè)小貓的大圖非常棒,不是嗎嗡害?


Pick an Animal, Any Animal!

小貓和小狗列在左側(cè)和右側(cè)小組中鼓择。 這些都是SidePanelViewController的實(shí)例,它只包含一個(gè)table view就漾。

打開SidePanelViewController.swift以查看SidePanelViewControllerDelegate協(xié)議呐能。 無論何時(shí)點(diǎn)擊動(dòng)物,都可以通過此方法通知側(cè)面板的代表抑堡。 是時(shí)候用了摆出!

SidePanelViewController.swift中,在類頂部添加以下屬性首妖,在表視圖IBOutlet下面:

var delegate: SidePanelViewControllerDelegate?

然后偎漫,在UITableViewDelegate擴(kuò)展中填寫tableView(_:didSelectRowAt :)的實(shí)現(xiàn):

let animal = animals[indexPath.row]
delegate?.didSelectAnimal(animal)

如果有一個(gè)委托集,這將告訴用戶選擇了一個(gè)動(dòng)物有缆。 目前象踊,沒有代理! 將CenterViewController作為側(cè)面板的委托是有意義的棚壁,因?yàn)樗梢燥@示所選的動(dòng)物照片和標(biāo)題杯矩。

打開CenterViewController.swift以實(shí)現(xiàn)委托協(xié)議。 在現(xiàn)有類定義下添加以下擴(kuò)展:

extension CenterViewController: SidePanelViewControllerDelegate {
  func didSelectAnimal(_ animal: Animal) {
    imageView.image = animal.image
    titleLabel.text = animal.title
    creatorLabel.text = animal.creator
    delegate?.collapseSidePanels()
  }
}

此方法只使用動(dòng)物的圖像袖外,標(biāo)題和創(chuàng)建者填充中心視圖控制器中的圖像視圖和標(biāo)簽史隆。 然后,如果中心視圖控制器具有自己的委托曼验,則告訴它折疊側(cè)面板泌射,以便您可以專注于所選項(xiàng)目。

但是鬓照,collapseSidePanels()不會(huì)做任何事情熔酷! 打開,ContainerViewController.swift并將以下實(shí)現(xiàn)添加到collapseSidePanels()

switch currentState {
case .rightPanelExpanded:
  toggleRightPanel()
case .leftPanelExpanded:
  toggleLeftPanel()
case .bothCollapsed:
  break
}

此方法中的switch語句只檢查側(cè)面板的當(dāng)前狀態(tài)豺裆,并折疊打開的任何一個(gè)拒秘。

現(xiàn)在,找到addChildSidePanelController(_ :)并將以下內(nèi)容添加到方法的底部:

sidePanelController.delegate = centerViewController

除了之前的工作之外留储,該方法現(xiàn)在將中心視圖控制器設(shè)置為側(cè)面板的委托翼抠。

應(yīng)該這樣做!

構(gòu)建并運(yùn)行應(yīng)用程序获讳。 查看小貓或小狗,然后點(diǎn)擊其中一個(gè)可愛的小動(dòng)物活喊。 側(cè)面板應(yīng)該再次折疊丐膝,你應(yīng)該看到你選擇的動(dòng)物的細(xì)節(jié)。


Move Your Hands Back and Forth

導(dǎo)航欄按鈕很棒,但大多數(shù)應(yīng)用程序還允許您滑動(dòng)以打開側(cè)面板帅矗。 向您的應(yīng)用添加手勢(shì)非常簡(jiǎn)單偎肃。 不要被暗示,你會(huì)做得很好浑此!

再次打開ContainerViewController.swift累颂。 首先,通過在UIStoryboard擴(kuò)展上方添加以下擴(kuò)展凛俱,使此類符合UIGestureRecognizerDelegate

// MARK: Gesture recognizer

extension ContainerViewController: UIGestureRecognizerDelegate {
  @objc func handlePanGesture(_ recognizer: UIPanGestureRecognizer) {
  }
}

接下來紊馏,找到viewDidLoad()。 將以下內(nèi)容添加到方法的末尾:

let panGestureRecognizer = UIPanGestureRecognizer(
  target: self, 
  action: #selector(handlePanGesture(_:)))
centerNavigationController.view.addGestureRecognizer(panGestureRecognizer)

這將創(chuàng)建一個(gè)UIPanGestureRecognizer蒲犬,將self指定為target朱监,并將handlePanGesture(_ :)作為選擇器來處理任何檢測(cè)到的拖動(dòng)手勢(shì)。

默認(rèn)情況下原叮,拖動(dòng)手勢(shì)識(shí)別器會(huì)檢測(cè)單個(gè)手指的單次觸摸赫编,因此不需要任何額外配置。 您只需要將新創(chuàng)建的手勢(shì)識(shí)別器添加到centerNavigationController視圖中奋隶。

我不是告訴你這很簡(jiǎn)單嗎擂送? 您的滑出導(dǎo)航面板例程中只剩下一個(gè)移動(dòng)。 手勢(shì)識(shí)別器在檢測(cè)到手勢(shì)時(shí)調(diào)用handlePanGesture(_ :)唯欣。 因此团甲,本教程的最后一項(xiàng)任務(wù)是實(shí)現(xiàn)該方法。

將以下代碼塊添加到handlePanGesture(_ :)(這是一個(gè)很多代碼J蚰簟):

// 1
let gestureIsDraggingFromLeftToRight = (recognizer.velocity(in: view).x > 0)

// 2
switch recognizer.state {
// 3
case .began:
  if currentState == .bothCollapsed {
    if gestureIsDraggingFromLeftToRight {
      addLeftPanelViewController()
    } else {
      addRightPanelViewController()
    }
    showShadowForCenterViewController(true)
  }

// 4
case .changed:
  if let rview = recognizer.view {
    rview.center.x = rview.center.x + recognizer.translation(in: view).x
    recognizer.setTranslation(CGPoint.zero, in: view)
  }

// 5
case .ended:
  if let _ = leftViewController,
    let rview = recognizer.view {
    // animate the side panel open or closed based on whether the view
    // has moved more or less than halfway
    let hasMovedGreaterThanHalfway = rview.center.x > view.bounds.size.width
    animateLeftPanel(shouldExpand: hasMovedGreaterThanHalfway)
  } else if let _ = rightViewController,
    let rview = recognizer.view {
    let hasMovedGreaterThanHalfway = rview.center.x < 0
    animateRightPanel(shouldExpand: hasMovedGreaterThanHalfway)
  }

default:
  break
}

下面進(jìn)行細(xì)分:

  • 1) 拖動(dòng)手勢(shì)識(shí)別器可以檢測(cè)任何方向的平移躺苦,但您只對(duì)水平移動(dòng)感興趣。首先产还,設(shè)置gestureIsDraggingFromLeftToRight布爾值匹厘,以使用手勢(shì)速度的x分量來檢查這一點(diǎn)。
  • 2) 需要跟蹤三種狀態(tài):UIGestureRecognizerState.began脐区,UIGestureRecognizerState.changedUIGestureRecognizerState.ended愈诚。使用switch相應(yīng)地處理每個(gè)。
  • 3) .began:如果用戶開始平移且兩個(gè)面板都不可見牛隅,則根據(jù)平移方向顯示正確的面板并使陰影可見炕柔。
  • 4) .changed:如果用戶已經(jīng)在平移,請(qǐng)按用戶平移的距離移動(dòng)中心視圖控制器的視圖
  • 5) .ended:當(dāng)拖動(dòng)結(jié)束時(shí)媒佣,檢查左視圖控制器或右視圖控制器是否可見匕累。根據(jù)哪個(gè)可見以及平移了多遠(yuǎn),執(zhí)行動(dòng)畫默伍。

您可以使用這三種狀態(tài)的組合以及平移手勢(shì)的位置欢嘿,速度和方向來移動(dòng)中心視圖衰琐,顯示和隱藏左視圖和右視圖。

例如炼蹦,如果手勢(shì)方向是右側(cè)羡宙,則顯示左側(cè)面板。如果方向是左側(cè)掐隐,則顯示右側(cè)面板狗热。

再次構(gòu)建并運(yùn)行應(yīng)用程序。此時(shí)虑省,您應(yīng)該能夠左右滑動(dòng)中央面板匿刮,露出下面的面板。

如果您想通過DIY解決方案嘗試預(yù)建庫(kù)慷妙,請(qǐng)務(wù)必查看SideMenu僻焚。 有關(guān)此UI控件的起源(以及內(nèi)存通道)的深入討論,請(qǐng)查看iOS開發(fā)人員和設(shè)計(jì)師Ken Yarmosh發(fā)布的New iOS Design Pattern: Slide-Out Navigation膝擂。 他很好地解釋了使用這種設(shè)計(jì)模式的好處并展示了常見用途虑啤。

后記

本篇主要講述了如何創(chuàng)建自己的側(cè)滑式面板導(dǎo)航,感興趣的給個(gè)贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末架馋,一起剝皮案震驚了整個(gè)濱河市狞山,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叉寂,老刑警劉巖萍启,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異屏鳍,居然都是意外死亡勘纯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門钓瞭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驳遵,“玉大人,你說我怎么就攤上這事山涡〉探幔” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵鸭丛,是天一觀的道長(zhǎng)竞穷。 經(jīng)常有香客問我,道長(zhǎng)鳞溉,這世上最難降的妖魔是什么瘾带? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮穿挨,結(jié)果婚禮上月弛,老公的妹妹穿的比我還像新娘肴盏。我一直安慰自己科盛,他們只是感情好帽衙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贞绵,像睡著了一般厉萝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上榨崩,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天谴垫,我揣著相機(jī)與錄音,去河邊找鬼母蛛。 笑死翩剪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的彩郊。 我是一名探鬼主播前弯,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼秫逝!你這毒婦竟也來了恕出?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤违帆,失蹤者是張志新(化名)和其女友劉穎浙巫,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刷后,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡的畴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了尝胆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丧裁。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖班巩,靈堂內(nèi)的尸體忽然破棺而出渣慕,到底是詐尸還是另有隱情,我是刑警寧澤抱慌,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布逊桦,位于F島的核電站,受9級(jí)特大地震影響抑进,放射性物質(zhì)發(fā)生泄漏强经。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一寺渗、第九天 我趴在偏房一處隱蔽的房頂上張望匿情。 院中可真熱鬧兰迫,春花似錦、人聲如沸炬称。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽玲躯。三九已至据德,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間跷车,已是汗流浹背棘利。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留朽缴,地道東北人善玫。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像密强,于是被迫代替她去往敵國(guó)和親茅郎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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