Странный сбой Swift TextField, у которого может не быть решения? - PullRequest
4 голосов
/ 17 июня 2020

, поэтому у меня есть довольно большой файл, определяющий вид панели поиска. Я просто потратил последние два часа на удаление всего лишнего / ненужного кода из файла. Моя ошибка заключается в том, что, когда я довольно быстро набираю текст в строку поиска, не каждая нажатая клавиша регистрируется, поэтому в итоге получается какой-то беспорядок. Кажется, что чем больше ObservedObjects, переменных состояния, переменных привязки и просто обычных переменных и кода я удаляю, тем быстрее, тем лучше работает текстовое поле.

Сбой, который у меня есть, можно увидеть по этой ссылке: https://youtu.be/42sjhDxSKBw

Для справки, я набрал «Привет, переполнение стека, это тест для быстрого набора текста» ... если я наберу его медленнее, все появится.

В приведенном ниже примере я удалил все переменные, поэтому он работает довольно плавно. Есть ли у кого-нибудь опыт работы с SwiftUI TextFields, демонстрирующий это странное поведение, заключающееся в том, что не регистрируются все ключи, когда много чего происходит? Представление для текстового поля (в его простейшей наиболее разбитой форме, без всех различных переменных и прочего, это следующее):

import SwiftUI
import Mapbox
import MapboxGeocoder

struct SearchBar: View {



var VModel : ViewModel
@Binding var searchedText: String


var body: some View {
    
    let binding = Binding<String>(get: {
        self.searchedText
    }, set: {
        self.searchText = $0
    self.searchedText = self.searchText
    self.VModel.findResults(address: self.searchedText)
    if self.VModel.searchResults.count >= 0 {
        self.showResults = true
        self.showMoreDetails = false
    } else {
        self.showResults = false
    }
    }
    )
    
    
    return VStack {
        HStack {
            TextField("Search", text: binding, onEditingChanged: { isEditing in
                print("we are not editing the text field")
            }, onCommit: {
                print("pressed enter")
                if self.VModel.searchResults.first != nil {
                                self.annotation.addNextAnnotation(address: self.rowText(result: self.VModel.searchResults.first!).label)
                                self.searchedText = "\(self.rowText(result: self.VModel.searchResults.first!).label)"
                            }
            })
        }
        .foregroundColor(Color(.white))
        .background(Color.gray)
    }
}
}

Класс ViewModel выглядит так:

import SwiftUI
import CoreLocation
import Mapbox
import MapboxGeocoder

class ViewModel: ObservableObject {

@ObservedObject var locationManager = LocationManager()
@Published var lat: Double?
@Published var lon: Double?
@Published var location: CLLocationCoordinate2D?
@Published var name: CLPlacemark?
@Published var searchResults: [GeocodedPlacemark] = []

func findResults(address: String) {
    let geocoder = Geocoder(accessToken: "pk.eyJ1Ijoibmlja2JyaW5zbWFkZSIsImEiOiJjazh4Y2dzcW4wbnJyM2ZtY2V1d20yOW4wIn0.LY1H3cf7Uz4BhAUz6JmMww")
    let foptions = ForwardGeocodeOptions(query: address)
    foptions.maximumResultCount = 10
    geocoder.geocode(foptions) { (placemarks, attribution ,error) in
        guard let placemarks = placemarks else {
            return
        }
        self.searchResults = []
        for placemark in placemarks {
            self.searchResults.append(placemark)
        }
    }
}
}

В функции, используемой для отображения результатов поиска, у меня есть следующий блок кода, который использует searchResults:

ForEach(self.VModel.searchResults, id: \.self) { result in
                Button(action: {
                    self.annotation.addNextAnnotation(address: self.rowText(result: result).label)
                    self.showResults = false
                    self.searchedText = self.rowText(result: result).label
                }, label: {
                    self.rowText(result: result).view.font(.system(size: 13))
                    
        }).listRowBackground(Color.gray)
}

1 Ответ

1 голос
/ 18 июня 2020

Попробуйте следующее (не тестировалось, так как env нельзя реплицировать)

import Combine

class ViewModel: ObservableObject {

    @ObservedObject var locationManager = LocationManager()
    @Published var lat: Double?
    @Published var lon: Double?
    @Published var location: CLLocationCoordinate2D?
    @Published var name: CLPlacemark?
    @Published var searchResults: [GeocodedPlacemark] = []

    private let searchValue = CurrentValueSubject<String, Never>("")
    private var cancellable: AnyCancellable?

    func findResults(address: String) {
        if nil == cancellable {
            cancellable = self.searchValue
                .debounce(for: 0.5, scheduler: DispatchQueue.main)
                .flatMap { newValue in
                    Future<[GeocodedPlacemark], Never> { promise in
                        let geocoder = Geocoder(accessToken: "pk.eyJ1Ijoibmlja2JyaW5zbWFkZSIsImEiOiJjazh4Y2dzcW4wbnJyM2ZtY2V1d20yOW4wIn0.LY1H3cf7Uz4BhAUz6JmMww")
                        let foptions = ForwardGeocodeOptions(query: address)
                        foptions.maximumResultCount = 10
                        geocoder.geocode(foptions) { (placemarks, attribution ,error) in
                            guard let placemarks = placemarks else {
                                return
                            }
                            promise(.success(placemarks))
                        }
                    }
                }
                .receive(on: DispatchQueue.main)
                .sink(receiveValue: { placemarks in
                    self.searchResults = placemarks
                })
        }
        self.searchValue.send(address)
    }
}
...