天天看點

50天iOS挑戰(Swift) - 第5天:模仿網易新聞頂端滑動分類清單50天iOS挑戰(Swift) - 第5天:模仿網易新聞頂端滑動分類清單

50天iOS挑戰(Swift) - 第5天:模仿網易新聞頂端滑動分類清單

50天,每天一個Swift語言的iOS練手項目,覆寫iOS開發的主要知識。貴在堅持,重在思考

文章清單:http://blog.csdn.net/b735098742/article/category/6978601

Github項目:https://github.com/Minecodecraft/50DaysOfSwift

簡介

本Demo模仿網易新聞的頂端分類清單,實作頂端ScrollView與CollectionView的互動,并在滑動或點選時完成字型變大變小等動态元素。

主要知識點: Collection View, UIScrollView,delegate

50天iOS挑戰(Swift) - 第5天:模仿網易新聞頂端滑動分類清單50天iOS挑戰(Swift) - 第5天:模仿網易新聞頂端滑動分類清單

過程

1、 界面搭建

界面采用SB搭建,類似網易新聞即可。建立限制可能稍微複雜些。

50天iOS挑戰(Swift) - 第5天:模仿網易新聞頂端滑動分類清單50天iOS挑戰(Swift) - 第5天:模仿網易新聞頂端滑動分類清單

2、 滾動條的實作

有以下兩個操作可以改變滾動條與CollectionViewCell的index:

1. 點選ScrollView上的項目

2. 滾動CollectionView的Cell

前者可以直接在ScrollView中實作,而後者則需要考慮兩個View之間的互動問題,我們先談前者。

進度條為自定義的UIView,其上為清單對應的ScrollView以及Button。清單上的選項采用UILabel,選中時為18号紅色字型,未選中時為14号黑色,對其點選的響應通過綁定手勢監測器實作。

對清單項目點選的響應

由于我們将清單項對應的Label添加到了ScrollView中,我們可以直接通過其indexOfView方法獲得對應的index

guard let view = gesture.view,

   let index: Int = self.scrollView.subviews.index(of: view)

else { return }

而後直接放大所選項目和縮小原有項目即可,因為要提供給CollectionView,我們在此封裝該方法。

/// 響應設定label比例的方法

///

/// - Parameters:

/// - scale: 變換的比例

/// - index: 目前頁面索引

func setScale(withScale scale: CGFloat, forIndex index: Int) {

  let label = self.scrollView.subviews[index] as! UILabel

  label.textColor = UIColor.init(red: scale, green: 0, blue: 0, alpha: 1)

  // 根據比例設定在14-18區間的字号

  let fontSize = 14 + (18-14) * scale

  label.transform = CGAffineTransform(scaleX: fontSize / 14, y: fontSize / 14)

  //将label移動至中央

  self.scrollToCenter(label)

}

同時,我們要在保持選中标簽的居中,同樣封裝将Label移至中央的方法

/// 将label滑動至scrollView正中的方法

///

/// - Parameter view: 對應的label

func scrollToCenter(_ view: UIView) {

  var offsetX = view.center.x - self.scrollView.frame.size.width * 0.5

  // 如果标簽旁邊沒有剩餘标簽,則不滾動

  if offsetX < 0 {

    offsetX = 0

  }

  

  self.scrollView.setContentOffset(CGPoint(x: offsetX, y: 0), animated: true)

}

最後處理一下選中後Label樣式變化的動畫:我們在調用封裝好的比例縮放方法時送出一個UIView動畫即可

// 點選時,讓點選的放大,其他縮小

UIView.animate(withDuration: 0.3) {

  for i in 0..< self.scrollView.subviews.count {

    let label = self.scrollView.subviews[i]

    if label == gesture.view {

      self.setScale(withScale: 1, forIndex: i)

    } else {

      self.setScale(withScale: 0, forIndex: i)

    }

  }

}

self.scrollToCenter(view)

這樣我們就實作了滾動條以及點選滾動條的操作。關于選中後如何同步控制CollectionViewCell的問題,在後面讨論。

3、 CollectionViewCell的滾動

我們知道,當我們滾動Cell時,上面新聞分類的清單同樣滾動,而與點選清單項目不同的是,滾動時清單項的Label樣式是漸變的。邏輯不難了解,我們直接看代碼:

// 當滑動的時候的響應

func scrollViewDidScroll(_ scrollView: UIScrollView) {

  // 計算整體的比例

  let ratio: CGFloat = scrollView.contentOffset.x / scrollView.frame.size.width

  // 計算出索引值

  let index: Int = Int(ratio)

  // 計算變化的比例

  let scale: CGFloat = ratio - CGFloat(index)

  

  // 修改channelView的大小

  if index+1 < self.channels.count {

    self.channelView.setScale(withScale: scale, forIndex: index+1)

    self.channelView.setScale(withScale: 1 - scale, forIndex: index)

  }

}

UICollectionView的代理方法實作不再贅述。

4、頂端滾動條點選時CollectionViewCell的同步響應

上面我們實作了分别建立滾動條和CollectionView,也實作了各自的邏輯。

由于CollectionView在Controller中,在滑動Cell時可以直接調用滾動條的方法控制滾動條選項的縮放等操作。而我們在點選滾動條選項時,如何讓CollectionView也随之切換呢?

我們知道,如果在滾動條的類中儲存一個指向控制器的強引用,則可以調用其方法來控制CollectionView,但是這樣會造成強引用循環。若控制器弱引用View,View強引用控制器,則會在使用前已經釋放View,且不遵守“對象不應該持有它的父對象”原則。這個問題可以通過代理來解決,即避免了強引用循環,也實作了代碼的解耦。

我們對滾動條類定義如下協定:

protocol MCChannelViewDelegate {

func channelView(_ channelView: MCChannelView, forItemAt index: Int)

}

同時在類中聲明代理:

var delegate: MCChannelViewDelegate?

而後在控制器中遵守協定并實作該代理方法即可:

/// 實作頻道條操作時collectionView同步切換的方法

///

/// - Parameters:

/// - channelView: 操作對應的channelView

/// - index: 切換到的view的索引值

func channelView(_ channelView: MCChannelView, forItemAt index: Int) {

  // 構造要滾動到的位置對應的indexPath

  let indexPath = IndexPath(item: index, section: 0)

  

  // 為了讓點選的時候按鈕能慢慢變大而不調用scrollViewDidScroll直接變大,要先不調用該方法

  self.collectionView.delegate = nil

  self.collectionView.scrollToItem(at: indexPath, at: .init(rawValue: 0), animated: false)

  self.collectionView.delegate = self

}

至此,已經講解了網易新聞頂端滑動分類清單的實作思路。具體細節請見項目Github的Day5目錄中的代碼。

項目源碼位址:50DaysOfSwift,歡迎大家前來支援,随手丢一發Star