iOS 抖音首頁頭部滑動標簽的實現

抖音首頁的頭部滑動標簽(通常稱為"Segmented Control"或"Tab Bar")是一個常見的UI組件,可以通過以下幾種方式實現:

1. 使用UISegmentedControl

最簡單的實現方式是使用系統自帶的UISegmentedControl

let segmentedControl = UISegmentedControl(items: ["推薦", "關注", "同城"])
segmentedControl.selectedSegmentIndex = 0 
segmentedControl.addTarget(self, action: #selector(segmentChanged(_:)), for: .valueChanged)
navigationItem.titleView = segmentedControl @objc func segmentChanged(_ sender: UISegmentedControl) {// 處理標簽切換 print("Selected segment: \(sender.selectedSegmentIndex)")
}

2. 自定義實現(更接近抖音效果)

抖音的效果通常是水平滾動的標簽欄,可以這樣實現:

import UIKit class TikTokTabBar: UIView {private let scrollView = UIScrollView()private var buttons: [UIButton] = []private let indicator = UIView()private var currentIndex: Int = 0 var titles: [String] = [] {didSet {setupButtons()}}var onTabSelected: ((Int) -> Void)?override init(frame: CGRect) {super.init(frame: frame)setupUI()}required init?(coder: NSCoder) {super.init(coder: coder)setupUI()}private func setupUI() {scrollView.showsHorizontalScrollIndicator = false addSubview(scrollView)indicator.backgroundColor = .red scrollView.addSubview(indicator)}override func layoutSubviews() {super.layoutSubviews()scrollView.frame = bounds var x: CGFloat = 0 let buttonHeight = bounds.height - 4 let padding: CGFloat = 20 for (index, button) in buttons.enumerated() {let width = button.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: buttonHeight)).width + padding * 2 button.frame = CGRect(x: x, y: 0, width: width, height: buttonHeight)x += width if index == currentIndex {indicator.frame = CGRect(x: button.frame.minX + padding, y: buttonHeight, width: button.frame.width - padding * 2, height: 3)}}scrollView.contentSize = CGSize(width: x, height: bounds.height)}private func setupButtons() {buttons.forEach { $0.removeFromSuperview() }buttons.removeAll()for (index, title) in titles.enumerated() {let button = UIButton(type: .custom)button.setTitle(title, for: .normal)button.setTitleColor(index == 0 ? .white : .lightGray, for: .normal)button.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium)button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)button.tag = index scrollView.addSubview(button)buttons.append(button)}}@objc private func buttonTapped(_ sender: UIButton) {selectTab(at: sender.tag, animated: true)onTabSelected?(sender.tag)}func selectTab(at index: Int, animated: Bool) {guard index >= 0 && index < buttons.count else { return }let button = buttons[index]currentIndex = index UIView.animate(withDuration: animated ? 0.25 : 0) {self.buttons.forEach {$0.setTitleColor($0.tag == index ? .white : .lightGray, for: .normal)}self.indicator.frame = CGRect(x: button.frame.minX + 20, y: button.frame.height, width: button.frame.width - 40, height: 3)// 確保選中的標簽可見 let visibleRect = CGRect(x: button.frame.minX - 30, y: 0, width: button.frame.width + 60, height: self.scrollView.frame.height)self.scrollView.scrollRectToVisible(visibleRect, animated: animated)}}
}

3. 結合PageViewController實現完整效果

要實現抖音首頁的完整效果(滑動標簽同時控制頁面切換),可以結合UIPageViewController

class TikTokHomeViewController: UIViewController {private let tabBar = TikTokTabBar()private var pageViewController: UIPageViewController!private var viewControllers: [UIViewController] = []override func viewDidLoad() {super.viewDidLoad()// 設置標簽欄 tabBar.titles = ["推薦", "關注", "同城"]tabBar.onTabSelected = { [weak self] index in self?.selectPage(at: index, animated: true)}navigationItem.titleView = tabBar // 設置頁面控制器 pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)pageViewController.delegate = self pageViewController.dataSource = self // 添加子控制器 viewControllers = [RecommendationViewController(),FollowingViewController(),NearbyViewController()]addChild(pageViewController)view.addSubview(pageViewController.view)pageViewController.didMove(toParent: self)pageViewController.setViewControllers([viewControllers[0]], direction: .forward, animated: false)}private func selectPage(at index: Int, animated: Bool) {guard index >= 0 && index < viewControllers.count else { return }let direction: UIPageViewController.NavigationDirection = index > tabBar.currentIndex ? .forward : .reverse pageViewController.setViewControllers([viewControllers[index]], direction: direction, animated: animated)tabBar.selectTab(at: index, animated: animated)}
}extension TikTokHomeViewController: UIPageViewControllerDelegate, UIPageViewControllerDataSource {func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {guard let index = viewControllers.firstIndex(of: viewController), index > 0 else { return nil }return viewControllers[index - 1]}func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {guard let index = viewControllers.firstIndex(of: viewController), index < viewControllers.count - 1 else { return nil }return viewControllers[index + 1]}func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {if completed, let currentVC = pageViewController.viewControllers?.first, let index = viewControllers.firstIndex(of: currentVC) {tabBar.selectTab(at: index, animated: true)}}
}

高級優化

1. 動畫效果:可以添加更流暢的滑動動畫和指示器動畫
2. 字體縮放:選中的標簽可以放大字體,未選中的縮小
3. 預加載:預加載相鄰的頁面以提高響應速度
4. 性能優化:對于大量標簽,實現重用機制

下面是優化的具體實現

1. 平滑滑動動畫與指示器效果優化

實現思路

  • 監聽UIScrollView的滾動偏移量
  • 根據偏移量動態計算指示器位置和寬度
  • 實現標簽顏色漸變效果

代碼實現

// 在TikTokTabBar類中添加以下方法 
private var lastContentOffset: CGFloat = 0 func scrollViewDidScroll(_ scrollView: UIScrollView) {guard scrollView.isTracking || scrollView.isDragging || scrollView.isDecelerating else { return }let offsetX = scrollView.contentOffset.x let scrollViewWidth = scrollView.bounds.width let progress = (offsetX / scrollViewWidth) - CGFloat(currentIndex)// 防止快速滑動時progress超出范圍 let clampedProgress = max(-1, min(1, progress))updateTabAppearance(progress: clampedProgress)updateIndicatorPosition(progress: clampedProgress)lastContentOffset = offsetX 
}private func updateTabAppearance(progress: CGFloat) {let absProgress = abs(progress)for (index, button) in buttons.enumerated() {// 當前標簽和下一個標簽 if index == currentIndex || index == currentIndex + (progress > 0 ? 1 : -1) {let isCurrent = index == currentIndex let targetIndex = isCurrent ? (progress > 0 ? currentIndex + 1 : currentIndex - 1) : currentIndex guard targetIndex >= 0 && targetIndex < buttons.count else { continue }let targetButton = buttons[targetIndex]// 顏色漸變 let currentColor = UIColor.white let targetColor = UIColor.lightGray let color = isCurrent ? currentColor.interpolate(to: targetColor, progress: absProgress) : targetColor.interpolate(to: currentColor, progress: absProgress)button.setTitleColor(color, for: .normal)// 字體縮放 let minScale: CGFloat = 0.9 let maxScale: CGFloat = 1.1 let scale = isCurrent ? maxScale - (maxScale - minScale) * absProgress : minScale + (maxScale - minScale) * absProgress button.transform = CGAffineTransform(scaleX: scale, y: scale)} else {// 其他標簽保持默認狀態 button.setTitleColor(.lightGray, for: .normal)button.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)}}
}private func updateIndicatorPosition(progress: CGFloat) {guard currentIndex >= 0 && currentIndex < buttons.count else { return }let currentButton = buttons[currentIndex]var nextIndex = currentIndex + (progress > 0 ? 1 : -1)nextIndex = max(0, min(buttons.count - 1, nextIndex))let nextButton = buttons[nextIndex]let absProgress = abs(progress)// 計算指示器位置和寬度 let currentFrame = currentButton.frame let nextFrame = nextButton.frame let originX = currentFrame.minX + (nextFrame.minX - currentFrame.minX) * absProgress let width = currentFrame.width + (nextFrame.width - currentFrame.width) * absProgress indicator.frame = CGRect(x: originX + 20,y: currentFrame.height,width: width - 40,height: 3 )
}// UIColor擴展,用于顏色插值 
extension UIColor {func interpolate(to color: UIColor, progress: CGFloat) -> UIColor {var fromRed: CGFloat = 0, fromGreen: CGFloat = 0, fromBlue: CGFloat = 0, fromAlpha: CGFloat = 0 var toRed: CGFloat = 0, toGreen: CGFloat = 0, toBlue: CGFloat = 0, toAlpha: CGFloat = 0 self.getRed(&fromRed, green: &fromGreen, blue: &fromBlue, alpha: &fromAlpha)color.getRed(&toRed, green: &toGreen, blue: &toBlue, alpha: &toAlpha)let red = fromRed + (toRed - fromRed) * progress let green = fromGreen + (toGreen - fromGreen) * progress let blue = fromBlue + (toBlue - fromBlue) * progress let alpha = fromAlpha + (toAlpha - fromAlpha) * progress return UIColor(red: red, green: green, blue: blue, alpha: alpha)}
}

2. 字體縮放效果優化

實現思路

  • 根據滑動進度動態調整標簽字體大小
  • 當前選中標簽放大,相鄰標簽適當縮小
  • 其他標簽保持最小尺寸

代碼實現
上面的updateTabAppearance方法已經包含了字體縮放邏輯,這里補充字體縮放的具體參數:

// 在updateTabAppearance方法中添加以下參數 
let minScale: CGFloat = 0.9   // 最小縮放比例 
let maxScale: CGFloat = 1.1  // 最大縮放比例 
let scale = isCurrent ? maxScale - (maxScale - minScale) * absProgress : minScale + (maxScale - minScale) * absProgress button.transform = CGAffineTransform(scaleX: scale, y: scale)

3. 頁面預加載機制

實現思路

  • 預加載當前頁面相鄰的頁面
  • 使用UIPageViewController的緩存機制
  • 監聽滑動方向提前準備內容

代碼實現

// 在TikTokHomeViewController中添加預加載邏輯 
private var pendingIndex: Int?
private var direction: UIPageViewController.NavigationDirection = .forward func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {if let pendingVC = pendingViewControllers.first,let index = viewControllers.firstIndex(of: pendingVC) {pendingIndex = index }
}func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {if completed, let pendingIndex = pendingIndex {currentIndex = pendingIndex tabBar.selectTab(at: currentIndex, animated: true)// 預加載相鄰頁面 preloadAdjacentPages()}pendingIndex = nil 
}private func preloadAdjacentPages() {// 預加載前一個頁面 if currentIndex > 0 {let previousIndex = currentIndex - 1 if let previousVC = pageViewController.dataSource?.pageViewController(pageViewController,viewControllerBefore: viewControllers[currentIndex]) {// 確保視圖已加載 _ = previousVC.view }}// 預加載后一個頁面 if currentIndex < viewControllers.count - 1 {let nextIndex = currentIndex + 1 if let nextVC = pageViewController.dataSource?.pageViewController(pageViewController,viewControllerAfter: viewControllers[currentIndex]) {// 確保視圖已加載 _ = nextVC.view }}
}// 修改selectPage方法以支持方向判斷 
private func selectPage(at index: Int, animated: Bool) {guard index >= 0 && index < viewControllers.count else { return }direction = index > currentIndex ? .forward : .reverse pageViewController.setViewControllers([viewControllers[index]], direction: direction, animated: animated) { [weak self] _ in self?.preloadAdjacentPages()}currentIndex = index tabBar.selectTab(at: index, animated: animated)
}

4. 性能優化與標簽重用

實現思路

  • 對于大量標簽,實現重用機制
  • 只保留可視區域附近的標簽
  • 動態加載和卸載標簽

代碼實現

// 在TikTokTabBar中添加重用邏輯 
private let reusableQueue = NSMutableSet()
private var visibleButtons = 
private var allTitles = func setTitles(_ titles: [String]) {allTitles = titles updateVisibleButtons()
}private func updateVisibleButtons() {// 計算當前可見范圍 let visibleRange = calculateVisibleRange()// 移除不再可見的按鈕 for (index, button) in visibleButtons {if !visibleRange.contains(index) {button.removeFromSuperview()reusableQueue.add(button)visibleButtons.removeValue(forKey: index)}}// 添加新可見的按鈕 for index in visibleRange {if visibleButtons[index] == nil {let button = dequeueReusableButton()configureButton(button, at: index)scrollView.addSubview(button)visibleButtons[index] = button }}// 更新布局 setNeedsLayout()
}private func calculateVisibleRange() -> ClosedRange<Int> {let contentOffsetX = scrollView.contentOffset.x let visibleWidth = scrollView.bounds.width // 計算第一個和最后一個可見的索引 var startIndex = 0 var endIndex = allTitles.count - 1 // 這里可以添加更精確的計算邏輯 // 例如根據按鈕寬度和偏移量計算 // 擴展可見范圍,預加載左右各2個 startIndex = max(0, startIndex - 2)endIndex = min(allTitles.count - 1, endIndex + 2)return startIndex...endIndex 
}private func dequeueReusableButton() -> UIButton {if let button = reusableQueue.anyObject() as? UIButton {reusableQueue.remove(button)return button }return UIButton(type: .custom)
}private func configureButton(_ button: UIButton, at index: Int) {button.setTitle(allTitles[index], for: .normal)button.setTitleColor(index == currentIndex ? .white : .lightGray, for: .normal)button.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium)button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)button.tag = index 
}// 在scrollViewDidScroll中調用updateVisibleButtons 
func scrollViewDidScroll(_ scrollView: UIScrollView) {updateVisibleButtons()// 其他滾動邏輯...
}

5. 綜合優化與細節處理

5.1 彈性效果限制

// 在TikTokTabBar中 
scrollView.bounces = false 
scrollView.alwaysBounceHorizontal = false 

5.2 點擊動畫效果

@objc private func buttonTapped(_ sender: UIButton) {// 點擊動畫 UIView.animate(withDuration: 0.1, animations: {sender.transform = CGAffineTransform(scaleX: 0.95, y: 0.95)}) { _ in UIView.animate(withDuration: 0.1) {sender.transform = .identity }}selectTab(at: sender.tag, animated: true)onTabSelected?(sender.tag)
}

5.3 性能優化提示

// 在TikTokTabBar的初始化中 
layer.shouldRasterize = true 
layer.rasterizationScale = UIScreen.main.scale 

5.4 內存管理優化

// 在視圖控制器中 
deinit {scrollView.delegate = nil 
}

總結

通過以上高級優化實現,你可以獲得一個接近抖音效果的滑動標簽欄,具有以下特點:

  1. 平滑的滑動動畫和指示器過渡效果
  2. 動態字體縮放和顏色漸變
  3. 高效的頁面預加載機制
  4. 優化的性能與內存管理
  5. 標簽重用機制支持大量標簽

這些優化可以顯著提升用戶體驗,使滑動更加流暢,響應更加迅速,同時保持良好的內存使用效率。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/909065.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/909065.shtml
英文地址,請注明出處:http://en.pswp.cn/news/909065.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ThreadLocal實現原理

ThreadLocal 是 Java 中實現線程封閉&#xff08;Thread Confinement&#xff09;的核心機制&#xff0c;它通過為每個線程創建變量的獨立副本來解決多線程環境下的線程安全問題。 Thread └── ThreadLocalMap (threadLocals) // 每個線程持有的專屬Map├── Entry[] tab…

【筆記】結合 Conda任意創建和配置不同 Python 版本的雙軌隔離的 Poetry 虛擬環境

如何結合 Conda 任意創建和配置不同 Python 版本的雙軌隔離的Poetry 虛擬環境&#xff1f; 在 Python 開發中&#xff0c;為不同項目配置獨立且適配的虛擬環境至關重要。結合 Conda 和 Poetry 工具&#xff0c;能高效創建不同 Python 版本的 Poetry 虛擬環境&#xff0c;接下來…

defineAsyncComponent

下面,我們來系統的梳理關于 defineAsyncComponent 懶加載 的基本知識點: 一、異步組件核心概念 1.1 什么是異步組件? 異步組件是 Vue 中一種按需加載組件的機制,允許將組件代碼拆分為獨立的 chunk,在需要時再從服務器加載。這種技術能顯著提升應用初始加載速度。 1.2 為…

ANeko v1.0.3 | 在手機里養只寵物貓 實時互動 動畫細膩

ANeko是一款專為喜歡貓咪的用戶設計的互動養寵應用。它讓你在手機屏幕上擁有一只可愛的貓咪動畫&#xff0c;這只貓咪會實時跟隨你的手指觸摸軌跡&#xff0c;帶來生動有趣的互動體驗。該應用不僅保留了用戶熟悉的交互式貓動畫&#xff0c;還結合了現代高清圖形技術&#xff0c…

人工智能AI

AI 簡介 AI 使我們能夠生成可以改進衛生保健的出色軟件,讓人能夠克服生理上的不便,改進智能基礎結構,創造令人驚嘆的娛樂體驗,甚至拯救地球! 什么是 AI? 簡而言之,AI 就是一種模仿人類行為和能力的軟件。 關鍵工作負載包括: 機器學習 - 它通常是 AI 系統的基礎,也是…

Vue 中 data 選項:對象 vs 函數

Vue 中 data 選項&#xff1a;對象 vs 函數 在 Vue 開發中&#xff0c;data 選項可以使用對象或函數形式&#xff0c;了解它們的使用場景非常重要。下面我將通過一個直觀的示例來展示兩者的區別和適用場景。 <!DOCTYPE html> <html lang"zh-CN"> <h…

python打卡第49天

知識點回顧&#xff1a; 通道注意力模塊復習空間注意力模塊CBAM的定義 CBAM 注意力模塊介紹 從 SE 到 CBAM&#xff1a;注意力機制的演進 之前我們介紹了 SE&#xff08;Squeeze-and-Excitation&#xff09;通道注意力模塊&#xff0c;其本質是對特征進行增強處理。現在&#…

iOS和桌面雙端抓包實戰經驗總結:Sniffmaster與常見工具組合解析

近幾年&#xff0c;移動端和桌面端的網絡調試工作變得越來越“棘手”。過去一個代理證書搞定的場景&#xff0c;現在常常被HTTPS加密、雙向驗證、App安全策略給難住。特別是涉及到iOS平臺時&#xff0c;很多傳統抓包方案都不再適用。作為一名在多個平臺開發和測試的程序員&…

cloudstudio騰訊云:matplotlib 設置中文字體

檢查可用字體&#xff1a; import matplotlib.font_manager as fm fonts [f.name for f in fm.fontManager.ttflist] print(fonts) # 查看系統中可用的字體列表# 列出所有中文字體文件 !fc-list :langzh沒有中文字體&#xff0c;需要下載 !sudo apt-get install fonts-wqy-m…

Django中的ORM的使用步驟----以MySQL為例

1 以純Python的形式創建項目虛擬環境 2 命令安裝Django 3 在當前虛擬環境目錄下命令創建Django項目 4 命令創建app 注&#xff1a; 若想將創建的子應用存放到指定目錄&#xff0c;如app&#xff0c; 那么需要先手動創建app目錄&#xff0c;再手動創建子應用目錄&#xff0c;如o…

Rust 學習筆記:通過 Send 和 Sync trait 實現可擴展并發性

Rust 學習筆記&#xff1a;通過 Send 和 Sync trait 實現可擴展并發性 Rust 學習筆記&#xff1a;通過 Send 和 Sync trait 實現可擴展并發性Send trait&#xff1a;允許在線程之間轉移所有權Sync trait&#xff1a;允許多線程訪問手動實現 Send 和 Sync 是不安全的練習題 Rust…

【C++】第十一節—一文詳解vector(使用+楊輝三角+深度剖析+模擬實現+細節詳細補充)

Hi&#xff0c;我是云邊有個稻草人&#xff0c;偶爾中二的C領域博主^(*&#xffe3;(oo)&#xffe3;)^&#xff0c;與你分享專業知識—— C_本篇博客所屬專欄—持續更新中—歡迎訂閱喔 目錄 一、vector的介紹及使用 1.1 vector的介紹 1.2 vector的使用 &#xff08;1&…

華為智選攜手IAM:突破技術邊界,重塑智慧健康家居新時代

華為智選與IAM的聯動創研&#xff0c;是科技與健康兩大領域深度結合的推動者&#xff0c;更是健康智能家電創新的引領者。他們不再只是產品的制造商&#xff0c;而是生活方式的革新者——用創新科技重構健康生活&#xff0c;用智慧生態重塑家居體驗。在這場深度的跨界融合中&am…

基于cornerstone3D的dicom影像瀏覽器 第三十一章 從PACS服務加載圖像

文章目錄 前言一、兩個服務接口1. 查詢檢查接口2. 查詢圖像接口 二、查詢界面組件三、修改歸檔總結 前言 "基于cornerstone3D的dicom影像瀏覽器"系列文章中都是加載本地文件夾的的dicom圖像。 作為一個合格的dicom影像瀏覽器需要對接PACS服務端&#xff0c;從PACS服…

STM32+rt-thread判斷是否聯網

一、根據NETDEV_FLAG_INTERNET_UP位判斷 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…

基于React Native開發HarmonyOS 5.0醫療類應用

隨著HarmonyOS 5.0的發布和React Native技術的成熟&#xff0c;開發者現在可以利用React Native框架為HarmonyOS平臺構建高性能的跨平臺醫療應用。 一、技術選型與優勢 1.React Native HarmonyOS的組合優勢 &#xff08;1&#xff09;跨平臺能力??&#xff1a;React Nati…

姜偉生《統計至簡》

姜偉生《統計至簡》 系列叢書之一 這套書圖真漂亮&#xff0c;字間距也大&#xff0c;特別合適直接作為課件。但是理論上弱&#xff0c;有的地方算法也get不點上。適合初學者&#xff0c;因為能看圖說話&#xff1b;又不適合初學者&#xff0c;因為沒有解析、沒有分析。 這學…

滾動—橫向滾動時,如何直接滾動到對應的內容板塊

使用scrollIntoView方法方法解讀 scrollIntoView 是 HTML 元素&#xff08;HTMLElement&#xff09;的一個方法。當調用該方法時&#xff0c;它會嘗試將調用它的元素滾動到瀏覽器的可視區域內。這個方法特別適用于處理頁面上的滾動行為&#xff0c;比如讓用戶能夠快速定位到頁面…

HTML5 定位網頁元素

1. 定位&#xff08;position&#xff09; position&#xff1a;static&#xff08;標準&#xff09; position&#xff1a;relative&#xff08;相對定位&#xff09; 偏移量的方向 相對定位的規律 浮動元素設置相對定位 position&#xff1a;absolute&#xff08;絕對…

分類數據集 - 植物分類數據集下載

數據集介紹&#xff1a;植物分類數據集&#xff0c;真實場景高質量圖片數據&#xff1b;適用實際項目應用&#xff1a;自然場景植物分類項目&#xff0c;以及作為通用分類數據集場景數據的補充&#xff1b;數據集類別&#xff1a;標注說明&#xff1a;采用文件夾來區分不同的目…