在 ForEach SwiftUI 中使用 Array 本身以编程方式更改 TabView 的选择
Change selection of TabView programatically using Array itself in ForEach SwiftUI
我有一个 TabView,其中有一个用于其中各种项目的 ForEach 循环,我想使用一个自动更改选择索引的计时器来更改 TabView 项目的选择。我可以使用具有 array.indices
或 0..<array.count
的 ForEach 循环来实现它,但是由于使用前面提到的方法是不安全的,所以我想将数组本身简单地传递到 ForEach 循环中,但是虽然简单在 foreach 循环中使用数组不会使用计时器或其他编程方式更改所选索引。用户需要手动滚动以更改选择。下面是我试过的代码行,谁能指出我做错了什么。
struct ContentView: View {
@State var currentIndex = 0
@State var itemNames = [ItemNames]()
@State var timer: Timer.TimerPublisher = Timer.publish (every: 6, on: .main, in: .common)
var body: some View {
TabView(selection: $currentIndex) {
ForEach(itemNames, id: \.id) { item in
Text(item.name).tag(Int(item.id))
}
}.tabViewStyle(.page(indexDisplayMode: .never))
.onReceive(timer, perform: {
_ in withAnimation {
currentIndex = currentIndex < itemNames.count ? currentIndex + 1 : 0
}
}).onDisappear {
self.cancelTimer()
}.onAppear {
setDemoNames()
self.instantiateTimer()
_ = self.timer.connect()
}
}
func instantiateTimer() {
self.timer = Timer.publish (every: 6, on: .main, in: .common)
return
}
func cancelTimer() {
self.timer.connect().cancel()
return
}
func setDemoNames(){
let item1 = ItemNames(id: "1", name: "John")
let item2 = ItemNames(id: "2", name: "Mark")
let item3 = ItemNames(id: "3", name: "Steve")
let item4 = ItemNames(id: "4", name: "Peter")
itemNames = [item1, item2, item3, item4]
}
}
struct ItemNames : Identifiable, Hashable {
var id:String, name:String;
init(id: String, name: String) {
self.id = id
self.name = name
}
}
类型必须完全匹配
将您的标签更改为
.tag(Int(item.id) as! Int)
以上行有效,因为它删除了 optional/forces.
您必须确保 item.id
始终有效 Int
Int(item.id)
生成类型 Int?
的值,因为它可能会失败
你也可以像下面这样不强制执行,但如果由于某种原因你有 2 个失败的 ID,你可能会有重复的标签。
.tag((Int(item.id) ?? itemNames.count + 1) as Int)
这很容易出现错误,它只在非常特定的情况下有效,而且几乎没有后备方案。
我会将选择变量切换为 String
并将 tag
保留为原始 id
然后创建一个函数来拉下一个 id
以使用定时器。
没有强制或可选。
您还可以通过使用 id
和 Int
而不是 String
来实现此目的
我有一个 TabView,其中有一个用于其中各种项目的 ForEach 循环,我想使用一个自动更改选择索引的计时器来更改 TabView 项目的选择。我可以使用具有 array.indices
或 0..<array.count
的 ForEach 循环来实现它,但是由于使用前面提到的方法是不安全的,所以我想将数组本身简单地传递到 ForEach 循环中,但是虽然简单在 foreach 循环中使用数组不会使用计时器或其他编程方式更改所选索引。用户需要手动滚动以更改选择。下面是我试过的代码行,谁能指出我做错了什么。
struct ContentView: View {
@State var currentIndex = 0
@State var itemNames = [ItemNames]()
@State var timer: Timer.TimerPublisher = Timer.publish (every: 6, on: .main, in: .common)
var body: some View {
TabView(selection: $currentIndex) {
ForEach(itemNames, id: \.id) { item in
Text(item.name).tag(Int(item.id))
}
}.tabViewStyle(.page(indexDisplayMode: .never))
.onReceive(timer, perform: {
_ in withAnimation {
currentIndex = currentIndex < itemNames.count ? currentIndex + 1 : 0
}
}).onDisappear {
self.cancelTimer()
}.onAppear {
setDemoNames()
self.instantiateTimer()
_ = self.timer.connect()
}
}
func instantiateTimer() {
self.timer = Timer.publish (every: 6, on: .main, in: .common)
return
}
func cancelTimer() {
self.timer.connect().cancel()
return
}
func setDemoNames(){
let item1 = ItemNames(id: "1", name: "John")
let item2 = ItemNames(id: "2", name: "Mark")
let item3 = ItemNames(id: "3", name: "Steve")
let item4 = ItemNames(id: "4", name: "Peter")
itemNames = [item1, item2, item3, item4]
}
}
struct ItemNames : Identifiable, Hashable {
var id:String, name:String;
init(id: String, name: String) {
self.id = id
self.name = name
}
}
类型必须完全匹配
将您的标签更改为
.tag(Int(item.id) as! Int)
以上行有效,因为它删除了 optional/forces.
您必须确保 item.id
始终有效 Int
Int(item.id)
生成类型 Int?
的值,因为它可能会失败
你也可以像下面这样不强制执行,但如果由于某种原因你有 2 个失败的 ID,你可能会有重复的标签。
.tag((Int(item.id) ?? itemNames.count + 1) as Int)
这很容易出现错误,它只在非常特定的情况下有效,而且几乎没有后备方案。
我会将选择变量切换为 String
并将 tag
保留为原始 id
然后创建一个函数来拉下一个 id
以使用定时器。
没有强制或可选。
您还可以通过使用 id
和 Int
而不是 String