iOS 實(shí)現(xiàn)NavigationController的titleView動(dòng)態(tài)縮放效果

早就心水簡書的個(gè)人中心界面的NavigationController動(dòng)態(tài)縮放titleView效果,也就是如下圖的效果:

screenShot.png

自己動(dòng)手用Object-C和Swift兩種語言各寫了一個(gè)簡單的小demo,下面先說一下用Object-C實(shí)現(xiàn)的簡單原理.

知識補(bǔ)充=====>

因?yàn)樵谶@個(gè)效果實(shí)現(xiàn)的過程中我遇到一些關(guān)于tableView的contentInset和contentOffset的困擾,所以在這里我想先解釋明白關(guān)于這兩個(gè)屬性,然后再談怎樣實(shí)現(xiàn)我們需要的效果。

1:概念:

contentSize:The size of the content view.其實(shí)就是scrollview可以滾動(dòng)的區(qū)域。比如frame = (0, 0, 320, 480) contentSize = (320, 960),代表scrollview可以上下滾動(dòng),滾動(dòng)區(qū)域?yàn)閒rame大小的兩倍。
contentOffset:The point at which the origin of the content view is offset from the origin of the scroll view.是scrollview當(dāng)前顯示區(qū)域定點(diǎn)相對于frame定點(diǎn)的偏移量,(向屏幕內(nèi)拉,偏移量是負(fù)值。向屏幕外推,偏移量是正數(shù)),比如上個(gè)例子,從初始狀態(tài)向下拉50像素,contentoffset就是(0 ,-50),從初始狀態(tài)向上推tableview100像素,contentOffset就是(0 ,100)。
contentInset:The distance that the content view is inset from the enclosing scroll view。是scrollview的contentview的頂點(diǎn)相對于scrollview的位置,例如你的contentInset = (0 ,100),那么你的contentview就是從scrollview的(0 ,100)開始顯示。
舉個(gè)簡單的例子:

截圖1.png

這是一個(gè)帶有導(dǎo)航欄的的控制器,控制器上有一個(gè)tableView。我將tableView初始化后并打印各個(gè)屬性的值:如下圖:

截圖2.png

打印屬性值的時(shí)候我是在scrollview的這個(gè)代理方法中打印的:


- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    
    NSLog(@"contentInset:{%f,%f,%f,%f}", self.tableView.contentInset.top,self.tableView.contentInset.right,self.tableView.contentInset.bottom,self.tableView.contentInset.left);
    
    NSLog(@"contentOffset:{%f,%f}", self.tableView.contentOffset.x, self.tableView.contentOffset.y);
    
    NSLog(@"contentSize:{%f,%f}", self.tableView.contentSize.height, self.tableView.contentSize.width);
}

*contentInset: top=64,即是navBar的高度,說明是從這兒開始顯示tableview而不是從(0,0,0,0)開始顯示的。

*contentOffset: y = -64。當(dāng)前顯示區(qū)域頂點(diǎn)相對于frame的偏移量。即是-64,可以理解成從(0,0)的位置向下拉了64像素,上面我們說到過,(向屏幕內(nèi)拉,偏移量是負(fù)值。向屏幕外推,偏移量是正數(shù))。

*contentSize 是tableView的滑動(dòng)區(qū)域。寬度就是屏幕的寬度,高度是cell.Height乘以cell.Count

解釋完這個(gè)概念接下來就相對比較好理解了,接下來說一下效果實(shí)現(xiàn)的原理。

Object-C實(shí)現(xiàn)NavigationController的titleView的動(dòng)態(tài)縮放

demo運(yùn)行圖片如下圖:

PageBlurTestGif.gif
1 ===>

首先創(chuàng)建一個(gè)topBkView用來替代系統(tǒng)的titleView.然后聲明一個(gè)全局的_topImageView放在topBkView上.在這里我是這樣做的:

//MARK:-createScaleHeaderView
- (void)createScaleHeaderView {
    
    UIView *topBkView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 60, 30)];
    topBkView.backgroundColor = [UIColor clearColor];
    _topImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
    _topImageView.backgroundColor = [UIColor whiteColor];
    _topImageView.layer.cornerRadius = _topImageView.bounds.size.width/2;
    _topImageView.layer.masksToBounds = YES;
    _topImageView.image = [UIImage imageNamed:@"head"];
    [topBkView addSubview:_topImageView];
    self.navigationItem.titleView = topBkView;
}

topImageViewheight設(shè)為topBkView的2倍,為的是顯示出頭像在導(dǎo)航欄上山下各一半的效果.

2 ===>

實(shí)現(xiàn)動(dòng)態(tài)縮放的思路主要體現(xiàn)在監(jiān)聽ScrollView滑動(dòng)的代理事件中:

