模擬鳳凰新聞 | 更復雜的標籤動畫 - Swift 實現多個 TableView 的側滑與切換

模擬鳳凰新聞 | 更復雜的標籤動畫 - Swift 實現多個 TableView 的側滑與切換

原文連接:模擬鳳凰新聞 | 更復雜的標籤動畫 - Swift 實現多個 TableView 的側滑與切換
項目源碼:github 倉庫:模擬鳳凰新聞首頁ios

下午逛 SegmentFault 時看到有人問如何實現鳳凰新聞 app 首頁效果,正好這兩天在學習如何實現多個 TableView 的側滑與切換,索性本身嘗試一下。git

目標和成果

如圖:github

簡單列一下關鍵點:swift

  1. 跟隨滑動app

  2. 點擊事件ide

總結

  1. 鳳凰新聞 app 裏面下劃線是在下面的 ScrollView 滾動動畫結束以後纔開始側滑的,因此須要監聽滾動是否結束。學習

    • 我剛開始想用 scrollViewDidEndScrollingAnimation,結果並不行。這個方法具體使用場景我還沒搞清楚。測試

    • 應該使用 scrollViewDidEndDecelerating,當 ScrollView 中止減速的時候即動畫結束的時候。字體

  2. 剛開始忘記了點擊事件,因此標籤用的 UILabel,其實能夠換成 UIButton。這樣就能省去尋找位置那一步。不過感受 UIButton 的樣式調整也很麻煩。幸運的是 UILabel 默認樣式(字體、字號)很是符合這個項目要求。動畫

  3. 不要忘記設置每一個 ScrollView 的 delegate;不要忘記在 delegate 方法中判斷當前是哪一個 ScrollView

源碼

