SwiftUI 选择器在 ObservedObject 公共变量更新期间滚动时跳转
Posted
技术标签:
【中文标题】SwiftUI 选择器在 ObservedObject 公共变量更新期间滚动时跳转【英文标题】:SwiftUI picker jumps while scrolling during ObservedObject public var update 【发布时间】:2020-06-10 19:30:19 【问题描述】:目前正在创建一个 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[$0]))")
...
TimerManager 管理一个计时器。 班级:
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 访问计时器类的 secondsLeft 变量来显示剩余时间。当计时器运行时,secondsLeft 变量会每秒更新一次,并且 ContentView 会重新呈现。 问题是,当计时器运行时,我不能“轻弹”我的选择器,它总是重置,这里也指出:https://forums.developer.apple.com/thread/127218。但与那篇文章不同的是,选择器的“选择”变量对问题没有任何影响。如果我从 secondsLeft 变量中删除 @Public,一切正常(问题是我无法显示剩余时间)。
有人知道如何解决这个问题吗?
感谢您的回答!
【问题讨论】:
也许环境对象会有所帮助?我们需要更多上下文,如果您可以创建一个可重现的示例,这将很有帮助:***.com/help/minimal-reproducible-example 谢谢!我刚刚添加了一些代码。 太好了,我能够使用您的代码编写一个简单的演示;但是,当我轻弹选择器时,它不会重置计时器,而是计时器标签“冻结”,直到我松开选择器。这是你的问题吗?还是我错过了什么? 只是为了确定:“计时器标签”是什么意思?选择器? 不,我创建了一个Text("Time remaining: \(self.timerManager.secondsLeft)")
来显示剩余时间,它会倒计时。
【参考方案1】:
这个问题可以通过自定义 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 公共变量更新期间滚动时跳转的主要内容,如果未能解决你的问题,请参考以下文章
基本的 SwiftUI 选择器在屏幕变化时发出严重警告和奇怪的动作