J'ai une vue de défilement horizontal avec des listes. Lors du défilement horizontal, comment faire en sorte que les listes s'alignent sur les bords.
2019-10-06 15:52:57.644766+0530 MyApp[12366:732544] [Render] CoreAnimation: Message::send_message() returned 0x1000000e
Quand je donne la valeur de ListView(n: 1000) , la vue plante. L'application se lance et un écran blanc s'affiche pendant un certain temps, puis j'obtiens un écran noir.
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))
}
}
}
}
}
Comment régler ceci? Mon hypothèse est qu'il utiliserait quelque chose comme des cellules de UITableView comme UITableView , mais UITableView ne sais pas pourquoi il plante.
3 Réponses :
Il y a quelques problèmes avec le code fourni. Le plus important est que vous n'utilisez pas une List juste un ForEach imbriqué dans un ScrollView , ce qui équivaut à placer 1000 UIViews dans un UIStack - pas très efficace. Il existe également de nombreuses dimensions codées en dur et un bon nombre d'entre elles sont des doublons, mais ajoutent néanmoins une charge importante lorsque les vues sont calculées.
J'ai beaucoup simplifié et cela fonctionne avec n = 10000 sans planter:
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
NavigationView {
ScrollView(.horizontal) {
HStack {
ForEach(0..<3) { _ in
ListView(n: 10000)
.frame(width: geometry.size.width - 60)
}
} .padding([.leading], 10)
}
}
}
}
}
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 {
List(0..<self.n) { n in
RowView(post: self.posts[0])
.frame(height: 200)
}
}
}
struct RowView: View {
var post: Post
var body: some View {
HStack {
Spacer()
VStack {
Spacer()
Text(self.post.title)
Text(self.post.description)
Spacer()
}
Spacer()
} .background(RoundedRectangle(cornerRadius: 10)
.fill(Color(#colorLiteral(red: 0.721568644, green: 0.8862745166, blue: 0.5921568871, alpha: 1))))
}
}
Je tiens à souligner à quel point l'utilisation du modificateur RoundedRectangle sur cornerRadius sur la vue elle-même signifie en cornerRadius de performances. Ma liste est juste passée de retardée à complètement lisse.
ScrollView ne réutilise rien. Mais List fait.
alors changez ceci:
List(0..<self.n) { n in
,,,
}
pour ça:
ScrollView {
ForEach(0..<self.n) { n in
,,,
}
}
Vous pouvez utiliser des piles Lazy comme le LazyVStack et le LazyHStack . Donc, même si vous les utilisez avec ScrollView , ce sera fluide et performant.
confirmé. Essayé avec ScrollView et le processeur est monté en flèche à 200%.
J'ai créé une liste horizontale SwiftUI qui ne charge les vues que pour les objets visibles + un élément supplémentaire en tant que tampon. De plus, il expose le paramètre de décalage en tant que liaison afin que vous puissiez le suivre ou le modifier de l'extérieur.
Vous pouvez accéder au code source iciHList
Essayez! Cet exemple est préparé dans le terrain de jeu rapide.
Exemple de cas d'utilisation
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()
}
}
Pour voir le contenu réutilisé en action, vous pouvez faire quelque chose comme ceci
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)")
}
}
}
Si vous supprimez .clipped() à la fin, vous verrez comment un composant supplémentaire est réutilisé lors du défilement lorsqu'il sort du cadre.
Mise à jour
Bien que la solution ci-dessus soit toujours valide, il s'agit cependant d'un composant personnalisé. Avec WWDC2020 SwiftUI introduit des composants paresseux tels que LazyHStack
La pile est "paresseuse", en ce sens que la vue de pile ne crée pas d'éléments tant qu'elle n'a pas besoin de les rendre à l'écran.
Gardez à l'esprit. Ma listeHList personnalisée ne fait référence qu'aux composants visibles. Pas celui qui est déjà apparu.
Merci pour la mise à jour! Il semble que LazyHStack ne réutilise pas réellement des cellules telles que UITableView et UICollectionView optimisées. Il crée des articles quand ils en ont besoin mais ne les recycle pas réellement. D'après ce que je comprends, votre HList réutilise - n'est-ce pas? N'est-ce pas l'approche la plus optimisée à adopter?
Salut @apunn, je crois que oui! Ma liste HList contient le nombre d'éléments nécessaires pour être visibles à l'écran ainsi que votre décalage (tampon) que vous pouvez définir. Je peux vraiment dire que je l'utilise partout dans mes applications. Une amélioration que vous pourriez faire est de distinguer HStack et LazyHStack entre iOS 14 et les versions antérieures. Faites-moi savoir si vous avez besoin d'aide!
Merci pour votre réponse! Cela signifie-t-il que LazyHStack ne réutilise pas les cellules? Je me demande pourquoi Apple a fait de cette façon. Étant donné que List réutilise des cellules, pourquoi les LazyHStack / LazyHGrids ne réutilisent-ils pas les cellules?
LazyHStack est un pur composant SwiftUI, donc il réutilisera les vues si l'id ou l'équatableview lui dira que c'est le même. Il le redessine autrement. Listez-le en haut de Table pour iOS. c'est pourquoi il les réutilise.
L'allocation de ce nombre d'éléments d'interface utilisateur consomme beaucoup de mémoire. Dans ce cas, je vous suggère d'utiliser UITableView.
Le changement de la vue de défilement en vue de liste fera-t-il une différence?