[英]SwiftUI : Picker does not update correctly when changing datasource
我剛開始學習 SwiftUI 並且卡在了某個地方!
我試圖在更改另一個段的值時更改段樣式的選擇器數據源。 但不知何故,它沒有按預期工作。 否則我可能編碼錯誤? 有人能弄清楚嗎?
這是我的一段代碼:
import SwiftUI
struct ContentView: View {
@State var selectedType = 0
@State var inputUnit = 0
@State var outputUnit = 1
let arrTypes = ["Temperature", "Length"]
var arrData: [String] {
switch self.selectedType {
case 0:
return ["Celsius", "Fahrenheit", "Kelvin"] //Temperature
case 1:
return ["meters", "kilometers", "feet", "yards", "miles"] //Length
default:
return ["Celsius", "Fahrenheit", "Kelvin"]
}
}
var body: some View {
NavigationView{
Form
{
Section(header: Text("Choose type"))
{
Picker("Convert", selection: $selectedType) {
ForEach(0 ..< 2, id: \.self)
{ i in
Text(self.arrTypes[i])
}
}
.pickerStyle(SegmentedPickerStyle())
}
Section(header: Text("From"))
{
Picker("", selection: $inputUnit) {
ForEach(0 ..< arrData.count, id: \.self)
{
Text(self.arrData[$0])
}
}
.pickerStyle(SegmentedPickerStyle())
}
Section(header: Text("To"))
{
Picker("", selection: $outputUnit) {
ForEach(0 ..< arrData.count, id: \.self)
{
Text(self.arrData[$0])
}
}
.pickerStyle(SegmentedPickerStyle())
}
}
}
}
}
當我將段從Length
更改回Temperature
時,它會以某種方式合並數組。 我嘗試調試並在日志中打印arrData
計數,然后它打印出正確的結果但不更新 UI!
更改段:
將段改回第一段:
任何幫助或建議將不勝感激。
Nick Polychronakis 在這個分支中解決了它: https://github.com/nickpolychronakis/100DaysOfSwiftUI/tree/master/UnitCoverter
解決方案是將 add.id(:identifier:) 添加到您的選擇器中,使其獨一無二。
可觀察變量:
@State var unit = 0
主要選擇器:
Picker("Length", selection: $unit) {
ForEach(0 ..< inputUnitTypes.count) {
Text("\(self.inputUnitTypes[$0].description)")
}
}
.pickerStyle(SegmentedPickerStyle())
二級選擇器之一,其內容由單元變量確定。
Picker("Length", selection: $inputUnit) {
ForEach(0 ..< selected.count) {
Text("\(self.selected[$0].description)")
}
}
.id(unit)
我不確定為什么 SwiftUI 會這樣,對我來說似乎是一個錯誤(如果我錯了,請糾正我)。 我只能建議為溫度和長度添加單獨的選擇器,並根據當前選擇的類型隱藏它們。 為了代碼可重用性,我已將選擇器添加到另一個文件中。
我的自定義選擇器
struct MyCustomPicker: View {
var pickerData: [String]
@Binding var binding: Int
var body: some View {
Picker("Convert", selection: $binding) {
ForEach(0 ..< pickerData.count, id: \.self)
{ i in
Text(self.pickerData[i])
}
}
.pickerStyle(SegmentedPickerStyle())
}
}
內容視圖
struct ContentView: View {
@State var selectedType = 0
@State var inputTempUnit = 0
@State var outputTempUnit = 1
@State var inputLenUnit = 0
@State var outputLenUnit = 1
let arrTypes = ["Temperature", "Length"]
let tempData = ["Celsius", "Fahrenheit", "Kelvin"]
let lenData = ["meters", "kilometers", "feet", "yards", "miles"]
var body: some View {
NavigationView {
Form {
Section(header: Text("Choose type")) {
MyCustomPicker(pickerData: arrTypes, binding: $selectedType)
}
Section(header: Text("From")) {
if selectedType == 0 {
MyCustomPicker(pickerData: tempData, binding: $inputTempUnit)
} else {
MyCustomPicker(pickerData: lenData, binding: $inputLenUnit)
}
}
Section(header: Text("To")) {
if selectedType == 0 {
MyCustomPicker(pickerData: tempData, binding: $outputTempUnit)
} else {
MyCustomPicker(pickerData: lenData, binding: $outputLenUnit)
}
}
}
}
}
}
注意:您必須使用不同的 state 變量來跟蹤溫度和長度選擇。
結合前面的兩個答案:
內容視圖
...
var units: [String] {
symbols[unitType]
}
...
Section(header: Text("Unit Type")) {
UnitPicker(units: unitTypes, unit: $unitType)
}
Section(header: Text("From Unit")) {
UnitPicker(units: units, unit: $inputUnit)
.id(unitType)
}
Section(header: Text("To Unit")) {
UnitPicker(units: units, unit: $outputUnit)
.id(unitType)
}
...
單位選擇器
struct UnitPicker: View {
var units: [String]
@Binding var unit: Int
var body: some View {
Picker("", selection: $unit) {
ForEach(units.indices, id: \.self) { index in
Text(self.units[index]).tag(index)
}
}
.pickerStyle(SegmentedPickerStyle())
.font(.largeTitle)
}
}
僅供參考,以上答案不適用於 SwiftUI 中的 Wheelpickerstyle。 單位計數將保持在初始值,因此如果您從溫度開始,然后切換到長度,您將丟失長度數組的最后兩個值。 如果您以另一種方式 go ,您的應用程序將因越界而崩潰。
我花了很長時間才找到解決方案。 這似乎是 Wheelpickerstyle 中的一個錯誤。 解決方法是更新選取器的 ID,提示它重新加載所有數據源。 我在下面提供了一個示例。
import SwiftUI
// Data
struct Item: Identifiable {
var id = UUID()
var category:String
var item:String
}
let myCategories:[String] = ["Category 1","Category 2"]
let myItems:[Item] = [
Item(category: "Category 1", item: "Item 1.1"),
Item(category: "Category 1", item: "Item 1.2"),
Item(category: "Category 2", item: "Item 2.1"),
Item(category: "Category 2", item: "Item 2.2"),
Item(category: "Category 2", item: "Item 2.3"),
Item(category: "Category 2", item: "Item 2.4"),
]
// Factory
class MyObject: ObservableObject {
// Category picker variables
@Published var selectedCategory:String = myCategories[0]
@Published var selectedCategoryItems:[Item] = []
@Published var selectedCategoryInt:Int = 0 {
didSet {
selectCategoryActions(selectedCategoryInt)
}
}
// Item picker variables
@Published var selectedItem:Item = myItems[0]
@Published var selectedItemInt:Int = 0 {
didSet {
selectedItem = selectedCategoryItems[selectedItemInt]
}
}
@Published var pickerId:Int = 0
// Initial category selection
init() {
selectCategoryActions(selectedCategoryInt)
}
// Actions when selecting a new category
func selectCategoryActions(_ selectedCategoryInt:Int) {
selectedCategory = myCategories[selectedCategoryInt]
// Get items in category
selectedCategoryItems = myItems.filter{ $0.category.contains(selectedCategory)}
// Select initial item in category
let selectedItemIntWrapped:Int? = myItems.firstIndex { $0.category == selectedCategory }
if let selectedItemInt = selectedItemIntWrapped {
self.selectedItem = myItems[selectedItemInt]
}
self.pickerId += 1 // Hack to change ID of picker. ID is updated to force refresh
}
}
// View
struct ContentView: View {
@ObservedObject var myObject = MyObject()
var body: some View {
VStack(spacing: 10) {
Section(header: Text("Observable Object")) {
Text("Selected category: \(myObject.selectedCategory)")
Text("Items in category: \(myObject.selectedCategoryItems.count)")
Text("PickerId updated to force refresh \(myObject.pickerId)")
Text("Selected item: \(myObject.selectedItem.item)")
Picker(selection: self.$myObject.selectedCategoryInt, label: Text("Select category")) {
ForEach(0 ..< myCategories.count, id: \.self) {
Text("\(myCategories[$0])")
}
}.labelsHidden()
Picker(selection: self.$myObject.selectedItemInt, label: Text("Select object item")) {
ForEach(0 ..< self.myObject.selectedCategoryItems.count, id: \.self) {
Text("\(self.myObject.selectedCategoryItems[$0].item)")
}
}
.labelsHidden()
.id(myObject.pickerId) // Hack to get picker to reload data. ID is updated to force refresh.
}
Spacer()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
只需在 ForEach (Picker) 中將標簽設置為您的文本。
.tag(String?.some(item))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.