Próbuję zaimplementować kod wyszukiwania w mojej aplikacji na iPhone'a opartej na CoreData. Nie wiem, jak mam postępować. Aplikacja ma już NSFetchedResultsController z predykatem do pobierania danych dla podstawowego TableView. Chcę się upewnić, że jestem na właściwej ścieżce, zanim zmienię zbyt dużo kodu. Jestem zdezorientowany, ponieważ tak wiele przykładów jest opartych na tablicach, a nie na CoreData.
Oto kilka pytań:
Czy muszę mieć drugi NSFetchedResultsController, który pobiera tylko pasujące elementy, czy mogę użyć tego samego, co podstawowy TableView?
Jeśli użyję tego samego, czy jest to tak proste, jak wyczyszczenie pamięci podręcznej FRC, a następnie zmiana predykatu w metodzie handleSearchForTerm: searchString? Czy predykat musi zawierać początkowy predykat, a także wyszukiwane hasła, czy też pamięta, że używał predykatu w celu pobrania danych w pierwszej kolejności?
Jak wrócić do pierwotnych wyników? Czy po prostu ustawiam predykat wyszukiwania na zero? Czy to nie zabije pierwotnego predykatu, który został użyty do pobrania wyników FRC w pierwszej kolejności?
Jeśli ktoś ma jakieś przykłady kodu używającego wyszukiwania z FRC, byłbym bardzo wdzięczny!
Odpowiedzi:
Właśnie zaimplementowałem to w jednym z moich projektów (Twoje pytanie i druga zła odpowiedź wskazywały, co mam robić). Wypróbowałem odpowiedź Sergio, ale miałem problemy z wyjątkami, gdy faktycznie działałem na urządzeniu.
Tak, tworzysz dwa kontrolery pobierania wyników: jeden do normalnego wyświetlania, a drugi do widoku tabeli UISearchBar.
Jeśli używasz tylko jednego FRC (NSFetchedResultsController), to oryginalny UITableView (nie widok tabeli wyszukiwania, który jest aktywny podczas wyszukiwania) prawdopodobnie będzie miał wywołania zwrotne podczas wyszukiwania i spróbuje nieprawidłowo użyć przefiltrowanej wersji twojego FRC, a zobaczysz wyjątki wyrzucony o nieprawidłowej liczbie sekcji lub wierszy w sekcjach.
Oto, co zrobiłem: mam dwa FRC dostępne jako właściwości pobraneResultsController i searchFetchedResultsController. Nie należy używać elementu searchFetchedResultsController, chyba że istnieje wyszukiwanie (gdy wyszukiwanie zostanie anulowane, poniżej możesz zobaczyć, że ten obiekt został zwolniony). Wszystkie metody UITableView muszą określić, do jakiego widoku tabeli będą kierowane zapytania i z którego odpowiedniego FRC mają pobierać informacje. Metody delegatów FRC muszą również określić, który tableView ma zostać zaktualizowany.
Zaskakujące jest, ile z tego stanowi kod standardowy.
Odpowiednie bity pliku nagłówkowego:
istotne fragmenty pliku implementacji:
Stworzyłem pomocną metodę pobierania poprawnego FRC podczas pracy ze wszystkimi metodami UITableViewDelegate / DataSource:
Metody delegowania dla paska wyszukiwania:
upewnij się, że używasz prawidłowego widoku tabeli podczas pobierania aktualizacji z metod delegata FRC:
Inne informacje dotyczące widoku:
Kod tworzenia FRC:
źródło
searchFetchedResultsController
zanil
każdym razem, gdy zmienia się tekst wyszukiwania.cellForRowAtIndexPath
, czy nie powinieneś dostać komórki odself.tableView
kogoś, kto wskazał w tym pytaniu SO? Jeśli tego nie zrobisz, komórka niestandardowa nie zostanie wyświetlona.Niektórzy komentowali, że można to zrobić za pomocą jednego
NSFetchedResultsController
. Tak właśnie zrobiłem, a oto szczegóły. To rozwiązanie zakłada, że chcesz po prostu przefiltrować tabelę i zachować wszystkie inne aspekty wyników wyszukiwania (porządek sortowania, układ komórek itp.).Najpierw zdefiniuj dwie właściwości w swojej
UITableViewController
podklasie (z odpowiednimi @synthesize i dealloc, jeśli dotyczy):Po drugie, zainicjuj pasek wyszukiwania w
viewDidLoad:
metodzie TwojejUITableViewController
podklasy:Po trzecie, zaimplementuj
UISearchDisplayController
metody delegatów w następujący sposób:Na koniec w
fetchedResultsController
metodzie zmieńNSPredicate
zależność, jeśliself.searchString
jest zdefiniowane:źródło
Zajęło mi kilka prób, aby to zadziałało ...
Moim kluczem do zrozumienia było uświadomienie sobie, że działają tutaj dwa widoki tabeli. Jeden zarządzany przez mój kontroler widoku i jeden zarządzany przez searchviewcontroller, a następnie mogłem przetestować, który jest aktywny i zrobić to, co należy. Dokumentacja też była pomocna:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UISearchDisplayController_Class/Reference/Reference.html
Oto, co zrobiłem -
Dodano flagę searchIsActive:
Dodano syntezę w pliku implementacji.
Następnie dodałem te metody do wyszukiwania:
Następnie w controllerWillChangeContent:
I kontrolerDidChangeContent:
I usuń pamięć podręczną podczas resetowania predykatu.
Mam nadzieję że to pomoże.
źródło
if ( [self.tableView isEqual:self.searchDisplayController.searchResultsTableView] ) { ... }
Stałem przed tym samym zadaniem i znalazłem NAJPROSTSZY SPOSÓB na jego rozwiązanie. W skrócie: musisz zdefiniować jeszcze jedną metodę, bardzo podobną do tej
-fetchedResultsController
w przypadku niestandardowego predykatu złożonego.W moim przypadku mój
-fetchedResultsController
wygląd wygląda tak:Jak widać, pobieram klientów jednej agencji przefiltrowanych według
agency.server_id
predykatu. W rezultacie pobieram zawartość w formacietableView
(wszystkie związane z implementacjątableView
ifetchedResultsController
kodem są dość standardowe). Aby zaimplementowaćsearchField
, definiujęUISearchBarDelegate
metodę delegata. Uruchamiam to metodą wyszukiwania, powiedz-reloadTableView
:i oczywiście definicja
-reloadTableView
:Ten fragment kodu jest bardzo podobny do pierwszego, „standardowego”,
-fetchedResultsController
ALE wewnątrz instrukcji if-else jest:+andPredicateWithSubpredicates:
- za pomocą tej metody możemy ustawić predykat, aby zapisać wyniki naszego głównego pierwszego pobierania w formacietableView
+orPredicateWithSubpredicates
- za pomocą tej metody filtrujemy istniejące pobieranie według zapytania wyszukiwaniasearchBar
Na koniec ustawiam tablicę predykatów jako predykat złożony dla tego konkretnego pobrania. AND dla wymaganych predykatów, LUB dla opcjonalnych.
I to wszystko! Nie musisz niczego więcej wdrażać. Miłego kodowania!
źródło
Czy korzystasz z wyszukiwania na żywo?
Jeśli NIE jesteś, prawdopodobnie chcesz mieć tablicę (lub NSFetchedResultsController) z poprzednimi wyszukiwaniami, których używałeś, kiedy użytkownik naciśnie "szukaj", mówisz FetchedResults, aby zmienił swój predykat.
Tak czy inaczej, za każdym razem będziesz musiał odbudować swoje FetchedResults. Zalecam używanie tylko jednego kontrolera NSFetchedResultsController, ponieważ będziesz musiał dużo powielać kod i nie musisz marnować pamięci na coś, czego nie pokazujesz.
Po prostu upewnij się, że masz zmienną NSString "searchParameters", a metoda FetchedResults odbudowuje ją w razie potrzeby, używając parametrów wyszukiwania, jeśli są dostępne, po prostu wykonaj:
Oto prosty kod:
źródło
Swift 3.0, UISearchController, NSFetchedResultsController i Core Data
Ten kod będzie działał w Swift 3.0 z
Core Data
! Będziesz potrzebować jednej metody delegata i kilku wierszy kodu do filtrowania i wyszukiwania obiektów z modelu. Nic nie będzie potrzebne, jeśli zaimplementowałeś również wszystkie metodyFRC
i ichdelegate
metodysearchController
.Metoda
UISearchResultsUpdating
protokołuOtóż to! Mam nadzieję, że ci to pomoże! Dzięki
źródło
SWIFT 3.0
Użyj textField, UISearchDisplayController jest przestarzały od iOS 8, musisz użyć UISearchController. Zamiast zajmować się kontrolerem wyszukiwania, dlaczego nie tworzysz własnego mechanizmu wyszukiwania? Możesz go bardziej dostosować i mieć nad nim większą kontrolę, bez martwienia się o zmianę i / lub wycofanie SearchController.
Ta metoda, której używam, działa bardzo dobrze i nie wymaga dużo kodu. Wymaga to jednak użycia podstawowych danych i implementacji NSFetchedResultsController.
Najpierw utwórz TextField i zarejestruj go metodą:
Następnie utwórz swoją metodę textFieldDidChange, opisaną w selektorze podczas dodawania celu:
Następnie chcesz przefiltrować listę w
filterList()
metodzie przy użyciu predykatu NSPredicate lub NSCompound, jeśli jest bardziej złożona. W mojej metodzie filterList filtruję na podstawie nazwy jednostki i nazwy obiektu „podkategorie” jednostek (relacja jeden do wielu).źródło
Myślę, że Luka ma do tego lepsze podejście. Zobacz LargeDataSetSample i jego powód
Nie używa
FetchedResultsController
, ale używa pamięci podręcznej podczas wyszukiwania, stąd wyniki wyszukiwania pojawiają się znacznie szybciej, gdy użytkownik wpisze więcej w SearchBarUżyłem jego podejścia w mojej aplikacji i działa dobrze. Pamiętaj również, że jeśli chcesz pracować z obiektem Model, uprość to tak, jak to tylko możliwe, zobacz moją odpowiedź na temat setPropertiesToFetch
źródło
Oto sposób obsługi funkcji fetchedResults z wieloma zestawami danych, który jest zarówno prosty, jak i wystarczająco ogólny, aby można go było zastosować prawie wszędzie. Po prostu przenieś swoje główne wyniki do tablicy, gdy występuje jakiś warunek.
Zapytaj tablicę, przechodząc przez nią w pętli lub cokolwiek chcesz, aby utworzyć podzbiór swoich głównych pobieranych wyników. Teraz możesz użyć pełnego zestawu lub podzbioru, gdy występuje jakiś warunek.
źródło
Bardzo podobało mi się podejście @Josh O'Connor, w którym nie używa
UISearchController
. Ten kontroler nadal (Xcode 9) ma błąd układu, który wielu próbuje obejść.Wróciłem do używania a
UISearchBar
zamiast aUITextField
i działa całkiem nieźle. Moje wymaganie dotyczące wyszukiwania / filtru polega na utworzeniu plikuNSPredicate
. To jest przekazywane do FRC:...
Na koniec połącz SearchBar z jego delegatem.
Mam nadzieję, że to pomoże innym
źródło
Proste podejście do filtrowania istniejącego UITableView przy użyciu CoreData i które jest już posortowane w odpowiedni sposób.
To dosłownie dla mnie 5 minut na skonfigurowanie i rozpoczęcie pracy.
Miałem istniejące
UITableView
użycieCoreData
wypełnione danymi z iCloud i które ma dość skomplikowane interakcje z użytkownikiem i nie chciałem powtarzać tego wszystkiego dla plikuUISearchViewController
. Udało mi się po prostu dodać predykat do istniejącegoFetchRequest
już używanego przezFetchResultsController
i który filtruje już posortowane dane.źródło