日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

高仿网易新闻频道选择器

發布時間:2025/7/14 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 高仿网易新闻频道选择器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

前段時間公司做一個新聞類的項目,需要支持頻道編輯,緩存等功能,界面效果邏輯就按照最新版的網易新聞來,網上沒找到類似的輪子,二話不說直接開擼,為了做到和網易效果一模一樣還是遇到不少坑和細節,這在此分享出來,自己做個記錄,大家覺得有用的話也可以參考。支持手動集成或者cocoapods集成。

項目地址

github.com/yd2008/YDCh…

最終效果

其實基本就和網易一毛一樣了啦,只是為了更加直觀還是貼出兩張圖片

調起方式

因為要彈出一個占據全屏的控件,7.0之前可能是加在window上,但是后面蘋果不建議這么做,所以還是直接present一個控制器出來是最優的選擇。

public class YDChannelSelector: UIViewController 復制代碼

創建

非常簡單,遵守數據源協議和代理協議

class ViewController: UIViewController, YDChannelSelectorDataSource, YDChannelSelectorDelegate// 頻道選擇控制器private lazy var channelSelector: YDChannelSelector = {let sv = YDChannelSelector()sv.dataSource = selfsv.delegate = self// 是否支持本地緩存用戶功能 默認開啟// sv.isCacheLastest = falsereturn sv}() 復制代碼

基于接口傻瓜的原則,呼出窗口最簡單的方法就是系統自帶的present方法就ok。

present(channelSelector, animated: true, completion: nil) 復制代碼

傳遞數據

作為一個頻道選擇器,它需要知道哪些關鍵信息呢?

  • 頻道名字
  • 頻道是否是固定欄目
  • 頻道自己的原始數據

基于以上需求,我設計了頻道結構體

