SwiftUI 选择器在 ObservedObject public var update 期间滚动时跳转
SwiftUI picker jumps while scrolling during ObservedObject public var update
目前正在创建一个 SwiftUI 应用程序,我在尝试解决 ObservedObject 和选择器的问题时遇到了困难。
我的内容视图:
struct ContentView: View {
@ObservedObject var timerManager = TimerManager()
...
var body: some View{
Circle()
.onTapGesture(perform: {
self.timerManager.setTimerLength(seconds: self.settings.selectedSecondPickerIndex, minutes: self.settings.selectedMinutePickerIndex)
self.timerManager.start()
})
Picker(selection: self.$settings.selectedSecondPickerIndex, label: Text("")) {
ForEach(0 ..< 60) {
Text("\(secondsToString(seconds: self.availableTimeInterval[[=11=]]))")
}
}
}
...
}
TimerManager 管理一个定时器。
class:
class TimerManager: ObservableObject {
@Published var secondsLeft : Int = -1
var secondsTotal : Int = -1
var timer = Timer()
func start() {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
if secondsLeft == 0 {
self.secondsLeft = secondsTotal
timer.invalidate()
}
else{
self.secondsLeft -= 1
}
}
RunLoop.current.add(timer, forMode: RunLoop.Mode.common)
}
func setTimerLength(seconds: Int, minutes: Int) {
let totalSeconds: Int = seconds + minutes * 60
secondsTotal = totalSeconds
secondsLeft = totalSeconds
}
...
}
我需要从 ContentView 访问计时器 class 的 secondsLeft 变量以显示剩余时间。当计时器运行时,secondsLeft 变量每秒更新一次,并且 ContentView 被重新渲染。
问题是,虽然计时器是 运行,但我不能 "flick" 我的选择器,它总是重置,也在这里指出:https://forums.developer.apple.com/thread/127218。但与 post 不同的是,选择器的 "selection" 变量对问题没有任何影响。如果我从 secondsLeft 变量中删除 @Public,一切正常(问题是我无法显示剩余时间)。
有人知道如何解决这个问题吗?
感谢您的回答!
可以使用自定义 UIPickerView 解决此问题:
import SwiftUI
struct ContentView: View {
@State var selection = 0
var body: some View {
VStack {
FixedPicker(selection: $selection, rowCount: 10) { row in
"\(row)"
}
}
}
}
struct FixedPicker: UIViewRepresentable {
class Coordinator : NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
@Binding var selection: Int
var initialSelection: Int?
var titleForRow: (Int) -> String
var rowCount: Int
func numberOfComponents(in pickerView: UIPickerView) -> Int {
1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
rowCount
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
titleForRow(row)
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
self.selection = row
}
init(selection: Binding<Int>, titleForRow: @escaping (Int) -> String, rowCount: Int) {
self.titleForRow = titleForRow
self._selection = selection
self.rowCount = rowCount
}
}
@Binding var selection: Int
var rowCount: Int
let titleForRow: (Int) -> String
func makeCoordinator() -> FixedPicker.Coordinator {
return Coordinator(selection: $selection, titleForRow: titleForRow, rowCount: rowCount)
}
func makeUIView(context: UIViewRepresentableContext<FixedPicker>) -> UIPickerView {
let view = UIPickerView()
view.delegate = context.coordinator
view.dataSource = context.coordinator
return view
}
func updateUIView(_ uiView: UIPickerView, context: UIViewRepresentableContext<FixedPicker>) {
context.coordinator.titleForRow = self.titleForRow
context.coordinator.rowCount = rowCount
//only update selection if it has been changed
if context.coordinator.initialSelection != selection {
uiView.selectRow(selection, inComponent: 0, animated: true)
context.coordinator.initialSelection = selection
}
}
}
我在这里找到了这个解决方案:
https://mbishop.name/2019/11/odd-behaviors-in-the-swiftui-picker-view/
希望对遇到同样问题的大家有所帮助!
目前正在创建一个 SwiftUI 应用程序,我在尝试解决 ObservedObject 和选择器的问题时遇到了困难。 我的内容视图:
struct ContentView: View {
@ObservedObject var timerManager = TimerManager()
...
var body: some View{
Circle()
.onTapGesture(perform: {
self.timerManager.setTimerLength(seconds: self.settings.selectedSecondPickerIndex, minutes: self.settings.selectedMinutePickerIndex)
self.timerManager.start()
})
Picker(selection: self.$settings.selectedSecondPickerIndex, label: Text("")) {
ForEach(0 ..< 60) {
Text("\(secondsToString(seconds: self.availableTimeInterval[[=11=]]))")
}
}
}
...
}
TimerManager 管理一个定时器。 class:
class TimerManager: ObservableObject {
@Published var secondsLeft : Int = -1
var secondsTotal : Int = -1
var timer = Timer()
func start() {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
if secondsLeft == 0 {
self.secondsLeft = secondsTotal
timer.invalidate()
}
else{
self.secondsLeft -= 1
}
}
RunLoop.current.add(timer, forMode: RunLoop.Mode.common)
}
func setTimerLength(seconds: Int, minutes: Int) {
let totalSeconds: Int = seconds + minutes * 60
secondsTotal = totalSeconds
secondsLeft = totalSeconds
}
...
}
我需要从 ContentView 访问计时器 class 的 secondsLeft 变量以显示剩余时间。当计时器运行时,secondsLeft 变量每秒更新一次,并且 ContentView 被重新渲染。 问题是,虽然计时器是 运行,但我不能 "flick" 我的选择器,它总是重置,也在这里指出:https://forums.developer.apple.com/thread/127218。但与 post 不同的是,选择器的 "selection" 变量对问题没有任何影响。如果我从 secondsLeft 变量中删除 @Public,一切正常(问题是我无法显示剩余时间)。
有人知道如何解决这个问题吗?
感谢您的回答!
可以使用自定义 UIPickerView 解决此问题:
import SwiftUI
struct ContentView: View {
@State var selection = 0
var body: some View {
VStack {
FixedPicker(selection: $selection, rowCount: 10) { row in
"\(row)"
}
}
}
}
struct FixedPicker: UIViewRepresentable {
class Coordinator : NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
@Binding var selection: Int
var initialSelection: Int?
var titleForRow: (Int) -> String
var rowCount: Int
func numberOfComponents(in pickerView: UIPickerView) -> Int {
1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
rowCount
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
titleForRow(row)
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
self.selection = row
}
init(selection: Binding<Int>, titleForRow: @escaping (Int) -> String, rowCount: Int) {
self.titleForRow = titleForRow
self._selection = selection
self.rowCount = rowCount
}
}
@Binding var selection: Int
var rowCount: Int
let titleForRow: (Int) -> String
func makeCoordinator() -> FixedPicker.Coordinator {
return Coordinator(selection: $selection, titleForRow: titleForRow, rowCount: rowCount)
}
func makeUIView(context: UIViewRepresentableContext<FixedPicker>) -> UIPickerView {
let view = UIPickerView()
view.delegate = context.coordinator
view.dataSource = context.coordinator
return view
}
func updateUIView(_ uiView: UIPickerView, context: UIViewRepresentableContext<FixedPicker>) {
context.coordinator.titleForRow = self.titleForRow
context.coordinator.rowCount = rowCount
//only update selection if it has been changed
if context.coordinator.initialSelection != selection {
uiView.selectRow(selection, inComponent: 0, animated: true)
context.coordinator.initialSelection = selection
}
}
}
我在这里找到了这个解决方案: https://mbishop.name/2019/11/odd-behaviors-in-the-swiftui-picker-view/
希望对遇到同样问题的大家有所帮助!