ios導航欄-UINavigationController使用與分析

UINavigationController就一本書一樣,而UIViewController就是它的每一頁碱屁,我們可以一頁一頁的翻刁绒,也可以瞬間跳到某一頁,或者回歸的過去的某一頁坊罢。

構(gòu)成

我們首先要有一個rootViewController续担,裝進UINavigationController。

例如在AppDelegate的didFinishLaunchingWithOptions中

let navVC = UINavigationController(rootViewController: ViewController())
window?.rootViewController = navVC
window?.backgroundColor = UIColor.gray
window?.makeKeyAndVisible()

ViewController()創(chuàng)建一個UIViewController來初始化UINavigationController, 這樣ViewController()作為導航欄的第一頁顯示出來活孩。

UINavigationController的組成物遇, 引用一下官網(wǎng)的

UI元素除了UIViewController的數(shù)組外,還有navigationBar憾儒,toolbar询兴。

接下里我們豐富一下,將其UI元素全部用上起趾。

ViewController的viewDidLoad中加入代碼

// 顯示toolbar
self.navigationController?.isToolbarHidden = false
self.setToolbarItems([UIBarButtonItem(barButtonSystemItem: .camera, target: nil, action: nil), UIBarButtonItem(barButtonSystemItem: .bookmarks, target: nil, action: nil)], animated: false)
    
// 設置navigationBar的title
self.title = "root view"
    
// 設置navigationBar按鈕
self.navigationItem.setLeftBarButton(UIBarButtonItem(barButtonSystemItem: .add, target: nil, action: nil), animated: false)
self.navigationItem.setRightBarButton(UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: nil), animated: false)

注意:toolbar的items的設置诗舰,是與UIViewController關聯(lián)的,不要UINavigationController上setItems是不會顯示的

跳轉(zhuǎn)

Push

push就是將VC一個一個疊上去训裆,默認行為是從右側(cè)進入

self.navigationItem.setRightBarButton(UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.pushAction(sender:))), animated: false)

func pushAction(sender:UIBarButtonItem) {
    self.navigationController?.pushViewController(ViewController2(), animated: true)
}    

我們將done按鈕加入push動作, 點擊done眶根,從右到左進入,這時候系統(tǒng)會添加一個navigationBar item边琉,用于pop到下層頁面属百,title就是下層頁面設置的title

push對應的返回接口是pop

self.navigationController?.popViewController(animated: true)

Present

說道跳轉(zhuǎn),這里也提一下present变姨,present跳到另外一個頁面族扰,默認行為是從下而上,除了表現(xiàn)上與push不一樣

  • 不是UINavigationController的管理范疇定欧,UIViewController可直接present到其他頁面
  • 同一個UIViewController不可以present兩次渔呵,但present起來的vc是可以再次present
  • present類似模態(tài)窗口,在這個窗口dissmiss前忧额,交互只能在這個窗口做厘肮,之前的頁面不可交互和變化, 就仿佛你放下一本書,就拿起另外一本書睦番,不能同時看兩本書

present對應的消失接口是dissmiss

self.dismiss(animated: true, completion: nil)

Show

8.0以后提供了show方法类茂,這個是根據(jù)viewController所處容器耍属,使用容器的跳轉(zhuǎn)行為,例如viewController所處容器為navigationController(viewController的容器還有splitViewController)巩检,就和push一樣

open func show(_ vc: UIViewController, sender: Any?)

自定義跳轉(zhuǎn)方式(轉(zhuǎn)場動畫)

自定義轉(zhuǎn)場動畫厚骗,需要制定兩個因素

  • 從哪個viewController跳轉(zhuǎn)到哪個viewController
  • 動畫內(nèi)容

自定義 Present

我們嘗試改寫present的動畫效果,它默認是由下而上兢哭,改成自上而下

func pushAction(sender:UIBarButtonItem) {
    let vc = ViewController2()
    vc.transitioningDelegate = self // 實現(xiàn)UIViewControllerTransitioningDelegate協(xié)議
    self.present(vc, animated: true, completion: nil)
}

實現(xiàn)UIViewControllerTransitioningDelegate協(xié)議, 下面就是用來實現(xiàn)领舰,present和dismiss動畫的

@available(iOS 2.0, *)
optional public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning?

    
@available(iOS 2.0, *)
optional public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?

這兩個接口需要返回id<UIViewControllerAnimatedTransitioning>

UIViewControllerAnimatedTransitioning

// 轉(zhuǎn)場時間
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval
// 動畫
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning)

接下來需要創(chuàng)建一個id<UIViewControllerAnimatedTransitioning>的類,

class PresentAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 1
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
        let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
        let containerView = transitionContext.containerView
        
        // 將跳轉(zhuǎn)后的頁面迟螺,設置到屏幕上方
        toViewController?.view.frame = (fromViewController?.view.frame)!
        toViewController?.view.frame.origin.y -= (toViewController?.view.frame.height)!
        
        // 添加到轉(zhuǎn)場動畫容器上冲秽,
        containerView.addSubview((toViewController?.view)!)
        
        UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { () -> Void in
            // 回歸的屏幕位置
            toViewController?.view.frame = (fromViewController?.view.frame)!
        }, completion: { (complete:Bool) -> Void in
            transitionContext.completeTransition(true)
        })
    }
}

