• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • SwiftUI 高級List分頁與無限滾動之基礎版(2020教程)

    盡管我們可以訪問List中的具體item,但是我們不知道List滾動到了當前哪個位置,也不知道我們到List末尾的距離。這些數據都是我們進行分頁到基礎。

    Pagination(分頁)對于每個人都有不同的含義,因此我們先給我分頁的目標做個明確定義:

    在滾動過程中,List應提取并追加下一頁的數據。當用戶到達列表末尾且請求仍在進行中時,應顯示加載視圖。

    基于上面的定義,讓我們實現一個解決方案來解決這些問題和給List增加分頁功能

    實現

    在此節中,我們將介紹兩種不同的方案。第一種將更為簡單,第二種將更為高級用戶喜歡。

    第一種方法

    最簡單的方法就是監測當前item是否是最后一個。如果是,我們則觸發一個異步請求去提取下一頁的數據。

    RandomAccessCollection+isLastItem
    

    由于List支持RandomAccessCollection,我們可以創建一個extension并實現isLastItem 函數。Self關鍵詞是必須的,它將限制extension的元素必須實現Identifable。

    好了,上面這段文字沒有深入研究過swift的朋友肯定要懵圈了。大家可以參考我之前文章,簡單了解一下RandomAccessCollection 和Identifiable

    下面是代碼

    extension RandomAccessCollection where Self.Element: Identifiable {
        func isLastItem<Item: Identifiable>(_ item: Item) -> Bool {
            guard !isEmpty else {
                return false
            }
            
            guard let itemIndex = firstIndex(where: { $0.id.hashValue == item.id.hashValue }) else {
                return false
            }
            
            let distance = self.distance(from: itemIndex, to: endIndex)
            return distance == 1
        }
    }
    

    上面代碼用于判斷item是否為List的末尾。

    該函數在集合中查找給定項目的索引。它使用id屬性的哈希值(需要實現Identifiable協議)將其與列表中的其他項目進行比較。如果找到了項目索引,則意味著項目索引與結束索引之間的距離必須恰好為一(結束索引等于集合中當前項目的數量)。這樣我們才能知道給定的項目是最后一個項目

    為了代替hash值的比較,我們可以使用 type-erased wrapper AnyHashable來直接比較Hashable類型。

    guard let itemIndex = firstIndex(where: { AnyHashable($0.id) == AnyHashable(item.id) }) else {
        return false
    }
    

    好了,基礎的業務邏輯我們已經實現,下面我們來實現界面部分。

    界面

    如果滾動到List底部,我們可以一個List 更新事件。為了達到這個目標,我們可以在根視圖新增一個onAppear修飾器(在例子中,我們根視圖是VStack)。onAppear將隨后調用listItemAppears函數。

    如果當前遍歷item是最后一個,一個等待視圖將顯示給用戶。在例子中,我們就用簡單的Text("Loading...")。

    由于SwiftUI是聲明式的,因此下面的代碼不言自明,非常易讀:

    
    struct ListPaginationExampleView: View {
        @State private var items: [String] = Array(0...24).map { "Item \($0)" }
        @State private var isLoading: Bool = false
        @State private var page: Int = 0
        private let pageSize: Int = 25
        
        var body: some View {
            NavigationView {
                List(items) { item in
                    VStack(alignment: .leading) {
                        Text(item)
                        
                        if self.isLoading && self.items.isLastItem(item) {
                            Divider()
                            Text("Loading ...")
                                .padding(.vertical)
                        }
                    }.onAppear {
                        self.listItemAppears(item)
                    }
                }
                .navigationBarTitle("List of items")
                .navigationBarItems(trailing: Text("Page index: \(page)"))
            }
        }
    }
    

    輔助函數listItemAppears內部檢查給定的item是否為最后一個。如果是最后一項,則當前頁面會增加,下一頁的項目會添加到列表中。此外,我們通過isLoading變量跟蹤加載狀態,該變量定義何時顯示加載視圖。

    extension ListPaginationExampleView {
        private func listItemAppears<Item: Identifiable>(_ item: Item) {
            if items.isLastItem(item) {
                isLoading = true
                
                /*
                    Simulated async behaviour:
                    Creates items for the next page and
                    appends them to the list after a short delay
                 */
                DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
                    self.page += 1
                    let moreItems = self.getMoreItems(forPage: self.page, pageSize: self.pageSize)
                    self.items.append(contentsOf: moreItems)
                    
                    self.isLoading = false
                }
            }
        }
    }
    

    通過上面的代碼,當前迭代中的項目是最后一個項目時,我們才獲取項目的下一頁。

    完整代碼

    創建個data.swift用于處理數據問題

    //
    //  data.swift
    //  Swift_pagination_01
    //
    //  Created by cf on 2020/1/26.
    //  Copyright © 2020 cf. All rights reserved.
    //
    
    import Foundation
    import SwiftUI
    
    
    struct DemoItem: Identifiable {
        let id = UUID()
        var sIndex = 0
        var page = 0
    }
    
    
    
    extension RandomAccessCollection where Self.Element: Identifiable {
        func isLastItem<Item: Identifiable>(_ item: Item) -> Bool {
            guard !isEmpty else {
                return false
            }
            
            guard let itemIndex = firstIndex(where: { $0.id.hashValue == item.id.hashValue }) else {
                return false
            }
            
            let distance = self.distance(from: itemIndex, to: endIndex)
            return distance == 1
        }
    }
    
    

    界面部分

    //
    //  ContentView.swift
    //  Swift_pagination_01
    //
    //  Created by cf on 2020/1/26.
    //  Copyright © 2020 cf. All rights reserved.
    //
    
    import SwiftUI
    
    struct ContentView: View {
        @State private var items: [DemoItem] = Array(0...24).map { DemoItem(sIndex: $0,page:0) }
        @State private var isLoading: Bool = false
        @State private var page: Int = 0
        private let pageSize: Int = 25
        
        var body: some View {
            NavigationView {
                List(items) { item in
                    VStack {
                        Text("page:\(item.page) item:\(item.sIndex)")
                      
                        if self.isLoading && self.items.isLastItem(item) {
                            Divider()
                            Text("Loading ...")
                                .padding(.vertical)
    
                        }
      
                    }.onAppear {
                        self.listItemAppears(item)
                    }
                }
                .navigationBarTitle("List of items")
                .navigationBarItems(trailing: Text("Page index: \(page)"))
            }
        }
        
        
    }
    
    extension ContentView {
        private func listItemAppears<Item: Identifiable>(_ item: Item) {
            if items.isLastItem(item) {
                isLoading = true
                
                /*
                    Simulated async behaviour:
                    Creates items for the next page and
                    appends them to the list after a short delay
                 */
                DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
                    self.page += 1
                    let moreItems = self.getMoreItems(forPage: self.page, pageSize: self.pageSize)
                    self.items.append(contentsOf: moreItems)
                    
                    self.isLoading = false
                }
            }
        }
        func getMoreItems(forPage: Int, pageSize: Int) -> [DemoItem]{
            let sitems: [DemoItem] = Array(0...24).map { DemoItem(sIndex: $0,page:forPage) }
            return sitems
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    
    
    

    最終效果

    41085-759b055355e7362e.jpg
    SwiftUI 高級List分頁與無限滾動之基礎版

    項目完成代碼

    https://github.com/zhishidapang/SwiftUI-List-Pagination

    下一步工作

    但這并不是真正的最佳用戶體驗,對吧?在實際應用中,如果要達到或超過定義的閾值,我們希望預加載下一頁。此外,我們僅應在確實有必要時(即,如果請求花費的時間比預期的長),使用加載指示器中斷用戶。我認為,這將帶來更好的用戶體驗。

    考慮到這些用戶體驗的問題,讓我們跳到第二種方法。

    更多SwiftUI教程和代碼關注專欄

    版權聲明:本文為iCloudEnd原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/iCloudEnd/article/details/104086256

    智能推薦

    SwiftUI 基礎之02 NavigationView和List(2020)

    SwiftUI 基礎之NavigationView和List(2020) 效果 [email protected] 更多SwiftUI教程和代碼關注專欄 請關注我的專欄 SwiftUI教程與源碼...

    SwiftUI 基礎之List如何設置風格

    UIKit時代我們可以給UITableview設置不同顯示風格,SwiftUI世界里該如何實現呢? 實現方法非常簡單,請看效果和代碼 效果 [email protected] 代碼 參考文章 https://stackoverflow.com/questions/56498045/remove-extra-separators-below-list-in-swiftui 更多S...

    iOS SwiftUI macOS之List基礎使用

    本文價值與收獲 看完本文后,您將能夠作出下面的界面 image.png 看完本文您將掌握的技能 macOS中list基礎使用 代碼 技術交流 QQ:3365059189 SwiftUI技術交流QQ群:518696470 請關注我的專欄icloudend, SwiftUI教程與源碼 https://www.jianshu.com/c/7b3e3b671970...

    SwiftUI 復用舊項目代碼之UIViewcontroller(2020版教程)

    SwiftUI 復用舊項目代碼之UIViewcontroller(2020版教程) SwiftUI 非常好用,開發界面高效快捷,但是具體到業務層面,復用歷史代碼是必須要研究到事情。 如何在SwiftUI中調用UIViewcontroller 1、 創建一個 Wrapper 例如我們有個Example1ViewController舊代碼,我們可以先通過UIViewControllerRepresen...

    SwiftUI 控件之Button 按鈕初級和高級實用(2020版教程)

    SwiftUI 控件之Button 按鈕初級和高級實用(2020版教程) Button是一個非常基本的UI控件,您可以在所有應用中找到該控件。如果您以前學習過iOS編程,則SwiftUI中的Button與UIKit中的UIButton非常相似。它更加靈活和可定制。本文將給您介紹一下幾個方面的內容: 如何創建一個簡單的按鈕并處理用戶的選擇 如何自定義按鈕的背景,填充和字體 如何在按鈕上添加邊框 如何...

    猜你喜歡

    SwiftUI List 如何顯示dictionary

    本文價值與收獲 看完本文后,您將能夠作出下面的界面 看完本文您將掌握的技能 list基礎使用 顯示dictionary 代碼 技術交流 QQ:3365059189 SwiftUI技術交流QQ群:518696470...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    精品国产乱码久久久久久蜜桃不卡