//MARK:-滑動(dòng)代理
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    
    CGFloat contentSet = scrollView.contentOffset.y + _tableView.contentInset.top;

    if (contentSet >= 0 && contentSet <= 30) {
        _topImageView.transform = CGAffineTransformMakeScale(1 - contentSet/60, 1-contentSet/60);
        _topImageView.y = 0;
    } else if (contentSet > 30) {
        _topImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
        _topImageView.y = 0;
    } else if (contentSet < 0 ) {
        _topImageView.transform = CGAffineTransformMakeScale(1, 1);
        _topImageView.y = 0;
    }
    
}

在這里聲明了一個(gè)變量contentSet這是scrollView.contentOffset.y_tableView.contentInset.top的和,初始值是0,當(dāng)tableView向上滑動(dòng)的時(shí)候contentSet為正值并增大,這里我們判斷,當(dāng)contentSet >= 0 && contentSet <= 30時(shí),我們控制_topImageView的縮放量,另外還有兩個(gè)情況的判斷已經(jīng)在代碼中寫出來了,還要說明的一點(diǎn)是在tableView滾動(dòng)監(jiān)聽并且改變_topImageView大小的過程中,要始終保持_topImageView.y = 0;要不然_topImageView會(huì)隨著大小的變化亂動(dòng)。

Swift實(shí)現(xiàn)NavigationController的titleView的動(dòng)態(tài)縮放

效果如圖:


PageBlurTestGif.gif

這個(gè)比較好玩,是我之前看的一個(gè)大牛做的一個(gè)效果,后來閑著沒事兒用swift寫了一遍。這幾個(gè)功能也是咱們平時(shí)比較常見的。這一塊我比較懶,沒有進(jìn)行tableView的復(fù)用,每一個(gè)功能直接copy的tableview。哈哈。

1 titleView動(dòng)態(tài)縮放===>

先看第一個(gè)界面,就是和簡書那個(gè)titleView動(dòng)態(tài)縮放比較相似的效果,只是多了一個(gè)下拉放大功能,原理是相同的。


//滑動(dòng)代理方法
    func scrollViewDidScroll(scrollView: UIScrollView) {
        
        let offsetY:CGFloat = scrollView.contentOffset.y + (tableView?.contentInset.top)!
        print("offsetY = %f contentOffset.y = %f contentInset.top = %f", offsetY, scrollView.contentOffset.y, tableView?.contentInset.top)
        if offsetY < 0 && offsetY >= -150 {
            topImageView?.transform = CGAffineTransformMakeScale(1 - offsetY/300, 1 - offsetY/300)
        } else if (offsetY >= 0 && offsetY <= 150) {
            topImageView?.transform = CGAffineTransformMakeScale(1 - offsetY/300, 1 - offsetY/300)
        } else if (offsetY > 150) {
            topImageView?.transform = CGAffineTransformMakeScale(0.45, 0.45)
        } else if(offsetY < -150) {
            topImageView?.transform = CGAffineTransformMakeScale(1.5, 1.5)
        }
        var frame:CGRect = (topImageView?.frame)!
        frame.origin.y = 5;
        topImageView?.frame = frame
        
    }
2 滑動(dòng)隱藏navBar===>

panTranslationY這個(gè)變量是監(jiān)聽tableView在scrollView中滑動(dòng)狀態(tài)的。

// translationInView :translation in the coordinate system of the specified view

意思就是你用手指觸動(dòng)tableView上下滑動(dòng)時(shí)所反映出來的一個(gè)數(shù)值,向下拉動(dòng)為正值,向上拖動(dòng)為負(fù)值。通過這個(gè)值的變動(dòng)進(jìn)行navBar的隱藏或顯示。

func scrollViewDidScroll(scrollView: UIScrollView) {
        
        let offsetY:CGFloat = scrollView.contentOffset.y + (tableView?.contentInset.top)!
        let panTranslationY = scrollView.panGestureRecognizer.translationInView(tableView).y
        
        if offsetY > 0 {
            if panTranslationY > 0 {
                //下滑趨勢 顯示
                [self.navigationController?.setNavigationBarHidden(false, animated: true)]
            } else {
                //上滑趨勢 隱藏
                [self.navigationController?.setNavigationBarHidden(true, animated: true)]
            }
        } else {
            [self.navigationController?.setNavigationBarHidden(false, animated: true)]
        }
    }
3 view動(dòng)態(tài)縮放===>

先放一張圖大致理解這個(gè)效果實(shí)現(xiàn)的套路:

截圖3.png

首先在創(chuàng)建tableview的時(shí)候需要設(shè)置tableview的contentInset,目的是給上部的view留出空間。而且不影響tableview的正常使用。在這里設(shè)置topContentInset = 100

