Я хотел бы инициализировать значение переменной @ State в SwiftUI с помощью init () метода Struct, чтобы он мог принимать правильный текст из подготовленного словаря для манипуляций в TextField. Исходный код выглядит так:

struct StateFromOutside: View {
    let list = [
        "a": "Letter A",
        "b": "Letter B",
        // ...
    ]
    @State var fullText: String = ""

    init(letter: String) {
        self.fullText = list[letter]!
    }

    var body: some View {
        TextField($fullText)
    }
}

К сожалению, выполнение завершается с ошибкой Поток 1: Неустранимая ошибка: доступ к состоянию вне View.body

Как разрешить ситуацию? Заранее большое спасибо!

Daniel Messner

Ответов: 4

Ответы (4)

Я бы попробовал инициализировать в onAppear.

struct StateFromOutside: View {
    пусть список = [
        "a": "Буква А",
        "b": "Буква B",
        // ...
    ]
    @State var fullText: String = ""

    var body: some View {
        Текстовое поле ($ fullText)
             .onAppear {
                 self.fullText = список [буква]!
             }
    }
}

Или, что еще лучше, используйте объект модели (BindableObject, связанный с вашим представлением) и выполните там всю инициализацию и бизнес-логику. Ваше представление обновится, чтобы отразить изменения автоматически.


Обновление: BindableObject теперь называется ObservableObject.

SwiftUI не позволяет изменить @ State в инициализаторе, но вы можете инициализировать it.

Удалите значение по умолчанию и используйте _fullText, чтобы установить @ State напрямую, вместо того, чтобы проходить через средство доступа оболочки свойства.

@State var fullText: String // No default value of ""

init(letter: String) {
    _fullText = State(initialValue: list[letter]!)
}

См. .id (count) в приведенном ниже примере.

import SwiftUI
import MapKit

struct ContentView: View {
@State private var count = 0

var body: some View {
    Button("Tap me") {
        self.count += 1
        print(count)
    }
    Spacer()
    testView(count: count).id(count) // <------ THIS IS IMPORTANT. Without this "id" the initializer setting affects the testView only once and calling testView again won't change it (not desirable, of course)
}
}



struct testView: View {
var count2: Int
@State private var region: MKCoordinateRegion

init(count: Int) {
    count2 = 2*count
    print("in testView: \(count)")
    
    let lon =  -0.1246402 + Double(count) / 100.0
    let lat =  51.50007773 + Double(count) / 100.0
    let myRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: lat, longitude: lon) , span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
    _region = State(initialValue: myRegion)
}

var body: some View {
    Map(coordinateRegion: $region, interactionModes: MapInteractionModes.all)
    Text("\(count2)")
}
}

Ответ Богдана Фарки верен для этого случая, но мы не можем сказать, что это решение заданного вопроса, потому что я обнаружил, что проблема с текстовым полем в заданном вопросе. Тем не менее, мы можем использовать init для того же кода. Посмотрите на приведенный ниже код, в котором показано точное решение заданного вопроса.

struct StateFromOutside: View {
    let list = [
        "a": "Letter A",
        "b": "Letter B",
        // ...
    ]
    @State var fullText: String = ""

    init(letter: String) {
        self.fullText = list[letter]!
    }

    var body: some View {
        VStack {
            Text("\(self.fullText)")
            TextField("Enter some text", text: $fullText)
        }
    }
}

И используйте это, просто вызвав внутри своего представления

struct ContentView: View {
    var body: some View {
        StateFromOutside(letter: "a")
    }
}

2022 WebDevInsider