iOS 實現NavigationController的titleView動態縮放效果


screenShot.png


自己動手用Object-C和Swift兩種語言各寫了一個簡單的小demo,下面先說一下用Object-C實現的簡單原理.

知識補充=====>

因爲在這個效果實現的過程中我遇到一些關於tableView的contentInset和contentOffset的困擾,所以在這裏我想先解釋明白關於這兩個屬性,然後再談怎樣實現我們需要的效果。

1:概念:

contentSize:The size of the content view.其實就是scrollview可以滾動的區域。比如frame = (0, 0, 320, 480) contentSize = (320, 960),代表scrollview可以上下滾動,滾動區域爲frame大小的兩倍。
contentOffset:The point at which the origin of the content view is offset from the origin of the scroll view.是scrollview當前顯示區域定點相對於frame定點的偏移量,(向屏幕內拉,偏移量是負值。向屏幕外推,偏移量是正數),比如上個例子,從初始狀態向下拉50像素,contentoffset就是(0 ,-50),從初始狀態向上推tableview100像素,contentOffset就是(0 ,100)。
contentInset:The distance that the content view is inset from the enclosing scroll view。是scrollview的contentview的頂點相對於scrollview的位置,例如你的contentInset = (0 ,100),那麼你的contentview就是從scrollview的(0 ,100)開始顯示。
舉個簡單的例子:


截圖1.png

這是一個帶有導航欄的的控制器,控制器上有一個tableView。我將tableView初始化後並打印各個屬性的值:如下圖:


截圖2.png


打印屬性值的時候我是在scrollview的這個代理方法中打印的:

- (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。當前顯示區域頂點相對於frame的偏移量。即是-64,可以理解成從(0,0)的位置向下拉了64像素,上面我們說到過,(向屏幕內拉,偏移量是負值。向屏幕外推,偏移量是正數)。

*contentSize 是tableView的滑動區域。寬度就是屏幕的寬度,高度是cell.Height乘以cell.Count

解釋完這個概念接下來就相對比較好理解了,接下來說一下效果實現的原理。

Object-C實現NavigationController的titleView的動態縮放

demo運行圖片如下圖:


PageBlurTestGif.gif
1 ===>

首先創建一個topBkView用來替代系統的titleView.然後聲明一個全局的_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設爲topBkView的2倍,爲的是顯示出頭像在導航欄上山下各一半的效果.

2 ===>

實現動態縮放的思路主要體現在監聽ScrollView滑動的代理事件中:

//MARK:-滑動代理
- (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;
    }

}

在這裏聲明瞭一個變量contentSet這是scrollView.contentOffset.y_tableView.contentInset.top的和,初始值是0,當tableView向上滑動的時候contentSet爲正值並增大,這裏我們判斷,當contentSet >= 0 && contentSet <= 30時,我們控制_topImageView的縮放量,另外還有兩個情況的判斷已經在代碼中寫出來了,還要說明的一點是在tableView滾動監聽並且改變_topImageView大小的過程中,要始終保持_topImageView.y = 0;要不然_topImageView會隨着大小的變化亂動。

Swift實現NavigationController的titleView的動態縮放

效果如圖:


PageBlurTestGif.gif

這個比較好玩,是我之前看的一個大牛做的一個效果,後來閒着沒事兒用swift寫了一遍。這幾個功能也是咱們平時比較常見的。這一塊我比較懶,沒有進行tableView的複用,每一個功能直接copy的tableview。哈哈。

1 titleView動態縮放===>

先看第一個界面,就是和簡書那個titleView動態縮放比較相似的效果,只是多了一個下拉放大功能,原理是相同的。

//滑動代理方法
    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 滑動隱藏navBar===>

panTranslationY這個變量是監聽tableView在scrollView中滑動狀態的。

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

意思就是你用手指觸動tableView上下滑動時所反映出來的一個數值,向下拉動爲正值,向上拖動爲負值。通過這個值的變動進行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動態縮放===>

先放一張圖大致理解這個效果實現的套路:


截圖3.png


首先在創建tableview的時候需要設置tableview的contentInset,目的是給上部的view留出空間。而且不影響tableview的正常使用。在這裏設置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!)
        }
    }

然後創建頭部背景視圖。我們將這個topImageView 插到tableView下方。這樣的話topImageViewtableview是不在同一層級的不會相互影響,並且我們在初始化tableView的時候已經設置好了tableViewcontentInset了,給topImageView的顯示留出了空間。

//MARK:-創建頂部背景視圖
    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!)
    }

接下來創建頭像視圖,headBkView:UIView的高度也是topContentInset = 100背景顏色設置爲透明。將tableView?.tableHeaderView = headBkView,因爲我們需要頭像跟隨tableview的滑動而移動。這樣我們也給topImageView留出了充足的顯示空間。

//MARK:-創建頭像視圖
    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)
    }

最後就是實現滑動時topImageView的縮放了,在這裏面我有對navBar的透明度的設置,真正起到改變topImageView縮放效果的就是這一句

else if offsetY < 0 { topImageView?.transform = CGAffineTransformMakeScale(1 + offsetY/(-500), 1 + offsetY/(-500)) }
//MARK:-滑動代理
    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

    }

轉自: http://www.jianshu.com/p/bcf3d692f99d

END

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章