func createTableView() -> () {
        
        if (tableView == nil) {
            tableView = UITableView(frame: UIScreen .mainScreen().bounds, style: .Plain)
            tableView?.contentInset = UIEdgeInsetsMake(topContentInset, 0, 0, 0)
            tableView?.delegate = self
            tableView?.dataSource = self
            tableView?.backgroundColor = UIColor.clearColor()
            tableView?.separatorStyle = .SingleLine
            self.view.addSubview(tableView!)
        }
    }

然后創(chuàng)建頭部背景視圖。我們將這個(gè)topImageView 插到tableView下方。這樣的話topImageViewtableview是不在同一層級的不會(huì)相互影響,并且我們在初始化tableView的時(shí)候已經(jīng)設(shè)置好了tableViewcontentInset了,給topImageView的顯示留出了空間。

//MARK:-創(chuàng)建頂部背景視圖
    func createScaleImageView() -> Void {
        
        topImageView = UIImageView(frame: CGRectMake(0, 0, UIScreen .mainScreen().bounds.width, UIScreen .mainScreen().bounds.width*435.5/313.0))
        topImageView?.backgroundColor = UIColor.whiteColor()
        topImageView?.image = UIImage(named: "backImage")
        self.view.insertSubview(topImageView!, belowSubview: tableView!)
    }

接下來創(chuàng)建頭像視圖,headBkView:UIView的高度也是topContentInset = 100背景顏色設(shè)置為透明。將tableView?.tableHeaderView = headBkView,因?yàn)槲覀冃枰^像跟隨tableview的滑動(dòng)而移動(dòng)。這樣我們也給topImageView留出了充足的顯示空間。

//MARK:-創(chuàng)建頭像視圖
    func createHeadView() -> Void {
        
//        topContentInset = 136;//136+64 = 200
        let headBkView:UIView = UIView(frame: CGRectMake(0, 0, UIScreen .mainScreen().bounds.width, topContentInset))
        headBkView.backgroundColor = UIColor.clearColor()
        tableView?.tableHeaderView = headBkView
        
        let headImageView = UIImageView()
        headImageView.bounds = CGRectMake(0, 0, 64, 64)
        headImageView.center = CGPointMake(UIScreen .mainScreen().bounds.width/2.0, (topContentInset - 64)/2.0)
        headImageView.backgroundColor = UIColor.whiteColor()
        headImageView.layer.cornerRadius = headImageView.bounds.size.width / 2.0
        headImageView.layer.masksToBounds = true
        headImageView.image = UIImage(named: "head")
        headBkView.addSubview(headImageView)
    }

最后就是實(shí)現(xiàn)滑動(dòng)時(shí)topImageView的縮放了,在這里面我有對navBar的透明度的設(shè)置,真正起到改變topImageView縮放效果的就是這一句

else if offsetY < 0 { topImageView?.transform = CGAffineTransformMakeScale(1 + offsetY/(-500), 1 + offsetY/(-500)) }
//MARK:-滑動(dòng)代理
    func scrollViewDidScroll(scrollView: UIScrollView) {
        
        let offsetY = scrollView.contentOffset.y + (tableView?.contentInset.top)!
        
        print("\(offsetY)")
        
        if offsetY > topContentInset && offsetY <= topContentInset*2 {
            
            statusBarStyleControl = true
            self.navigationController?.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: UIBarMetrics.Default)
            self.navigationController?.navigationBar.shadowImage = UIImage()
            self.navigationController?.navigationBar.translucent = true
            
        }
        else if (offsetY <= topContentInset && offsetY >= 0) {
            
            statusBarStyleControl = false
            if (self.respondsToSelector(#selector(UIViewController.setNeedsStatusBarAppearanceUpdate))) {
                self.setNeedsStatusBarAppearanceUpdate()
            }
            self.navigationController?.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: UIBarMetrics.Default)
            self.navigationController?.navigationBar.shadowImage = UIImage()
            self.navigationController?.navigationBar.translucent = true
        }
        else if offsetY > topContentInset * 2 {

            self.navigationController?.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: UIBarMetrics.Default)
            self.navigationController?.navigationBar.shadowImage = UIImage()
            self.navigationController?.navigationBar.translucent = true
            
        }
        else if offsetY < 0 {
            topImageView?.transform = CGAffineTransformMakeScale(1 + offsetY/(-500), 1 + offsetY/(-500))
        }
        var frame:CGRect = (topImageView?.frame)!
        frame.origin.y = 0
        topImageView?.frame = frame
        
    }

END

這幾個(gè)小功能的大體思路就大概是這樣的,如果有不正確的地方歡迎批評指正。最后放上代碼鏈接:
https://github.com/irembeu/NavBarTitleViewScaleDemo.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,619評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,155評論 3 425
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,635評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,539評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,255評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,646評論 1 326
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,655評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,838評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,399評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,146評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,338評論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,893評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,565評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,983評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,257評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,059評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,296評論 2 376

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