我對 Mac OS 上的 Swift UI 有一個非常奇怪的行為。這個想法是我有一個可編輯元素的動態串列(我可以添加、編輯和洗掉)。如果我不關注 TextField,我可以毫無問題地添加/洗掉元素。但是,如果我開始關注 TextField 并使用 瀏覽我的串列 TextField tab,它最終會使我的應用程式崩潰。
為了說明這個問題,我創建了一個小游樂場。
import Foundation
import SwiftUI
import PlaygroundSupport
struct Container {
var lines: [Line]
}
struct Line: Identifiable {
var id = UUID()
var field1: String
var field2: String
var field3: Double = 0
var field4: Double = 0
}
struct ContainerEditor: View {
@State var hidden = false
@State var myContainer = Container(lines: [
Line(field1: "Line1.1", field2: "Line1.2"),
Line(field1: "Line2.1", field2: "Line2.2"),
Line(field1: "Line3.1", field2: "Line3.2"),
Line(field1: "Line4.1", field2: "Line4.2"),
])
var body: some View {
if !hidden {
ContainerView(container: $myContainer) { line in
print("Removing:")
print(line)
myContainer.lines.removeAll(where: { $0.id == line.id })
}
Button("Add line", action: { myContainer.lines.append(Line(field1: "New1", field2: "New2"))})
.buttonStyle(.bordered)
}
Button("Toggle hidden", action: { hidden = !hidden })
}
}
struct ContainerView: View {
var container: Binding<Container>
var onRemove: (_ line: Line) -> Void
var body: some View {
ForEach(container.lines) { line in
LineView(line: line) {
onRemove(line.wrappedValue)
}
}
}
}
struct LineView: View {
var line: Binding<Line>
var onRemove: () -> Void
private var numberFormatter: NumberFormatter {
get {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 4
return formatter
}
}
var body : some View {
HStack {
TextField("field1", text: line.field1).textFieldStyle(.roundedBorder)
TextField("field2", text: line.field2).textFieldStyle(.roundedBorder)
TextField("field3", value: line.field3, formatter: numberFormatter).textFieldStyle(.roundedBorder)
TextField("field4", value: line.field4, formatter: numberFormatter).textFieldStyle(.roundedBorder)
Button("remove") {
print("Remove insider")
onRemove()
}.buttonStyle(.bordered)
}.frame(maxWidth: 300)
}
}
PlaygroundPage.current.setLiveView(ContainerEditor())
該示例以 4 行開頭。
例如,如果我用按鈕洗掉第二行,則聚焦第一行的第一個 TextField 并開始導航,tab當焦點到達最后一個欄位時應用程式崩潰。
玩洗掉行和焦點并在焦點時洗掉,還有其他方法可以使操場崩潰。
我還懷疑TextField與NumberFormatter.
有什么我做錯了嗎?從與動態串列相關的其他執行緒來看,它對我來說看起來不錯。
在我的應用程式中,當它崩潰時,如果出現問題,我不會得到任何跟蹤。
只是Swift/ContiguousArrayBuffer.swift:575: Fatal error: Index out of range
表明這一行:
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
歡迎任何幫助!
更新
因此,我嘗試了一種不同的方法,將 anNSViewRepresentable與一個常規NSTextField與一個協調器一起使用。這是 Xcode mac os 游樂場
import Foundation
import SwiftUI
import AppKit
import PlaygroundSupport
struct Container {
var lines: [Line]
}
struct Line: Identifiable {
var id = UUID()
var field1: String
var field2: String
var field3: Double = 0
var field4: Double = 0
}
struct ContainerEditor: View {
@State var hidden = false
@State var myContainer = Container(lines: [
Line(field1: "Line1.1", field2: "Line1.2"),
Line(field1: "Line2.1", field2: "Line2.2"),
Line(field1: "Line3.1", field2: "Line3.2"),
Line(field1: "Line4.1", field2: "Line4.2"),
])
var body: some View {
if !hidden {
ContainerView(container: $myContainer) { line in
print("Removing:")
print(line)
myContainer.lines.removeAll(where: { $0.id == line.id })
}
Button("Add line", action: { myContainer.lines.append(Line(field1: "New1", field2: "New2"))})
.buttonStyle(.bordered)
}
Button("Toggle hidden", action: { hidden = !hidden })
}
}
struct ContainerView: View {
@Binding var container: Container
var onRemove: (_ line: Line) -> Void
var body: some View {
ForEach($container.lines) { line in
LineView(line: line) {
onRemove(line.wrappedValue)
}
}
}
}
struct LineView: View {
@Binding var line: Line
var onRemove: () -> Void
private var numberFormatter: NumberFormatter {
get {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 4
return formatter
}
}
var body : some View {
HStack {
TextNumberField(value: $line.field3)
TextNumberField(value: $line.field4)
Button("remove") {
print("Remove insider")
onRemove()
}.buttonStyle(.bordered)
}.frame(width: 400)
}
}
struct TextNumberField: NSViewRepresentable {
@Binding var value: Double
var font = NSFont.systemFont(ofSize: 12, weight: .medium)
var onEnter: (() -> Void)? = nil
var initialize: ((NSTextField) -> Void)? = nil
func makeNSView(context: Context) -> NSTextField {
let view = NSTextField()
view.delegate = context.coordinator
view.isEditable = true
let formatter = NumberFormatter()
formatter.hasThousandSeparators = false
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 4
view.formatter = formatter
return view
}
func updateNSView(_ nsView: NSTextField, context: Context) {
nsView.doubleValue = value
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
class Coordinator: NSObject, NSTextFieldDelegate {
var parent: TextNumberField
init(_ parent: TextNumberField) {
self.parent = parent
}
func controlTextDidChange(_ obj: Notification) {
guard let textView = obj.object as? NSTextField else {
return
}
self.parent.value = textView.doubleValue
}
}
}
PlaygroundPage.current.setLiveView(ContainerEditor())
只要我不洗掉任何行,它就可以作業。如果我這樣做了,并嘗試在已洗掉的行之后編輯一行,則應用程式Index out of range在此行崩潰并顯示:
self.parent.value = textView.doubleValue
這就像協調器不再與視圖同步。洗掉線后,確實感覺回圈中存在問題。
uj5u.com熱心網友回復:
我可以用你的步驟復制,我相信這是一個錯誤。
您可以通過使用“新”format而.number不是formatter
TextField("field3", value: $line.field3, format: .number).textFieldStyle(.roundedBorder)
TextField("field4", value: $line.field4, format: .number).textFieldStyle(.roundedBorder)
您應該提交錯誤報告
作業代碼
struct ContainerView: View {
@Binding var container: Container
var onRemove: (_ line: Line) -> Void
var body: some View {
ForEach($container.lines) { $line in
LineView(line: $line) {
onRemove(line)
}
}
}
}
struct LineView: View {
@Binding var line:Line
var onRemove: () -> Void
var body : some View {
HStack {
TextField("field1", text: $line.field1)
TextField("field2", text: $line.field2)
TextField("field3", value: $line.field3, format: .number)
TextField("field4", value: $line.field4, format: .number)
Button("remove") {
print("Remove insider")
onRemove()
}.buttonStyle(.bordered)
}.frame(maxWidth: 300)
.textFieldStyle(.roundedBorder)
}
}
撞線
struct LineView: View {
@Binding var line:Line
var onRemove: () -> Void
var body : some View {
HStack {
TextField("field1", text: $line.field1)
TextField("field2", text: $line.field2)
TextField("field3", value:
Binding(get: {
line.field3 //**Crash at this line**
}, set: { new in
line.field3 = new
})
, formatter: .numberFormatter)
TextField("field4", value:
Binding(get: {
line.field4
}, set: { new in
line.field4 = new
}), formatter: .numberFormatter )
Button("remove") {
print("Remove insider")
onRemove()
}.buttonStyle(.bordered)
}.frame(maxWidth: 300)
.textFieldStyle(.roundedBorder)
}
}
extension Formatter{
static var numberFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 4
return formatter
}()
}
解決方法
這是目前的解決方法。它會影響性能,因為它會強制完全重繪 ,View這樣簡單的您不會看到太多,View但是如果您的視圖變得更長和更復雜,它會減慢一切。
添加.id(myContainer.lines.count)到ContainerView
struct ContainerEditor: View {
@State var hidden = false
@State var myContainer = Container(lines: [
Line(field1: "Line1.1", field2: "Line1.2"),
Line(field1: "Line2.1", field2: "Line2.2"),
Line(field1: "Line3.1", field2: "Line3.2"),
Line(field1: "Line4.1", field2: "Line4.2"),
])
var body: some View {
if !hidden {
ContainerView(container: $myContainer) { line in
print("Removing:")
print(line)
myContainer.lines.removeAll(where: { $0.id == line.id })
}.id(myContainer.lines.count)
}
Button("Toggle hidden", action: { hidden = !hidden })
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/471070.html
