Бесконечная прокрутка в SwiftUI - PullRequest
0 голосов
/ 06 февраля 2020

Хотя SwiftUI предлагает множество функций для создания пользовательского интерфейса, я обнаружил, что сам что-то упустил. Я пытаюсь отобразить список с информацией календаря, один элемент pr. день. Я хочу прокрутить это бегло и по характеру календаря, нет определенного c начала или конца списка, поэтому стандартный ScrollView или Список не подходит для этой задачи.

Я имею поэтому попытался собрать пользовательский элемент управления вместе. Мой подход состоит в том, чтобы иметь на 2 пункта больше, чем нужно, изначально расположенных вне экрана (сверху и снизу). Все элементы имеют одинаковую высоту, чтобы сделать макет простым. Я прокручиваю, смещая элементы. Как только смещение превышает высоту элемента, я сдвигаю содержимое на 1 и переставляю элементы, чтобы снова разместить первый и последний элемент за пределами экрана.

Этот метод работает довольно хорошо, пока представления элемента просты. Когда отдельные элементы становятся сложными, я вижу эффект заикания, когда перетаскиваю их вверх и вниз. Я подозреваю, что задержка, вызывающая заикание, происходит из-за регенерируемого изображения.

Является ли этот подход хорошим, и если да, что я могу сделать, чтобы оптимизировать его, чтобы уменьшить / устранить эффект заикания? Если нет, есть ли альтернативные идеи для решения?

Я включил здесь грубую версию исходного кода, чтобы дать представление о дизайне:

struct InfinityScroller<Item:View> : View
{
  @State private  var offset     : CGFloat = 0
  @State private  var itemIndex  : Int     = 0
  @State private  var itemOffset : CGFloat

  private let itemHeight     : CGFloat
  private let zero           : CGFloat
  private let item           : (Int,CGFloat,CGFloat) -> Item
  private let itemCount      : Int = 7
  private let offScreenCount : Int = 1

  init(location:ObservedObject<InfinityLocation>,zero:CGFloat,_ height:CGFloat=121,@ViewBuilder item : @escaping (Int,CGFloat,CGFloat) -> Item)
  {
    self._location   = location
    self.zero        = zero
    self.itemHeight  = height
    self.item        = item
    self._itemOffset = State(initialValue: zero)
  }

  var body : some View
  {
    GeometryReader
    {
      g in
      ZStack(alignment:.top)
      {
        Color.clear.frame(width:0)
        Color.clear.frame(height:0)
        self.makeItems(g.size.width)
      }
      .contentShape(Rectangle())
      .gesture(DragGesture(minimumDistance: 0)
        .onChanged
        { g in
          self.updatePosition(g.translation.height)
        }
        .onEnded
        { g in
          self.offset += g.translation.height
          self.updatePosition()
        }
      )
    }
  }

  func updatePosition(_ scroll:CGFloat=0)
  {
    let off = offset + scroll + zero
    itemIndex  = -Int((off/itemHeight).rounded(.down))
    itemOffset = off.mod(itemHeight)
  }

  func makeItems(_ w:CGFloat) -> some View
  {
    return ForEach (-offScreenCount...itemCount+offScreenCount,id:\.self)
    { i in
      self.item(i+self.itemIndex,w,self.itemHeight)
        .position(x:0,y:CGFloat(i) * self.itemHeight + self.itemOffset)
        .offset(x:w/2,y:self.itemHeight/2)
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...