3

我有一个列表和一个数组中的大约 1000 个项目,并且我的项目实时更新(100 次更新/秒)。我的清单表现不佳。

这是我的测试代码(我尝试使用 List 和 LazyVStack):


struct PriceBoardView2 : View {
  @EnvironmentObject var loadingEnv : LoadingEnv
  @State var listUser : [UserModel]
   
  var body: some View {
    VStack(content: {
      Button(action: {
        updateData() //fake data realtime update
      }, label: {
        Text("Fake data realtime update")
      })
      List(content: {
        ForEach(listUser.indices, id: \.self) { i in
          RowPriceBoardView2(userModel: listUser[i])
        }
      })
//      ScrollView(content: {
//        LazyVStack(content: {
//          ForEach(listUser.indices, id: \.self) { i in
//            RowPriceBoardView2(userModel: listUser[i])
//          }
//        })
//      })
    })
    .onAppear(perform: {
      for i in 0..<1000 {
        listUser.append(UserModel(number: i, name: "-", age: 0))
      }
    })
  }
   
  func updateData() {
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01, execute: {
      let i = Int.random(in: 0..<1000)
      self.listUser[i] = UserModel(number: i, name: "Pla pla", age: Int.random(in: 0..<1000))
      updateData()
    })
  }
}


struct UserModel {
  var number : Int
   var name : String
   var age : Int
}

struct RowPriceBoardView2: View {
  var userModel : UserModel
   
  var body: some View {
    HStack(content: {
      Text("\(userModel.number)")
      Spacer()
      Text("\(userModel.name)")
      Spacer()
      Text("\(userModel.age)")
    })
    .frame(width: .infinity, height: 30, alignment: .center)
  }
}

4

2 回答 2

1

您执行 a) 主队列上的所有操作,并且 b) 过于频繁地刷新 UI。因此答案是——a)数据准备应该在后台线程上完成,b)如果准备速度更快,则需要以某种方式组合 50ms 数据(这部分应用程序逻辑在你身上)

这是一个将数据准备和 UI 更新分开的模式:


  func updateData() {
    DispatchQueue.global(qos: .background).asyncAfter(deadline: DispatchTime.now() + 0.1, execute: {
      // 1) prepare in background queue
      let i = Int.random(in: 0..<1000)
      let newValue = UserModel(number: i, name: "Pla pla", age: Int.random(in: 0..<1000))

      DispatchQueue.main.async {
            // 2) update UI on main queue
            self.listUser[i] = newValue
            updateData()
        }
    })
  }

于 2021-08-13T16:05:07.887 回答
0

经常更新真的没用。当前设备只有 60 fps,因此无需更新。

但是你不需要硬编码一些值,或者检测当前的 fps(如果新模型有更多的 fps),因为有一个CADisplayLink

这是一种在每次帧更新时触发的计时器,这是更新数据的最佳时机。

我将使用 anObservableObject因为它是CADisplayLink目标所必需的。像这样初始化它:

@StateObject
var model = Model()
  1. 您可以每帧更新一次列表
class Model: ObservableObject {
    @Published
    var listUser = Array(0..<1000)
    
    init() {
        displayLink = CADisplayLink(target: self, selector: #selector(displayLinkHandler))
        displayLink.add(to: .main, forMode: .default)
    }

    private var displayLink: CADisplayLink!
    
    @objc private func displayLinkHandler() {
        let i = listUser.indices.randomElement()!
        listUser[i] = Int.random(in: 0..<1000)
    }
}
  1. 如果您在现实生活中的计算比这更重,您应该将它们移出主线程并暂停计时器以进行计算,如下所示:
class Model: ObservableObject {
    @Published
    var listUser = Array(0..<1000)
    
    init() {
        displayLink = CADisplayLink(target: self, selector: #selector(displayLinkHandler))
        displayLink.add(to: .main, forMode: .default)
    }
    
    private var displayLink: CADisplayLink!
    private let queue = DispatchQueue(label: "modelQueue")
    
    @objc private func displayLinkHandler() {
        displayLink.isPaused = true
        queue.async { [self] in
            var newListUser = listUser
            let i = listUser.indices.randomElement()!
            newListUser[i] = Int.random(in: 0..<1000)
            DispatchQueue.main.async {
                listUser = newListUser
                displayLink.isPaused = false
            }
        }
    }
}
  1. 如果您确实必须每帧多次重新计算,您可以将数据存储在 tmp 变量中,并且只使用 same 更新您的视图值CADisplayLink,如下所示:
class Model: ObservableObject {
    @Published
    var listUser = Array(0..<1000)
    
    init() {
        displayLink = CADisplayLink(target: self, selector: #selector(displayLinkHandler))
        displayLink.add(to: .main, forMode: .default)
        updateData()
    }
    
    private var displayLink: CADisplayLink!
    private var tmpListUser = Array(0..<1000)
    
    private func updateData() {
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.01, execute: { [self] in
            let i = Int.random(in: 0..<1000)
            self.tmpListUser[i] = Int.random(in: 0..<1000)
            updateData()
        })
    }
    
    @objc private func displayLinkHandler() {
        listUser = tmpListUser
    }
}
于 2021-08-14T07:26:56.663 回答