我刚刚开始学习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!
更改细分:
将细分更改回第一位:
任何帮助或建议,将不胜感激。
答案 0 :(得分:3)
Nick Polychronakis在此fork中解决了它: https://github.com/nickpolychronakis/100DaysOfSwiftUI/tree/master/UnitCoverter
解决方案是将.id(:identifier :)添加到选择器中,使其唯一。
可观察的变量:
@State var unit = 0
主选择器:
Picker("Length", selection: $unit) {
ForEach(0 ..< inputUnitTypes.count) {
Text("\(self.inputUnitTypes[$0].description)")
}
}
.pickerStyle(SegmentedPickerStyle())
辅助选择器之一,其内容由unit变量确定。
Picker("Length", selection: $inputUnit) {
ForEach(0 ..< selected.count) {
Text("\(self.selected[$0].description)")
}
}
.id(unit)
答案 1 :(得分:2)
我不确定SwiftUI为何会这样,对我来说似乎是个错误(如果我输入错了,请纠正我)。我只能建议为温度和长度添加单独的选择器,并根据当前选择的类型隐藏这些选择器。为了实现代码的可重用性,我将选择器添加到了另一个文件中。
MyCustomPicker
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())
}
}
ContentView
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)
}
}
}
}
}
}
注意:您必须使用不同的状态变量来跟踪温度和长度选择。
答案 2 :(得分:1)
上述供参考的FYI在SwiftUI中不适用于Wheelpickerstyle。单位数将保持在初始值,因此,如果从“温度”开始然后切换到“长度”,则将丢失“长度”数组的最后两个值。如果您选择其他方式,则您的应用将崩溃,并超出范围。
我花了很长时间才找到解决方案。看来这是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()
}
}
答案 3 :(得分:0)
结合前面的两个答案:
ContentView
...
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)
}
...
UnitPicker
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)
}
}