TL; DR:
Применение визуальных эффектов к содержимому ScrollView
вызывает тысячи запросов на одно и то же (неизменное) изображение для каждого жеста перетаскивания. Могу я уменьшить это? (В моем реальном приложении у меня 50 с лишним изображений в представлении, и, соответственно, прокрутка медленная.)
Суть
Чтобы немного оживить прокрутку HStack
изображений, я применил несколько преобразований для эффекта круговой «карусели». (Подсказки к образцу кода от Джона М. и Пол Хадсон)
Код можно запускать с копированием и вставкой, как указано. (Вам необходимо предоставить изображение.) Без двух строк, отмеченных /* 1 */
и /* 2 */
, объект Slide
сообщает о шести запросах изображения, независимо от того, сколько вы перетаскиваете и прокручиваете. Включите две строки и посмотрите, как количество запросов увеличивается до 1000 одним движением пальца.
Примечания
SwiftUI основан на недорогом повторном рисовании легковесных Views
в зависимости от текущего состояния. Неосторожное управление зависимостью состояний может привести к неправильному отключению частей дерева представления. И в этом случае постоянное вращение и масштабирование при прокрутке заставляет среду выполнения повторно отображать контент.
Но ... обязательно ли это требует постоянного повторного извлечения статических изображений? Случайное перетаскивание мизинца вперед и назад вызовет десятки тысяч запросов изображения. Это кажется чрезмерным. Есть ли способ уменьшить накладные расходы в этом примере?
Конечно, это примитивный дизайн, который постоянно выкладывает все свое содержимое, вместо того, чтобы использовать подход повторного использования ячеек, как, например, UITableView
. Можно подумать о применении преобразований только к трем видимым в данный момент представлениям. В Интернете есть некоторая дискуссия об этом, но в мои попытки, компилятор не смог сделать вывод типа.
Код
import SwiftUI
// Comment out lines marked 1 & 2 and watch the request count go down.
struct ContentView: View {
var body: some View {
GeometryReader { outerGeo in
ScrollView(.horizontal, showsIndicators: false) {
HStack {
ForEach(Slide.all) { slide in
GeometryReader { innerGeo in
Image(uiImage: slide.image).resizable().scaledToFit()
/* 1 */ .rotation3DEffect(.degrees(Double(innerGeo.localOffset(in: outerGeo).width) / 10), axis: (x: 0, y: 1, z: 0))
/* 2 */ .scaleEffect(1.0 - abs(innerGeo.localOffset(in: outerGeo).width) / 800.0)
}
.frame(width:200)
}
}
}
}
.clipped()
.border(Color.red, width: 4)
.frame(width: 400, height: 200)
}
}
// Provides images for the ScrollView. Tracks and reports image requests.
struct Slide : Identifiable {
let id: Int
static let all = (1...6).map(Self.init)
static var requestCount = 0
var image: UIImage {
Self.requestCount += 1
print("Request # \(Self.requestCount)")
return UIImage(named: "blueSquare")! // Or whatever image
}
}
// Handy extension for finding local coords.
extension GeometryProxy {
func localOffset(in outerGeo: GeometryProxy) -> CGSize {
let innerFrame = self.frame(in: .global)
let outerFrame = outerGeo.frame(in: .global)
return CGSize(
width : innerFrame.midX - outerFrame.midX,
height: innerFrame.midY - outerFrame.midY
)
}
}