Хотя 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)
}
}
}