Перезагрузить ayn c вызов при обновлении состояния просмотра - PullRequest
0 голосов
/ 09 июля 2020

У меня следующее представление:

struct SpriteView: View {
    @Binding var name: String
    @State var sprite: Image = Image(systemName: "exclamationmark")
    
    var body: some View {
        VStack{
            sprite
        }
        .onAppear(perform: loadSprite)
    }
    
    func loadSprite() {
        // async function
        getSpriteFromNetwork(self.name){ result in
            switch result {
            // async callback
            case .success(newSprite):
                self.sprite = newSprite
            }
    }
}

То, что я хочу сделать, довольно просто: пользователь изменяет name в текстовом поле (из родительского представления), которое перезагружает SpriteView с новым спрайт. Но приведенное выше представление не работает, поскольку при перезагрузке представления с новым именем loadSprite больше не вызывается (onAppear срабатывает только при первой загрузке представления). Я также не могу поместить loadSprite в само представление (и вернуть изображение), так как это приведет к бесконечному l oop.

Есть бета-функция onChange, которая именно то, что я ищу, но это только в бета-версии Xcode. Поскольку Combine - это все об асинхронных c обратных вызовах, а SwiftUI и Combine должны хорошо работать вместе, я думал, что такое поведение было бы тривиально реализовать, но у меня было много проблем с этим.

1 Ответ

0 голосов
/ 15 июля 2020

Мне не особенно нравится это решение, поскольку оно требует создания нового ObservableObject, но вот как я это сделал:

class SpriteLoader: ObservableObject {
    @Published var sprite: Image = Image(systemName: "exclamationmark")
    
    func loadSprite(name: String) {
        // async function
        self.sprite = Image(systemName: "arrow.right")
    }
}

struct ParentView: View {
    @State var name: String

    @State var spriteLoader = SpriteLoader()

    var body: some View {
        SpriteView(spriteLoader: spriteLoader)
        TextField(name, text: $name, onCommit: {
            spriteLoader.loadSprite(name: name)
        })
    }
}

struct SpriteView: View {
    @ObservedObject var spriteLoader: SpriteLoader
    
    var body: some View {
        VStack{
            spriteLoader.sprite
        }
    }
}

Старый ответ:

Я думаю, что лучший способ сделать это:

Родительское представление:

struct ParentView: View {
    @State var name: String

    @State spriteView = SpriteView()

    var body: some View {
        spriteView
        TextField(value: $name, onCommit: {
            spriteView.loadSprite(name)
        })
    }

И тогда представлению спрайтов даже не понадобится член @Binding name.

...