//
    //  ViewController.swift
    //  FenghuangXinwen
    //
    //  Created by Ant on 4/8/16.
    //  Copyright © 2016 Ant. All rights reserved.
    //
    
    import UIKit
    
    class ViewController: UIViewController, UIScrollViewDelegate {
        
        @IBOutlet weak var tabScrollView: UIScrollView!
        @IBOutlet weak var contentScrollView: UIScrollView!
        
        let tabLine = UIView() //tab 標籤下劃線
        let TAB_LINE_HEIGHT = CGFloat(2) //tab 標籤下劃線高度
        let tabTitles = [
            "頭條",
            "推薦",
            "娛樂",
            "財經",
            "自媒體",
            "鳳凰衛視",
            "科技",
            "良品",
            "美女",
            "軍事",
            "體育",
            "歷史",
            "汽車",
            "時尚",
            "房產",
            "FUN來了",
            "段子",
            "萌物",
        ] //tab 標籤標題
        
        var tabLbls: [UILabel] = [] //tab 標籤對應的 UILabl
        
        //定義要用到的顏色及 RGB 值差,用於顏色變化
        let TEXT_COLOR_NORMAL = UIColor(red: 115/255, green: 120/255, blue: 134/255, alpha: 1)
        let TEXT_COLOR_ACTIVE = UIColor(red: 245/255, green: 67/255, blue: 66/255, alpha: 1)
        let TEXT_COLOR_NORMAL_RED = CGFloat(115)
        let TEXT_COLOR_NORMAL_GREEN = CGFloat(120)
        let TEXT_COLOR_NORMAL_BLUE = CGFloat(134)
        let TEXT_COLOR_ACTIVE_RED = CGFloat(245)
        let TEXT_COLOR_ACTIVE_GREEN = CGFloat(67)
        let TEXT_COLOR_ACTIVE_BLUE = CGFloat(66)
        let TEXT_COLOR_RED_DIF = CGFloat(130)
        let TEXT_COLOR_GREEN_DIF = CGFloat(-53)
        let TEXT_COLOR_BLUE_DIF = CGFloat(-68)
        let TAB_LINE_COLOR = UIColor(red: 245/255, green: 67/255, blue: 66/255, alpha: 1)
        
        let MARGIN = CGFloat(20) //tab 標籤左右間距
        
        var currentTabIndex = 0 //當前 tab 標籤 index
        var currentTabX = CGFloat(20) //當前 tab 標籤 x 座標,方便定位
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            //設置 scrollView delegate
            tabScrollView.delegate = self
            contentScrollView.delegate = self
            
            //初始化視圖內容
            initView()
        }
        
        func initView() {
            //定義一些常量方便使用
            let TABSCROLLVIEW_HEIGHT = self.tabScrollView.frame.height //tabScrollview 高度
            let LABEL_Y = TABSCROLLVIEW_HEIGHT / 2 - 5 // 每一個 tab 標籤的 y 座標
            
            //生成 tab 標籤,添加到 tabScrollview 並設置大小位置
            for var i = 0; i < self.tabTitles.count; i++ {
                let tabLbl = UILabel()
                tabLbl.text = tabTitles[i]
                tabLbl.textColor = self.TEXT_COLOR_NORMAL
                tabLbl.sizeToFit()
                tabLbls.append(tabLbl)
                
                self.tabScrollView.addSubview(tabLbl)
                
                if i > 0 {
                    tabLbl.center = CGPointMake( self.MARGIN + self.tabLbls[i-1].center.x + self.tabLbls[i-1].frame.width / 2 + tabLbl.frame.width / 2 , LABEL_Y)
                } else {
                    tabLbl.center = CGPointMake( self.MARGIN + tabLbl.frame.width / 2, LABEL_Y)
                }
                
                //順便生成並添加每一個 tab 頁面對應的 view。用於測試。
                let tabContentView = UIView()
                self.contentScrollView.addSubview(tabContentView)
                tabContentView.backgroundColor = UIColor.whiteColor()
                tabContentView.frame = CGRectMake(self.view.frame.width * CGFloat(i), 0, self.view.frame.width, self.contentScrollView.frame.height)
                let labelInContent = UILabel()
                labelInContent.text = tabTitles[i]
                labelInContent.sizeToFit()
                tabContentView.addSubview(labelInContent)
                labelInContent.center = CGPointMake(tabContentView.frame.width / 2, tabContentView.frame.height / 2 - 100)
            }
            self.contentScrollView.contentSize = CGSizeMake(self.view.frame.width * CGFloat(self.tabLbls.count), self.contentScrollView.frame.height) //設置 contentScrollView 內容大小
            
            //計算並設置 tabScrollView 內容大小
            var TABVIEW_WIDTH = CGFloat(0)
            for tabLbl in self.tabLbls {
                TABVIEW_WIDTH += self.MARGIN + tabLbl.frame.width
            }
            TABVIEW_WIDTH += self.MARGIN
            self.tabScrollView.contentSize = CGSizeMake(TABVIEW_WIDTH, 40)
            
            //默認選中第一個標籤
            self.tabLbls[0].textColor = self.TEXT_COLOR_ACTIVE
            self.currentTabIndex = 0
            self.currentTabX = self.tabLbls[0].frame.origin.x
            
            //添加 tab 標籤下劃線
            //設置位置有一個沒搞清楚的問題:不知爲什麼 y 座標設爲 TABSCROLLVIEW_HEIGHT - self.TAB_LINE_HEIGHT 時,下劃線看不見
            self.tabScrollView.addSubview(self.tabLine)
            self.tabLine.backgroundColor = TAB_LINE_COLOR
            self.tabLine.frame = CGRectMake(MARGIN, TABSCROLLVIEW_HEIGHT - 5, self.tabLbls[0].frame.width, self.TAB_LINE_HEIGHT)
        }
        
        func scrollViewDidScroll(scrollView: UIScrollView) {
            //當 contentScrollView 滾動時
            if scrollView == self.contentScrollView {
                let index = scrollView.contentOffset.x / self.view.frame.width //獲取當前頁面 index
                
                if floor(index) == index {
                    self.currentTabIndex = Int(index)
                    self.currentTabX = self.tabLbls[self.currentTabIndex].frame.origin.x
                }
                
                //阻止第一頁和最後一頁越界滾動
                let MIN_X = CGFloat(0)
                let MAX_X = scrollView.contentSize.width - self.view.frame.width
                let CONTENT_OFFSET_X = scrollView.contentOffset.x
                
                if CONTENT_OFFSET_X < MIN_X {
                    scrollView.contentOffset.x = MIN_X
                } else if CONTENT_OFFSET_X > MAX_X {
                    scrollView.contentOffset.x = MAX_X
                } else {
                    //當沒有越界時,執行『動畫』
                    
                    //初始化一些要用到的值
                    let isLeft = index < CGFloat(self.currentTabIndex)
                    let nextTabIndex = isLeft ? self.currentTabIndex - 1 : index == CGFloat(self.currentTabIndex) ? self.currentTabIndex : self.currentTabIndex + 1 //下一個標籤 index
                    let currentTabWidth = self.tabLbls[self.currentTabIndex].frame.width //當前標籤寬度
                    let nextTabWidth = self.tabLbls[nextTabIndex].frame.width //下一個標籤寬度
                    let widthDif = nextTabWidth - currentTabWidth //兩個標籤寬度差
                    let distance = self.MARGIN + (isLeft ? self.tabLbls[nextTabIndex].frame.width : currentTabWidth) //下劃線須要滑動的距離
                    var offsetPercentage = index - CGFloat(self.currentTabIndex) //當前偏移百分比
                    //若是滑動超過一頁,將偏移百分比設置爲 ±1,避免多餘動畫
                    if offsetPercentage < -1 {
                        offsetPercentage = -1
                    }
                    
                    if offsetPercentage > 1 {
                        offsetPercentage = 1
                    }
                    
                    //改變標籤底部橫線位置和長度
                    self.tabLine.frame = CGRectMake(currentTabX + distance * offsetPercentage, self.tabLine.frame.origin.y, currentTabWidth + widthDif * abs(offsetPercentage), self.tabLine.frame.height)
                    
                    //改變顏色
                    self.tabLbls[nextTabIndex].textColor = UIColor(red: (TEXT_COLOR_NORMAL_RED + TEXT_COLOR_RED_DIF * abs(offsetPercentage)) / 255, green: (TEXT_COLOR_NORMAL_GREEN + TEXT_COLOR_GREEN_DIF * abs(offsetPercentage)) / 255, blue: (TEXT_COLOR_NORMAL_BLUE + TEXT_COLOR_BLUE_DIF * abs(offsetPercentage)) / 255, alpha: 1)
                    self.tabLbls[self.currentTabIndex].textColor = UIColor(red: (TEXT_COLOR_ACTIVE_RED - TEXT_COLOR_RED_DIF * abs(offsetPercentage)) / 255, green: (TEXT_COLOR_ACTIVE_GREEN - TEXT_COLOR_GREEN_DIF * abs(offsetPercentage)) / 255, blue: (TEXT_COLOR_ACTIVE_BLUE - TEXT_COLOR_BLUE_DIF * abs(offsetPercentage)) / 255, alpha: 1)
                }
            }
        }
        
        func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
            if scrollView == self.contentScrollView {
                let TWO_WORD_WIDTH = CGFloat(34) //兩個字標籤的寬度。這個間距實際上是根據本身需求隨便設置的。
                
                //當標籤左邊被遮擋時,調整 tabScrollView x 軸偏移量
                if self.tabLine.frame.origin.x < self.tabScrollView.contentOffset.x {
                    UIView.animateWithDuration(0.4, delay: 0, options: [.CurveEaseInOut], animations: { () -> Void in
                        self.tabScrollView.contentOffset.x = self.tabLine.frame.origin.x - self.MARGIN
                        }, completion: nil)
                }
                
                //當下劃線 x 座標在 tabScrollView 中部以後時,調整 tabScrollView x 軸偏移量
                if self.tabLine.frame.origin.x > self.tabScrollView.frame.width / 2 && self.currentTabIndex + 1 < self.tabLbls.count - 3 {
                    UIView.animateWithDuration(0.4, delay: 0, options: [.CurveEaseInOut], animations: { () -> Void in
                        self.tabScrollView.contentOffset.x = self.tabLine.frame.origin.x - self.MARGIN - TWO_WORD_WIDTH
                        }, completion: nil)
                }
                
                //當標籤右邊被遮擋時,調整 tabScrollView x 軸偏移量
                if self.tabLine.frame.origin.x + self.tabLine.frame.width + self.MARGIN > self.tabScrollView.contentOffset.x + self.tabScrollView.frame.width{
                    UIView.animateWithDuration(0.4, delay: 0, options: [.CurveEaseInOut], animations: { () -> Void in
                        self.tabScrollView.contentOffset.x += (self.tabLine.frame.origin.x + self.tabLine.frame.width + self.MARGIN) - (self.tabScrollView.contentOffset.x + self.tabScrollView.frame.width) + self.MARGIN
                        }, completion: nil)
                }
            }
        }
        
        @IBAction func tabScrollViewTapped(sender: UITapGestureRecognizer) {
            let location = sender.locationInView(self.tabScrollView) //獲取當前點擊事件在 tabScrollView 裏的座標
            
            //循環找到點擊的是哪個標籤,找到時執行方法
            for var i = 0; i < self.tabLbls.count; i++ {
                if CGRectContainsPoint(self.tabLbls[i].frame, location) {
                    self.tabLbls[self.currentTabIndex].textColor = TEXT_COLOR_NORMAL
                    self.tabLbls[i].textColor = TEXT_COLOR_ACTIVE
                    self.currentTabIndex = i
                    self.currentTabX = self.tabLbls[self.currentTabIndex].frame.origin.x
                    
                    self.contentScrollView.contentOffset.x = self.view.frame.width * CGFloat(i)
                    
                    break
                }
            }
        }
    }
相關文章
相關標籤/搜索