public struct SelectorItem {/// 頻道名稱public var channelTitle: String!/// 是否是固定欄目public var isFixation: Bool!/// 頻道對應初始字典或模型public var rawData: Any?public init(channelTitle: String, isFixation: Bool = false, rawData: Any?) {self.channelTitle = channelTitleself.isFixation = isFixationself.rawData = rawData} } 復制代碼

數據源代理方法和tableView一致,上手簡單容易

public protocol YDChannelSelectorDataSource: class {func numberOfSections(in selector: YDChannelSelector) -> Intfunc selector(_ selector: YDChannelSelector, numberOfItemsInSection section: Int) -> Intfunc selector(_ selector: YDChannelSelector, itemAt indexPath: IndexPath) -> SelectorItem } 復制代碼

代理

用戶做了各種操作后如何通知控制器當前狀態

public protocol YDChannelSelectorDelegate: class {/// 數據源發生變化func selector(_ selector: YDChannelSelector, didChangeDS newDataSource: [[SelectorItem]])/// 點擊了關閉按鈕func selector(_ selector: YDChannelSelector, dismiss newDataSource: [[SelectorItem]])/// 點擊了某個頻道func selector(_ selector: YDChannelSelector, didSelectChannel channelItem: SelectorItem) } 復制代碼

核心思路

如果你只是打算直接用的話那下面已經不用看了,因為以下是記錄初版功能實現的核心思路以及難點介紹,如果感興趣想自己擴展功能或者自定義的話可以看看。

寫在前面: ios9以后蘋果又添加了很多強大的api,所以本插件主要基于幾個新api實現,整個邏輯還是很清晰明了。主要是很多細節比較惡心,后期調試了很久。

控件選擇一眼就能看出 UICollectionView

private lazy var collectionView: UICollectionView = {let layout = UICollectionViewFlowLayout()layout.minimumLineSpacing = itemMarginlayout.minimumInteritemSpacing = itemMarginlayout.itemSize = CGSize(width: itemW, height: itemH)let cv = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)cv.contentInset = UIEdgeInsets.init(top: 0, left: itemMargin, bottom: 0, right: itemMargin)cv.backgroundColor = UIColor.whitecv.showsVerticalScrollIndicator = falsecv.delegate = selfcv.dataSource = selfcv.register(YDChannelSelectorCell.self, forCellWithReuseIdentifier: YDChannelSelectorCellID)cv.register(YDChannelSelectorHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: YDChannelSelectorHeaderID)cv.addGestureRecognizer(longPressGes)return cv }() 復制代碼

最近刪除 & 用戶操作緩存

基于網易的邏輯,在操作時會出現一個新的section叫最近刪除,dismiss時把最近刪除的頻道下移到我的欄目,思路就是在viewWillApperar時操縱數據源,添加最近刪除section,在viewDidDisappear時整理用戶操作,移除最近刪除section,與此同時進行用戶操作的緩存和讀取,具體實現代碼如下:

public override func viewWillAppear(_ animated: Bool) {super.viewWillAppear(animated)// 根據需求處理數據源if isCacheLastest && UserDefaults.standard.value(forKey: operatedDS) != nil { // 需要緩存之前數據 且用戶操作有存儲// 緩存原始數據源if isCacheLastest { cacheDataSource(dataSource: dataSource!, isOrigin: true) }var bool = falselet newTitlesArrs = dataSource!.map { $0.map { $0.channelTitle! } }let orginTitlesArrs = UserDefaults.standard.value(forKey: originDS) as? [[String]]// 之前有存過原始數據源if orginTitlesArrs != nil { bool = newTitlesArrs == orginTitlesArrs! }if bool { // 和之前數據相等 -> 返回緩存數據源let cacheTitleArrs = UserDefaults.standard.value(forKey: operatedDS) as? [[String]]let flatArr = dataSource!.flatMap { $0 }var cachedDataSource = cacheTitleArrs!.map { $0.map { SelectorItem(channelTitle: $0, rawData: nil) }}for (i,items) in cachedDataSource.enumerated() {for (j,item) in items.enumerated() {for originItem in flatArr {if originItem.channelTitle == item.channelTitle {cachedDataSource[i][j] = originItem}}}}dataSource = cachedDataSource} else { // 和之前數據不等 -> 返回新數據源(不處理)}}// 預處理數據源var dataSource_t = dataSourcedataSource_t?.insert(latelyDeleteChannels, at: 1)dataSource = dataSource_tcollectionView.reloadData()}public override func viewDidDisappear(_ animated: Bool) {super.viewDidDisappear(animated)// 移除界面后的一些操作dataSource![2] = dataSource![1] + dataSource![2]dataSource?.remove(at: 1)latelyDeleteChannels.removeAll()} 復制代碼

用戶操作相關

移動主要依賴9.0新增的InteractiveMovement系列接口,通過給collectionView添加長按手勢并監聽拖動的location實現item拖動效果:

@objc private func handleLongGesture(ges: UILongPressGestureRecognizer) {guard isEdit == true else { return }switch(ges.state) {case .began:guard let selectedIndexPath = collectionView.indexPathForItem(at: ges.location(in: collectionView)) else { break }collectionView.beginInteractiveMovementForItem(at: selectedIndexPath)case .changed:collectionView.updateInteractiveMovementTargetPosition(ges.location(in: ges.view!))case .ended:collectionView.endInteractiveMovement()default:collectionView.cancelInteractiveMovement()} } 復制代碼

這里有個小坑就是cell自己的長按手勢會和collectionView的長按手勢沖突,需要在創建cell的時候做沖突解決:

public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {......// 手勢沖突解決longPressGes.require(toFail: cell.longPressGes)...... } 復制代碼

仔細觀察發現網易的有個細節,就是點擊item的時候要先閃爍一下在進入編輯狀態,但是觸碰事件會被collectionView攔截,所以要先自定義collectionView,重寫func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?做下轉換和提前處理:

fileprivate class HitTestView: UIView {open var collectionView: UICollectionView!/// 攔截系統觸碰事件public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {if let indexPath = collectionView.indexPathForItem(at: convert(point, to: collectionView)) { // 在某個cell上let cell = collectionView.cellForItem(at: indexPath) as! YDChannelSelectorCellcell.touchAnimate()}return super.hitTest(point, with: event)} }復制代碼

在編輯模式頻道不能拖到更多欄目里面,需要還原編輯動作,蘋果提供了現成接口,我們只需要實現相應邏輯即可:

/// 這個方法里面控制需要移動和最后移動到的IndexPath(開始移動時) /// - Returns: 當前期望移動到的位置 public func collectionView(_ collectionView: UICollectionView, targetIndexPathForMoveFromItemAt originalIndexPath: IndexPath, toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath {let item = dataSource![proposedIndexPath.section][proposedIndexPath.row]if proposedIndexPath.section > 0 || item.isFixation { // 不是我的欄目 或者是固定欄目return originalIndexPath} else {return proposedIndexPath} } 復制代碼

用戶操作后的數據源處理

用戶操作完后對數據源要操作方法是func handleDataSource(sourceIndexPath: IndexPath, destinationIndexPath: IndexPath), 調用時間有兩個,一是拖動編輯后調用,二就是點擊事件調用,為了數據源越界統一在此處理:

private func handleDataSource(sourceIndexPath: IndexPath, destinationIndexPath: IndexPath) {let sourceStr = dataSource![sourceIndexPath.section][sourceIndexPath.row]if sourceIndexPath.section == 0 && destinationIndexPath.section == 1 { // 我的欄目 -> 最近刪除latelyDeleteChannels.append(sourceStr)}if sourceIndexPath.section == 1 && destinationIndexPath.section == 0 && !latelyDeleteChannels.isEmpty { // 最近刪除 -> 我的欄目latelyDeleteChannels.remove(at: sourceIndexPath.row)}dataSource![sourceIndexPath.section].remove(at: sourceIndexPath.row)dataSource![destinationIndexPath.section].insert(sourceStr, at: destinationIndexPath.row)// 通知代理delegate?.selector(self, didChangeDS: dataSource!)// 存儲用戶操作cacheDataSource(dataSource: dataSource!) } 復制代碼

以上就是項目核心思路和具體實現過程,歡迎使用,求Star~ 后續還會添加oc版本和外部的tab滑動條,敬請期待!你有好的建議或者問題也可隨時pull request或者issue我。

轉載于:https://juejin.im/post/5bfb91dee51d4548657d0265

總結

以上是生活随笔為你收集整理的高仿网易新闻频道选择器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产又粗又大又长 | 亚洲精品国产成人无码 | 蜜臀少妇久久久久久久高潮 | 特黄老太婆aa毛毛片 | 在线视频观看一区 | 97精品人妻一区二区三区蜜桃 | 蜜桃视频久久一区免费观看入口 | 夜夜操网 | 无人在线观看的免费高清视频 | 97公开视频 | 免费处女在线破视频 | 九九国产精品视频 | 日韩一级片在线播放 | 中文在线字幕观看 | 日本视频免费在线 | 美女av在线播放 | 国产精品一区二区三区免费视频 | 国产馆av | 婷婷综合激情网 | 北条麻妃一区二区三区四区五区 | 日本在线观看一区二区三区 | 精品不卡一区二区 | 日批黄色片 | 超碰2020| 男人免费网站 | 久久伊人一区二区 | 欲色网站| 精品日本视频 | 日韩在线色 | 久久一线 | 中文av网站 | 香蕉视频链接 | 白白色在线播放 | 欧美激情在线观看一区 | www视频在线观看 | 大胸美女被爆操 | 可以看的毛片 | 97久久综合| 3d动漫啪啪精品一区二区中文字幕 | 97av在线播放| 亚洲精品中文字幕乱码三区91 | 毛片导航| 亚洲伦理视频 | 日韩精品自拍 | 一区二区免费在线视频 | 国产乱妇乱子 | 精品国产乱码久久久久夜深人妻 | 特黄在线 | 免费的a级片 | 日本少妇激情 | 波岛野结衣 | 亚洲国产成人精品久久久 | 日韩三级久久 | 日本视频网站在线观看 | 欧美日韩在线看 | 亚洲高h| 黑人巨大精品欧美一区二区免费 | 免费在线观看成人 | 亚洲日本中文 | 日本不卡中文字幕 | 一本之道久久 | 亚洲综合在线中文字幕 | 日本一区二区三区网站 | 青青草免费观看视频 | 一区二区三区av | av色婷婷| 在线观看福利网站 | 一区二区三区四区视频在线观看 | 亚洲人在线播放 | 欧美综合一区二区 | 99插插插| 国产日日夜夜 | 国产精品福利在线 | 香蕉福利 | 久久国产精彩视频 | 国内成人自拍 | 亚洲毛片在线免费观看 | 亚洲av无码国产精品久久不卡 | 日韩综合网站 | 香蕉视频在线播放 | 亚洲大逼| 性xxxx视频| 爱情岛论坛亚洲品质自拍视频 | 成为性瘾网黄的yy对象后 | 色偷偷中文字幕 | 国产一区二区三区网站 | 无码粉嫩虎白一线天在线观看 | 中文日韩 | 国产成人无码精品久久久性色 | 久久精品国产熟女亚洲AV麻豆 | av天天堂| 丁香婷婷视频 | 午夜宅男影院 | 中文字幕永久在线观看 | 春草 | 日日躁夜夜躁白天躁晚上躁91 | 久久久久久色 | 五月天av网站 | 天天做日日干 |