美文网首页swift
SwiftUI 高级List分页与无限滚动之高级版(2020教程

SwiftUI 高级List分页与无限滚动之高级版(2020教程

作者: iCloudEnd | 来源:发表于2020-01-26 13:43 被阅读0次

    在《SwiftUI 高级List分页与无限滚动之基础版(2020教程)》中我们介绍了List分页的基础实现方法,但是这种方法没有站在用户的角度思考,下面我们以用户为中心制作分页。

    下面,我们将研究一下如何通过阈值控制获取下一页时间。

    RandomAccessCollection+isThresholdItem
    

    这次,我们将从extentions的RandomAccessCollection开始。这次,我们将实现一个名为isThresholdItem的函数,该函数确定给定item是否为阈值item。

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

    此函数查找给定项目的索引。如果找到,它将计算到终点索引的距离。指定的偏移量(即,结束之前的项目数)应等于distance —1。我们必须从该距离中减去1,因为结束索引等于count属性的值(即,集合中的当前项目数) )。我还为偏移量添加了一个简单的验证检查。偏移量应小于集合中的当前项目数.

    界面部分

    UI实现与第一种方法中的UI几乎相同。但是,有一个关键的区别,那就是listItemAppears函数

    请记住,这里我们从第一种方法重用了isLastItem函数。仅当用户到达列表的末尾并且对下一页的请求仍在进行中时,才会显示加载视图

    struct ListPaginationThresholdExampleView: 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
        private let offset: Int = 10
        
        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)"))
            }
        }
    }
    
    

    代替调用isLastItem,我们调用isThresholdItem来检查给定项目是否为阈值项目。

    extension ListPaginationThresholdExampleView {
        private func listItemAppears<Item: Identifiable>(_ item: Item) {
            if items.isThresholdItem(offset: offset,
                                     item: 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() + 0.5) {
                    self.page += 1
                    let moreItems = self.getMoreItems(forPage: self.page, pageSize: self.pageSize)
                    self.items.append(contentsOf: moreItems)
                    
                    self.isLoading = false
                }
            }
        }
    }
    

    如果您是一位特别专心的读者,您可能会注意到缺少一些代码。让我们进入下一部分。

    载入更多的数据

    extension ListPaginationExampleView {
        /*
            In a real app you would probably fetch data
            from an external API.
         */
        private func getMoreItems(forPage page: Int,
                                  pageSize: Int) -> [String] {
            let maximum = ((page * pageSize) + pageSize) - 1
            let moreItems: [String] = Array(items.count...maximum).map { "Item \($0)" }
            return moreItems
        }
    }
    

    String 实现Identifiable

    /*
       If you want to display an array of strings
       in the List view you have to specify a key path,
       so each string can be uniquely identified.
       With this extension you don't have to do that.
    */
    extension String: Identifiable {
       public var id: String {
           return self
       }
    }
    

    好的,两种方法都给大家介绍了。我先去睡个午觉,下午把整个项目放到github上

    项目完整代码

    //
    //  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
        }
        func isThresholdItem<Item: Identifiable>(offset: Int,
                                                 item: Item) -> Bool {
            guard !isEmpty else {
                return false
            }
            
            guard let itemIndex = firstIndex(where: { AnyHashable($0.id) == AnyHashable(item.id) }) else {
                return false
            }
            
            let distance = self.distance(from: itemIndex, to: endIndex)
            let offset = offset < count ? offset : count - 1
            return offset == (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
        private let offset: Int = 10
        
        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.isThresholdItem(offset: offset,item: 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() + 0.5) {
                    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
        }
     */
        private func getMoreItems(forPage page: Int,
                                    pageSize: Int) -> [DemoItem] {
              let maximum = ((page * pageSize) + pageSize) - 1
              let moreItems: [DemoItem] = Array(items.count...maximum).map { DemoItem(sIndex: $0,page:page)  }
              return moreItems
          }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    
    
    

    项目下载地址

    更多SwiftUI教程和代码关注专栏

    相关文章

      网友评论

        本文标题:SwiftUI 高级List分页与无限滚动之高级版(2020教程

        本文链接:https://www.haomeiwen.com/subject/mzyjthtx.html