在SwiftUI中创建1000个元素的列表时出现性能问题

时间:2019-10-06 10:28:42

标签: ios swift swiftui

我有一个带有列表的水平滚动视图。水平滚动时,如何使列表对齐到边缘。

struct RowView: View {
    var post: Post
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Text(self.post.title)
                Text(self.post.description)
            }.frame(width: geometry.size.width, height: 200)
            //.border(Color(#colorLiteral(red: 0.1764705926, green: 0.01176470611, blue: 0.5607843399, alpha: 1)))
            .background(Color(#colorLiteral(red: 0.721568644, green: 0.8862745166, blue: 0.5921568871, alpha: 1)))
            .cornerRadius(10, antialiased: true)
            .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
        }
    }
}

struct ListView: View {
    var n: Int
    @State var posts = [Post(id: UUID(), title: "1", description: "11"),
                        Post(id: UUID(), title: "2", description: "22"),
                        Post(id: UUID(), title: "3", description: "33")]

    var body: some View {
        GeometryReader { geometry in
            ScrollView {
                ForEach(0..<self.n) { n in
                    RowView(post: self.posts[0])
                    //.border(Color(#colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1)))
                    .frame(width: geometry.size.width, height: 200)
                }
            }
        }
    }
}

struct ContentView: View {
    init() {
        initGlobalStyles()
    }

    func initGlobalStyles() {
        UITableView.appearance().separatorColor = .clear
    }

    var body: some View {
        GeometryReader { geometry in
            NavigationView {
                ScrollView(.horizontal) {
                    HStack {
                        ForEach(0..<3) { _ in
                            ListView(n: 1000)  // crashes
                                .frame(width: geometry.size.width - 60)
                        }
                    }.padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 0))
                }
            }
        }
    }
}

当我提供ListView(n: 1000)的值时,视图崩溃。该应用程序启动后,显示白色屏幕一段时间,然后出现黑色屏幕。

2019-10-06 15:52:57.644766+0530 MyApp[12366:732544] [Render] CoreAnimation: Message::send_message() returned 0x1000000e

如何解决此问题?我的假设是,它将使用类似UITableView之类的出队单元格,但不确定为什么会崩溃。

3 个答案:

答案 0 :(得分:4)

所提供的代码存在两个问题。最重要的是,您使用的不是arrow-kt,而是嵌套在List中的ForEach,这相当于在UIStack中放置1000个UIViews-效率不高。硬编码的维也很多,其中很多是重复的,但是在计算视图时却增加了很大的负担。

我已经简化了很多,它在n = 10000的情况下不会崩溃:

ScrollView

答案 1 :(得分:2)

ScrollView不重用任何内容。但是List可以。

所以改变这个:

ScrollView {
    ForEach(0..<self.n) { n in
        ,,,
    }
}

对此:

List(0..<self.n) { n in
    ,,,
}

答案 2 :(得分:2)

我创建了SwiftUI水平列表,该列表仅加载可见对象的视图+额外的元素作为缓冲区。此外,它会将offset参数公开为绑定,因此您可以跟随它或从外部对其进行修改。

您可以在HList

处访问源代码

试试吧!这个例子是在迅速的操场上准备的。

示例用例

struct ContentView: View {
    @State public var offset: CGFloat = 0
    
    var body: some View {
        HList(offset: self.$offset, numberOfItems: 10000, itemWidth: 80) { index in
            Text("\(index)")
        }
    }
}

要查看实际使用中的内容,您可以执行以下操作

struct ContentView: View {
    @State public var offset: CGFloat = 0
    
    var body: some View {
        HList(offset: self.$offset, numberOfItems: 10000, itemWidth: 80) { index in
            Text("\(index)")
        }
        .frame(width: 200, height: 60)
        .border(Color.black, width: 2)
        .clipped()
    }
}

如果您确实删除了.clipped(),您将看到在移出框架时滚动时如何重复使用多余的组件。

更新

虽然上述解决方案仍然有效,但是它是一个自定义组件。 在WWDC2020中,SwiftUI引入了惰性组件,例如LazyHStack

堆栈是“惰性的”,因为堆栈视图在需要将它们呈现在屏幕上之前不会创建项目。

请记住。我的自定义HList仅引用可见的组件。