goh*_*tis 6 picker ios swift swiftui
我Picker
在 SwiftUI 中有一个Form
,并且我正在尝试在选择器更改时处理该值。我希望能够对didSet
代表当前选定值的变量执行此操作。
import SwiftUI
struct ContentView: View {
enum TransmissionType: Int {
case automatic
case manual
}
@State private var selectedTransmissionType: Int = TransmissionType.automatic.rawValue {
didSet {
print("selection changed to \(selectedTransmissionType)")
}
}
private let transmissionTypes: [String] = ["Automatic", "Manual"]
var body: some View {
NavigationView {
Form {
Section {
Picker(selection: $selectedTransmissionType,
label: Text("Transmission Type")) {
ForEach(0 ..< transmissionTypes.count) {
Text(self.transmissionTypes[$0])
}
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
该Picker
UI作品(大部分)如期望的那样,我可以看到默认值被选中,点击进入选择器,它会打开一个新的观点,我可以选择其它的值,然后返回到主窗体和新节目我值被选中。但是,didSet
永远不会被调用。
我看到了这个问题,但在我的View
代码中添加更多内容而不是仅在变量更改时处理新值(如果可能的话)似乎很奇怪。onReceive
即使它导致更复杂的视图,使用它是否更好?我的主要问题是:我的代码有什么问题以防止didSet
被调用?
我用这个例子来达到这一点。
除了我的常规问题之外,关于这个例子,我还有其他一些问题:
A)我用 anenum
和 anArray
来表示相同的两个值似乎很奇怪。有人还可以建议一种更好的方法来构建它以避免这种冗余吗?我考虑过一个TransmissionType
对象,但与enum
……相比,这似乎有点过分了……也许不是?
B)点击选择器时,选择器选项的屏幕滑过,然后两个选项跳起来一点。这感觉刺耳和神经质,以及糟糕的用户体验。我在这里做错了什么导致糟糕的用户体验吗?或者它可能是一个 SwiftUI 错误?每次更改选择器时都会收到此错误:
[TableView] Warning once only: UITableView was told to layout its visible cells and other contents without being in the view hierarchy (the table view or one of its superviews has not been added to a window). This may cause bugs by forcing views inside the table view to load and perform layout without accurate information (e.g. table view bounds, trait collection, layout margins, safe area insets, etc), and will also cause unnecessary performance overhead due to extra layout passes. Make a symbolic breakpoint at UITableViewAlertForLayoutOutsideViewHierarchy to catch this in the debugger and see what caused this to occur, so you can avoid this action altogether if possible, or defer it until the table view has been added to a window.
我早些时候开始打字,然后回来发现 LuLuGaGa 打败了我。:D 但既然我有这个...
主要问题:来自Swift 语言指南:
“当您为存储的属性分配默认值,或在初始化程序中设置其初始值时,该属性的值将直接设置,无需调用任何属性观察器。”
因此,在构造视图时,属性观察器不会触发。但是当一个@State
变量改变时,视图的一个新实例被构建(记住,视图是结构体,或值类型)。因此,didSet
实际上,属性观察器对@State
属性没有用处。
您想要做的是创建一个符合 的类ObservableObject
,并使用@ObservedObject
属性包装器从您的视图中引用它。因为该类存在于结构之外,所以您可以在其属性上设置属性观察器,它们会像您期望的那样触发。
问题 A:如果你让它符合CaseIterable
(见下面的例子),你可以只使用枚举
问题 B:据我所知,这似乎是一个 SwiftUI 错误,因为它发生Picker
在NavigationView
/Form
组合的任何内部。我建议将其报告给 Apple。
以下是我将如何删除枚举和数组的冗余,并将选择保存在 UserDefaults 中:
extension ContentView {
// CaseIterable lets us use .allCases property in ForEach
enum TransmissionType: String, CaseIterable, Identifiable, CustomStringConvertible {
case automatic
case manual
// This lets us omit 'id' parameter in ForEach
var id: TransmissionType {
self
}
// This just capitalizes the first letter for prettier printing
var description: String {
rawValue.prefix(1).uppercased() + rawValue.dropFirst()
}
}
class SelectionModel: ObservableObject {
// Save selected type to UserDefaults on change
@Published var selectedTransmissionType: TransmissionType {
didSet {
UserDefaults.standard.set(selectedTransmissionType.rawValue, forKey: "TransmissionType")
}
}
// Load selected type from UserDefaults on initialization
init() {
if let rawValue = UserDefaults.standard.string(forKey: "TransmissionType") {
if let transmissionType = TransmissionType(rawValue: rawValue) {
self.selectedTransmissionType = transmissionType
return
}
}
// Couldn't load from UserDefaults
self.selectedTransmissionType = .automatic
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后你的观点看起来就像
struct ContentView: View {
@ObservedObject var model = SelectionModel()
var body: some View {
NavigationView {
Form {
Section {
Picker(selection: $model.selectedTransmissionType, label: Text("Transmission Type")) {
ForEach(TransmissionType.allCases) { type in
Text(type.description)
}
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这里有两个问题。
1)如果标题样式为“.inline”,则可以解决跳跃问题。
2) OnReceive() 是didSet
用组合框架方法替换请求的最简单方法之一,这是SwiftUI的核心技术。
struct ContentView: View {
enum TransmissionType: Int {
case automatic
case manual
}
@State private var selectedTransmissionType: Int = TransmissionType.automatic.rawValue {
didSet {
print("selection changed to \(selectedTransmissionType)")
}
}
private let transmissionTypes: [String] = ["Automatic", "Manual"]
var body: some View {
NavigationView {
Form {
Section {
Picker(selection: $selectedTransmissionType,
label: Text("Transmission Type")) {
ForEach(0 ..< transmissionTypes.count) {
Text(self.transmissionTypes[$0])
}
}
}
}.navigationBarTitle("Title", displayMode: .inline) // this solves jumping and all warnings.
}.onReceive(Just(selectedTransmissionType)) { value in
print(value) // Just one step can monitor the @state value.
}
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
4202 次 |
最近记录: |