2016-11-29 3 views
0

Я создаю приложение iOS, где хочу, чтобы пользователь мог выполнять поиск по UITableView и отображать результаты на основе различных входных данных. Я использую NSPredicate с несколькими условиями, разделенными «OR», чтобы сделать это, но по какой-то причине он ищет только первое условие и ни одно из других.Проблема с форматированием NSPredicate

Вот как отформатирован мой NSPredicate:

let searchPredicate = NSPredicate(format: "(SELF.firstName CONTAINS[c] 
%@) OR (SELF.lastName CONTAINS[c] %@) OR (SELF.major CONTAINS[c] %@) OR 
(SELF.year CONTAINS[c] %@) OR (SELF.gpa CONTAINS[c] %@)", 
searchController.searchBar.text!) 

Мои параметры ПгвЬЫат, LastName, майор, год, и ГПД. Когда я набираю что-либо помимо первого имени в строке поиска, результаты не отображаются. Но написание первого имени действительно возвращает совпадения. Почему это происходит? Является ли мой NSPredicate некорректным?

ответ

1
if let text = searchController.searchBar.text, text.characters.count > 0{ 
    var predicates = [NSPredicate]() 
    predicates.append(NSPredicate(format: "self.firstName CONTAINS[c]",text)) 
    predicates.append(NSPredicate(format: "self.lastName CONTAINS[c]",text)) 
    predicates.append(NSPredicate(format: "self.major CONTAINS[c]",text)) 
    predicates.append(NSPredicate(format: "self.year CONTAINS[c]",text)) 
    predicates.append(NSPredicate(format: "self.gpa CONTAINS[c]",text)) 

    let searchPredicate:NSPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: predicates) 

    } 
1

У вас есть пять экземпляров спецификатора формата %@ в вашем предикате, поэтому вам нужно передать пять значений. Вы только проходите мимо.

Необходимо повторить searchController.searchBar.text! пять раз. BTW - назначьте это безопасно развернутой переменной и повторите эту переменную пять раз. Это будет меньше печатать.

if let srch = searchController.searchBar.text { 
    let searchPredicate = NSPredicate(format: "(SELF.firstName CONTAINS[c] %@) OR 
     (SELF.lastName CONTAINS[c] %@) OR 
     (SELF.major CONTAINS[c] %@) OR 
     (SELF.year CONTAINS[c] %@) OR 
     (SELF.gpa CONTAINS[c] %@)", srch, srch, srch, srch, srch) 
    ... 
} 

Я на самом деле не пробовал это, но вы можете быть в состоянии сделать это проще, обновляя каждый %@ быть %[email protected]. Тогда вам нужно указать только searchController.searchBar.text! один раз.

let searchPredicate = NSPredicate(format: "(SELF.firstName CONTAINS[c] %[email protected]) OR 
    (SELF.lastName CONTAINS[c] %[email protected]) OR 
    (SELF.major CONTAINS[c] %[email protected]) OR 
    (SELF.year CONTAINS[c] %[email protected]) OR 
    (SELF.gpa CONTAINS[c] %[email protected])", searchController.searchBar.text!) 
+0

NSPredicate не поддерживает позиционные параметры. –

+0

@MartinR спасибо. Я не был уверен в этом. – rmaddy

0

Не тестировался, но это должно работать и, вероятно, самый простой синтаксис для Swift, если все ваши subpredicates одинаковы:

if let text = searchController.searchBar.text { 
    let myCompondPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: ["firstName", "lastName", "major", "year", "gpa"].map { 
     NSPredicate(format: "%K CONTAINS[c] %@", $0, text) 
    }) 

    // Do something with myCompondPredicate here.. 
} 
0

Для простой, статический, предикат, как это, я выбрал бы родной Swift подход:

import Foundation 

struct Student { 
    let firstName: String 
    let lastName: String 
    let major: String 
    let gpa: Double 
} 

extension Student { 
    func anyFields(contain searchString: String) -> Bool { 
     return firstName.contains(searchString) 
      || lastName.contains(searchString) 
      || major.contains(searchString) 
      || String(gpa).contains(searchString) 
    } 

    // Alternate implementation: 
    /* 
    func anyFields(contain searchString: String) -> Bool { 
     return [firstName, lastName, major, String(gpa)].contains(where: { field in 
      field.contains(searchString) 
     }) 
    } */ 
} 


let matchingStudents = students.filter(Student.anyFields(contain:)) 
Смежные вопросы