經(jīng)過我打印地址研究,得出以下結(jié)論

  • containerView是做動畫用的容器, 初始狀態(tài)是將跳轉(zhuǎn)前頁面(較為頂層的一個view矩父,如果vc在navigationController中就是naviController的view)添加為自己的subview
  • fromViewController不是跳轉(zhuǎn)前vc, 只是將跳轉(zhuǎn)前頁面添加為自己的subview锉桑,用來做舊頁面的退出動畫
  • toViewController則是跳轉(zhuǎn)后vc,做新頁面的登臺動畫
  • transitionDuration時間是提供給系統(tǒng)的窍株,提供跳轉(zhuǎn)時系統(tǒng)的默認表現(xiàn)民轴,自定義animate的時間最好和其保持一致
  • containerView臨時產(chǎn)生,在動畫結(jié)束后球订,從UI上撤掉后裸,但view的改變會遺留下來

注意:transitionContext.completeTransition(true)是必須要調(diào)用的,否則系統(tǒng)認為轉(zhuǎn)場為完成冒滩,第二頁不能進行任何交互

自定義 Dismiss

dismiss 與 present 類似微驶,只是fromViewController變成了第二頁,toViewController變成了第一頁, 記住上述的那幾條結(jié)論來理解containerView.addSubview

class DismissAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 2
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
        let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
        let containerView = transitionContext.containerView
        containerView.addSubview((toViewController?.view)!)
        containerView.addSubview((fromViewController?.view)!)
        UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { () -> Void in
            fromViewController?.view.alpha = 0.0
        }, completion: { (complete:Bool) -> Void in
            transitionContext.completeTransition(true)
        })
    }
}

自定義 Push

只是掛接delegate的位置不同

// 自定義navigationController動畫
self.navigationController?.delegate = self

實現(xiàn)同樣和上面一樣旦部,返回id<UIViewControllerAnimatedTransitioning>, 為了簡單祈搜,我們做之前presentdismiss一樣的動畫

func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    if operation == .push {
        return PresentAnimator();
    } else {
        return DismissAnimator();
    }
}

交互式動畫

最后在說一個present的交互動畫

交互式動畫就隨手勢做的動畫,手拖的距離士八,來決定動畫執(zhí)行的進度

需要再實現(xiàn)UIViewControllerTransitioningDelegate協(xié)議中的交互式接口容燕,要求返回一個id<UIViewControllerInteractiveTransitioning>

optional public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?

    
optional public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?

這個交互式動畫,依賴于之前設置的動畫方式婚度,只是用來設置百分比蘸秘,這個百分比需要你自己不斷提供給他,一般用手勢來不斷調(diào)用id<UIViewControllerInteractiveTransitioning>的update接口蝗茁,我們?yōu)榱撕唵未茁玻胻imer來模擬

創(chuàng)建一個id<UIViewControllerInteractiveTransitioning>

class InteractiveAnimator: UIPercentDrivenInteractiveTransition {
    
}

viewController中加入屬性

var interactiveAnimator:InteractiveAnimator = InteractiveAnimator()
var timer:Timer? = nil
var times:CGFloat = 0;

修改接口

func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?
{
    timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.timeUpdate), userInfo: nil, repeats: true);
    return interactiveAnimator
}
    
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?
{
    timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.timeUpdate), userInfo: nil, repeats: true);
    return interactiveAnimator
}

func timeUpdate() {
    times += 1
    if times <= 5 {
        let per:CGFloat = times/5
        interactiveAnimator.update(per)
    } else {
        timer?.invalidate()
        interactiveAnimator.finish()
    }
}

timeUpdate的會該5次百分比,這個動畫也分5下完成, 每秒變化一次哮翘。timeUpdate這是里timer來調(diào)用的颈嚼,你可以用任意的交互方式來調(diào)用,百分比自己來計算

demo 地址 記得 pod install

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末饭寺,一起剝皮案震驚了整個濱河市阻课,隨后出現(xiàn)的幾起案子叫挟,更是在濱河造成了極大的恐慌,老刑警劉巖限煞,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抹恳,死亡現(xiàn)場離奇詭異,居然都是意外死亡署驻,警方通過查閱死者的電腦和手機奋献,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旺上,“玉大人瓶蚂,你說我怎么就攤上這事「Ч伲” “怎么了扬跋?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長凌节。 經(jīng)常有香客問我,道長洒试,這世上最難降的妖魔是什么倍奢? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮垒棋,結(jié)果婚禮上卒煞,老公的妹妹穿的比我還像新娘。我一直安慰自己叼架,他們只是感情好畔裕,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著乖订,像睡著了一般扮饶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上乍构,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天甜无,我揣著相機與錄音,去河邊找鬼哥遮。 笑死岂丘,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的眠饮。 我是一名探鬼主播奥帘,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼仪召!你這毒婦竟也來了寨蹋?” 一聲冷哼從身側(cè)響起松蒜,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钥庇,沒想到半個月后牍鞠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡评姨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年难述,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吐句。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡胁后,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嗦枢,到底是詐尸還是另有隱情攀芯,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布文虏,位于F島的核電站侣诺,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏氧秘。R本人自食惡果不足惜年鸳,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丸相。 院中可真熱鬧搔确,春花似錦、人聲如沸灭忠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽弛作。三九已至涕蜂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缆蝉,已是汗流浹背宇葱。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留刊头,地道東北人黍瞧。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像原杂,于是被迫代替她去往敵國和親印颤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

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