Пытаюсь понять, как .id(_:) работает в SwiftUI

#ios #swift #swiftui

Вопрос:

Я пытаюсь использовать .id(_:) программную прокрутку с помощью ForEach, чтобы форма прокручивалась каждый раз, когда в нее добавляется новый раздел с помощью ForEach. Тем не менее, я получаю некоторое нежелательное поведение, и мне хотелось бы понять механику того, что вызывает указанное поведение. Как вы можете видеть на скриншоте, третье текстовое представление даже не отображается. Когда я удаляю id метод, все три вида текста отображаются так, как вы и ожидали.

 import SwiftUI  struct ContentView: View {  @State private var array: [Int] = [1]  var body: some View {  ScrollViewReader { p in  NavigationView {  Form {  ForEach(self.array, id: .self) { num in  Section {  Text("Hello")  Text("My name is slim shady")  Text("number (num)")  }  .id(num)  .onChange(of: array.count) { count in  p.scrollTo((count   1), anchor: .top)  }  }  }  .toolbar {  ToolbarItem(placement: .bottomBar) {  Button("add") {  array.append(array.count   1)  }  }  }  }  }  } }  

Любая помощь будет очень признательна! Ваше здоровье.

Скриншот симулятора

Ответ №1:

У вас здесь происходит пара проблем. Я никогда не пытался поместить a Section внутри a ForEach , как это, но они, очевидно, плохо играют. Я удалил его и использовал VStack вместо этого. Я не уверен, сможете ли вы создать такие новые разделы, но это еще один вопрос на другой день.

Проблема, с которой вы ScrollTo столкнулись, — это непонимание того, что id вы на самом деле использовали. Когда вы создаете экземпляр a ForEach с массивом, который иначе не идентифицируется, вы используете .self в качестве id . Однако это означает, что вы отслеживаете сами элементы, а не содержимое каждого элемента. Итак, когда вы попытались вставить an int в свой scrollTo() , ничего не произошло, потому что an int не является элементом вашего массива. Что вам нужно сделать, так это предоставить такой элемент, как этот:

 struct ContentView: View {  @State private var array: [Int] = [1]  var body: some View {  ScrollViewReader { p in  NavigationView {  Form {  ForEach(self.array, id: .self) { num in  VStack(alignment: .leading) {  Text("Hello")  Text("My name is slim shady")  Text("number (num)")  }  // num is an element of your array, not an int  .id(num)  .onChange(of: array.count) { count in  // Supply scrollTo with an array element here  p.scrollTo(array.last, anchor: .top)  }  }  }  .toolbar {  ToolbarItem(placement: .bottomBar) {  Button("add") {  array.append(array.count   1)  }  }  }  }  }  } }  

Комментарии:

1. Я ценю ответ Yrb. Я потратил много времени, обдумывая ваш комментарий… На самом деле я не думаю, что то, о чем вы упомянули, было проблемой. Я снова посмотрел на свой код и допустил небольшую математическую ошибку с аргументом, в который я ввел scrollTo ; это должен был быть просто счет. После того, как я это исправил, он начал работать, хотя, как вы упомянули, создание разделов внутри по ForEach -прежнему вызывает проблему. Еще раз спасибо за ответ.

Ответ №2:

На всякий случай, если кто-нибудь случайно прочтет это, после множества экспериментов я смог заставить это работать. Похоже , что проблема, которую я опубликовал на своих скриншотах, была вызвана созданием разделов внутри ForEach , как уже упоминалось, Yrb. Тем не менее, я попробовал другой инициализатор для создания разделов, и, похоже, он работает до тех пор, пока вы предоставляете представление для заголовка раздела. Я понятия не имею, почему, однако, если у кого-то есть какие-либо идеи, пожалуйста, поделитесь ими.

 struct ContentView: View {  @State private var array: [Int] = [1]  var body: some View {  ScrollViewReader { p in  NavigationView {  Form {  ForEach(self.array, id: Int.self) { num in  Section {  TextVeiwshg(num: num)  } header: {  Text("section #(num)")  }  .id(num)  }  }  .toolbar {  ToolbarItem(placement: .bottomBar) {  Button("add") {  array.append(array.count   1)  print(array)  }  }  }  }  .onChange(of: array.count) { count in  print(array.count)  withAnimation {  p.scrollTo((count), anchor: .top)  }  print("scrolling to (count)")  }  }  } }