在 SwiftUI 中添加元素时如何获取列表的偏移量
How to get the List's offset when adding an element in SwiftUI
我想获取列表的偏移量来实现分页。所以,我写了如下。
- ContentView.swift
import SwiftUI
struct ContentView: View {
@State var list = ["item1" ,"item2" ,"item3", "item4" ,"item5" ,"item6" ,"item7" ,"item8" ,"item9" ,"item10" ,"item11" ,"item12"]
@State private var offset: CGFloat = 0
var body: some View {
NavigationView {
GeometryReader { outsideProxy in
List {
ForEach(list, id: \.self) { item in
ZStack {
GeometryReader { insideProxy in
Color.clear
.preference(key: ScrollOffsetPreferenceKey.self, value: [outsideProxy.frame(in: .global).minY - insideProxy.frame(in: .global).minY])
}
NavigationLink("\(item), \(self.offset)") {
Text("\(item), \(self.offset)")
}
.frame(height: 100)
}
.listRowInsets(EdgeInsets(top: 5, leading: 0, bottom: 5, trailing: 3))
}
}
.onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in
self.offset = value[0]
}
.listStyle(PlainListStyle())
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(Text("title"))
.toolbar(content: {
ToolbarItem(placement: .bottomBar) {
HStack {
Button("Add") {
list.insert("hello_\(Date.now.description)", at: 0)
}
Spacer()
Text("\(offset)")
}
}
})
}
}
}
}
struct ScrollOffsetPreferenceKey: PreferenceKey {
typealias Value = [CGFloat]
static var defaultValue: [CGFloat] = [0]
static func reduce(value: inout [CGFloat], nextValue: () -> [CGFloat]) {
value.append(contentsOf: nextValue())
}
}
但是,当我按“添加”按钮添加元素时,offset
值发生了变化。
我不希望 offset
更新,因为我想在列表顶部进一步向上滚动时添加元素。
谁能告诉我如何添加元素而不移动 offset
?
谢谢,
增加220419
我参考
写了下面的代码
- List 和 ScrollView(仅主体)
var body: some View {
NavigationView {
List {
ScrollView {
VStack {
ForEach(list, id: \.self) { item in
NavigationLink("\(item), \(self.offset)") {
Text("hello\(item), \(self.offset)")
}
.frame(height: 100)
.onAppear {
print("appear: \(item)")
print(offset)
}
.listRowInsets(EdgeInsets(top: 5, leading: 0, bottom: 5, trailing: 3))
}
}
.background(GeometryReader {
Color.clear.preference(key: ViewOffsetKey.self, value: -[=11=].frame(in: .named("scroll")).origin.y)
})
.onPreferenceChange(ViewOffsetKey.self) {
self.offset = [=11=]
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(Text("title"))
.toolbar(content: {
ToolbarItem(placement: .bottomBar) {
HStack {
Text("\(thre)")
Spacer()
Button("Add") {
list.insert("hello_\(Date.now.description)", at: 0)
}
Spacer()
Text("\(offset)")
}
}
})
}
.coordinateSpace(name: "scroll")
.listStyle(PlainListStyle())
}
}
- 仅列出(不工作)
var body: some View {
NavigationView {
List {
ForEach(list, id: \.self) { item in
NavigationLink("\(item), \(self.offset)") {
Text("hello\(item), \(self.offset)")
}
.frame(height: 100)
.onAppear {
print("appear: \(item)")
print(offset)
}
.listRowInsets(EdgeInsets(top: 5, leading: 0, bottom: 5, trailing: 3))
}
.background(GeometryReader {
Color.clear.preference(key: ViewOffsetKey.self, value: -[=12=].frame(in: .named("scroll")).origin.y)
})
.onPreferenceChange(ViewOffsetKey.self) {
self.offset = [=12=]
}
}
.coordinateSpace(name: "scroll")
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(Text("title"))
.toolbar(content: {
ToolbarItem(placement: .bottomBar) {
HStack {
Text("\(thre)")
Spacer()
Button("Add") {
list.insert("hello_\(Date.now.description)", at: 0)
}
Spacer()
Text("\(offset)")
}
}
})
.listStyle(PlainListStyle())
.onChange(of: offset) { newValue in
if lock { return }
self.lock = true
if newValue < -105 {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
list.insert("hello_\(Date.now.description)", at: 0)
self.thre = newValue
self.lock = false
}
} else {
self.lock = false
}
}
}
}
最后还是用了Asperi的idea。如下所示。
var body: some View {
NavigationView {
ScrollView {
LazyVStack {
ForEach(list, id: \.self) { item in
NavigationLink("\(item), \(self.offset)") {
Text("hello\(item), \(self.offset)")
}
.frame(height: 100)
.onAppear {
print("appear: \(item)")
print(offset)
}
.listRowInsets(EdgeInsets(top: 5, leading: 0, bottom: 5, trailing: 3))
}
}
.background(GeometryReader {
Color.clear.preference(key: ViewOffsetKey.self, value: -[=10=].frame(in: .named("scroll")).origin.y)
})
.onPreferenceChange(ViewOffsetKey.self) {
self.offset = [=10=]
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(Text("title"))
.toolbar(content: {
ToolbarItem(placement: .bottomBar) {
HStack {
Text("\(thre)")
Spacer()
Button("Add") {
list.insert("hello_\(Date.now.description)", at: 0)
}
Spacer()
Text("\(offset)")
}
}
})
.coordinateSpace(name: "scroll")
.listStyle(PlainListStyle())
}
}
我想获取列表的偏移量来实现分页。所以,我写了如下。
- ContentView.swift
import SwiftUI
struct ContentView: View {
@State var list = ["item1" ,"item2" ,"item3", "item4" ,"item5" ,"item6" ,"item7" ,"item8" ,"item9" ,"item10" ,"item11" ,"item12"]
@State private var offset: CGFloat = 0
var body: some View {
NavigationView {
GeometryReader { outsideProxy in
List {
ForEach(list, id: \.self) { item in
ZStack {
GeometryReader { insideProxy in
Color.clear
.preference(key: ScrollOffsetPreferenceKey.self, value: [outsideProxy.frame(in: .global).minY - insideProxy.frame(in: .global).minY])
}
NavigationLink("\(item), \(self.offset)") {
Text("\(item), \(self.offset)")
}
.frame(height: 100)
}
.listRowInsets(EdgeInsets(top: 5, leading: 0, bottom: 5, trailing: 3))
}
}
.onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in
self.offset = value[0]
}
.listStyle(PlainListStyle())
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(Text("title"))
.toolbar(content: {
ToolbarItem(placement: .bottomBar) {
HStack {
Button("Add") {
list.insert("hello_\(Date.now.description)", at: 0)
}
Spacer()
Text("\(offset)")
}
}
})
}
}
}
}
struct ScrollOffsetPreferenceKey: PreferenceKey {
typealias Value = [CGFloat]
static var defaultValue: [CGFloat] = [0]
static func reduce(value: inout [CGFloat], nextValue: () -> [CGFloat]) {
value.append(contentsOf: nextValue())
}
}
但是,当我按“添加”按钮添加元素时,offset
值发生了变化。
我不希望 offset
更新,因为我想在列表顶部进一步向上滚动时添加元素。
谁能告诉我如何添加元素而不移动 offset
?
谢谢,
增加220419
我参考
- List 和 ScrollView(仅主体)
var body: some View {
NavigationView {
List {
ScrollView {
VStack {
ForEach(list, id: \.self) { item in
NavigationLink("\(item), \(self.offset)") {
Text("hello\(item), \(self.offset)")
}
.frame(height: 100)
.onAppear {
print("appear: \(item)")
print(offset)
}
.listRowInsets(EdgeInsets(top: 5, leading: 0, bottom: 5, trailing: 3))
}
}
.background(GeometryReader {
Color.clear.preference(key: ViewOffsetKey.self, value: -[=11=].frame(in: .named("scroll")).origin.y)
})
.onPreferenceChange(ViewOffsetKey.self) {
self.offset = [=11=]
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(Text("title"))
.toolbar(content: {
ToolbarItem(placement: .bottomBar) {
HStack {
Text("\(thre)")
Spacer()
Button("Add") {
list.insert("hello_\(Date.now.description)", at: 0)
}
Spacer()
Text("\(offset)")
}
}
})
}
.coordinateSpace(name: "scroll")
.listStyle(PlainListStyle())
}
}
- 仅列出(不工作)
var body: some View {
NavigationView {
List {
ForEach(list, id: \.self) { item in
NavigationLink("\(item), \(self.offset)") {
Text("hello\(item), \(self.offset)")
}
.frame(height: 100)
.onAppear {
print("appear: \(item)")
print(offset)
}
.listRowInsets(EdgeInsets(top: 5, leading: 0, bottom: 5, trailing: 3))
}
.background(GeometryReader {
Color.clear.preference(key: ViewOffsetKey.self, value: -[=12=].frame(in: .named("scroll")).origin.y)
})
.onPreferenceChange(ViewOffsetKey.self) {
self.offset = [=12=]
}
}
.coordinateSpace(name: "scroll")
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(Text("title"))
.toolbar(content: {
ToolbarItem(placement: .bottomBar) {
HStack {
Text("\(thre)")
Spacer()
Button("Add") {
list.insert("hello_\(Date.now.description)", at: 0)
}
Spacer()
Text("\(offset)")
}
}
})
.listStyle(PlainListStyle())
.onChange(of: offset) { newValue in
if lock { return }
self.lock = true
if newValue < -105 {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
list.insert("hello_\(Date.now.description)", at: 0)
self.thre = newValue
self.lock = false
}
} else {
self.lock = false
}
}
}
}
最后还是用了Asperi的idea。如下所示。
var body: some View {
NavigationView {
ScrollView {
LazyVStack {
ForEach(list, id: \.self) { item in
NavigationLink("\(item), \(self.offset)") {
Text("hello\(item), \(self.offset)")
}
.frame(height: 100)
.onAppear {
print("appear: \(item)")
print(offset)
}
.listRowInsets(EdgeInsets(top: 5, leading: 0, bottom: 5, trailing: 3))
}
}
.background(GeometryReader {
Color.clear.preference(key: ViewOffsetKey.self, value: -[=10=].frame(in: .named("scroll")).origin.y)
})
.onPreferenceChange(ViewOffsetKey.self) {
self.offset = [=10=]
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(Text("title"))
.toolbar(content: {
ToolbarItem(placement: .bottomBar) {
HStack {
Text("\(thre)")
Spacer()
Button("Add") {
list.insert("hello_\(Date.now.description)", at: 0)
}
Spacer()
Text("\(offset)")
}
}
})
.coordinateSpace(name: "scroll")
.listStyle(PlainListStyle())
}
}