ScrollView 内 LazyVStack 中高度可变的内容会导致卡顿跳跃

swift

1个回答

写回答

1761830442

2025-08-20 06:45

+ 关注

IOS
IOS

使用ScrollView包裹具有可变高度内容的LazyVStack时,可能会遇到卡顿或跳跃的问题。这是因为当LazyVStack中的内容高度发生变化时,ScrollView需要重新计算其内容的布局和尺寸,从而引起性能问题。为了解决这个问题,我们可以采取一些优化措施。

优化方案一:使用UITableView代替ScrollView和LazyVStack

UITableView是IOS中专门用于展示大量数据的控件,它对于可变高度的内容具有更好的性能优化。我们可以将UITableView作为ScrollView的替代品,并使用UITableViewCell来展示可变高度的内容。UITableView会自动处理内容的布局和尺寸,从而减少卡顿和跳跃的问题。

下面是一个示例代码,演示了如何使用UITableView替代ScrollView和LazyVStack:

Swift

import SwiftUI

struct ContentView: View {

var body: some View {

ScrollView {

VStack {

// 其他内容...

// 使用UITableView替代ScrollView和LazyVStack

UITableViewWrapper {

ForEach(0..<10) { index in</p> UITableViewCellWrapper {

Text("Row \(index)")

.padding()

}

}

}

.frame(height: 500) // 设置UITableView的高度

.background(Color.gray)

// 其他内容...

}

}

}

}

struct UITableViewWrapper<Content: View>: UIViewRepresentable {

let content: Content

init(@ViewBuilder content: () -> Content) {

self.content = content()

}

func makeUIView(context: Context) -> UITableView {

let tableView = UITableView()

tableView.dataSource = context.coordinator

tableView.delegate = context.coordinator

tableView.separatorStyle = .none

tableView.register(UITableViewCellWrapper<Content>.self, forCellReuseIdentifier: "Cell")

return tableView

}

func updateUIView(_ uiView: UITableView, context: Context) {

// 更新UITableView的数据源和委托

uiView.dataSource = context.coordinator

uiView.delegate = context.coordinator

uiView.reloadData()

}

func makeCoordinator() -> Coordinator {

Coordinator(content: content)

}

class Coordinator: NSObject, UITableViewDataSource, UITableViewDelegate {

let content: Content

init(content: Content) {

self.content = content

}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

10 // 返回行数

}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! UITableViewCellWrapper<Content>

cell.setcontent(content)

return cell

}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

UITableView.automaticDimension // 自动计算行高

}

}

}

struct UITableViewCellWrapper<Content: View>: UIViewRepresentable {

let content: Content

init(@ViewBuilder content: () -> Content) {

self.content = content()

}

func makeUIView(context: Context) -> UITableViewCell {

let cell = UITableViewCell()

let hostingController = UIHostingController(rootView: content)

cell.contentView.addSubview(hostingController.view)

hostingController.view.translatesAutoresizingMaskIntoConstrAInts = false

hostingController.view.leadingAnchor.constrAInt(equalTo: cell.contentView.leadingAnchor).isActive = true

hostingController.view.trAIlingAnchor.constrAInt(equalTo: cell.contentView.trAIlingAnchor).isActive = true

hostingController.view.topAnchor.constrAInt(equalTo: cell.contentView.topAnchor).isActive = true

hostingController.view.bottomAnchor.constrAInt(equalTo: cell.contentView.bottomAnchor).isActive = true

return cell

}

func updateUIView(_ uiView: UITableViewCell, context: Context) {

// 更新UITableViewCell的内容

let hostingController = uiView.contentView.subviews.first as! UIHostingController<Content>

hostingController.rootView = content

hostingController.view.invalidateIntrinsicContentSize()

}

}

struct ContentView_Previews: PreviewProvider {

static var previews: some View {

ContentView()

}

}

在上面的代码中,我们首先在ScrollView中使用了UITableViewWrapper来展示可变高度的内容。UITableViewWrapper是一个UIViewRepresentable,它将UITableView封装成一个SwiftUI的View,以便能够在SwiftUI中使用。UITableViewWrapper利用UITableView的数据源和委托方法来实现内容的展示和布局。我们在UITableViewWrapper中创建了一个Coordinator来处理UITableView的数据源和委托,然后将内容作为UITableViewCellWrapper展示在每一行中。

优化方案二:使用UITableView和动态高度的UITableViewCell

如果你不想使用SwiftUI的ScrollView,你也可以在UIKit中直接使用UITableView,并自定义UITableViewCell的高度。这种方式也能有效解决可变高度内容导致的卡顿和跳跃问题。

下面是一个示例代码,演示了如何使用UITableView和动态高度的UITableViewCell:

Swift

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

let tableView = UITableView()

var cellHeights: [IndexPath: CGFloat] = [:]

override func viewDidLoad() {

super.viewDidLoad()

tableView.dataSource = self

tableView.delegate = self

tableView.separatorStyle = .none

tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "Cell")

view.addSubview(tableView)

tableView.translatesAutoresizingMaskIntoConstrAInts = false

tableView.leadingAnchor.constrAInt(equalTo: view.leadingAnchor).isActive = true

tableView.trAIlingAnchor.constrAInt(equalTo: view.trAIlingAnchor).isActive = true

tableView.topAnchor.constrAInt(equalTo: view.topAnchor).isActive = true

tableView.bottomAnchor.constrAInt(equalTo: view.bottomAnchor).isActive = true

}

// 设置UITableView的行数

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

10

}

// 设置UITableViewCell的内容和高度

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomTableViewCell

cell.setcontent("Row \(indexPath.row)")

return cell

}

// 动态计算UITableViewCell的高度

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

if let height = cellHeights[indexPath] {

return height

} else {

let cell = CustomTableViewCell()

cell.setcontent("Row \(indexPath.row)")

let height = cell.systemLayoutSizeFitting(CGSize(width: tableView.frame.width, height: CGFloat.greatestFiniteMagnitude)).height

cellHeights[indexPath] = height

return height

}

}

}

class CustomTableViewCell: UITableViewCell {

let label = UILabel()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {

super.init(style: style, reuseIdentifier: reuseIdentifier)

contentView.addSubview(label)

label.translatesAutoresizingMaskIntoConstrAInts = false

label.leadingAnchor.constrAInt(equalTo: contentView.leadingAnchor, constant: 16).isActive = true

label.trAIlingAnchor.constrAInt(equalTo: contentView.trAIlingAnchor, constant: -16).isActive = true

label.topAnchor.constrAInt(equalTo: contentView.topAnchor, constant: 8).isActive = true

label.bottomAnchor.constrAInt(equalTo: contentView.bottomAnchor, constant: -8).isActive = true

label.numberOfLines = 0

}

required init?(coder: NSCoder) {

fatalError("init(coder:) has not been implemented")

}

func setcontent(_ text: String) {

label.text = text

}

}

在上面的代码中,我们使用了一个UIViewController来展示UITableView,并实现了UITableView的数据源和委托方法。我们自定义了一个CustomTableViewCell来展示可变高度的内容,并使用UILabel来展示文本。在UITableViewDelegate的heightForRowAt方法中,我们动态计算了UITableViewCell的高度,并将计算结果缓存到cellHeights字典中,以便下次使用。

通过以上两种优化方案,我们可以有效解决ScrollView内LazyVStack中可变高度内容导致的卡顿和跳跃问题。根据具体需求选择合适的方案,可以提升用户体验并改善应用性能。

举报有用(4分享收藏

Copyright © 2025 IZhiDa.com All Rights Reserved.

知答 版权所有 粤ICP备2023042255号