NSPredicateEditorRowTemplate: как заполнить всплывающее окно справа
Я пытаюсь создать NSPredicateEditorRowTemplate
что на левой стороне есть несколько имен свойств из сущности Foo, среди которых есть панель свойств. Когда пользователь выбирает "бар", правая сторона должна стать всплывающим, который содержит все значения "бар".
Как я могу лучше всего заполнить всплывающее окно с правой стороны? Все уникальные значения бара хранятся в NSMutableArray
так что, возможно, я смогу использовать KVO для изменения шаблона строки при изменении массива.
Есть ли способ, которым я могу использовать код, чтобы легко изменить значения в правой части всплывающего окна в NSPredicateEditor
строка? Я могу ввести несколько статических значений в IB, но в этой ситуации этого не произойдет.
РЕДАКТИРОВАТЬ
Прочитав множество связанных вопросов и ответов, включая NSPredicateEditor в Xcode 4 и превосходный ответ на него @Dave DeLong, я думаю, что большую часть работы можно выполнить следующим образом:
NSArray *leftexp = @[[NSExpression expressionForKeyPath:@"name"],[NSExpression expressionForKeyPath:@"married"]];
NSArray *rightexp = @[[NSExpression expressionWithFormat:@"One"],[NSExpression expressionWithFormat:@"Two"]];
NSPredicateEditorRowTemplate *template = [[NSPredicateEditorRowTemplate alloc] initWithLeftExpressions:leftexp rightExpressions:rightexp modifier:NSDirectPredicateModifier operators:@[@(NSEqualToPredicateOperatorType)] options:0];
NSPredicateEditorRowTemplate *compound = [[NSPredicateEditorRowTemplate alloc] initWithCompoundTypes:@[@(NSOrPredicateType)]];
[self.predicateEditor setRowTemplates:@[template,compound]];
NSEntityDescription *description = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:self.managedObjectContext];
self.predicate = [NSPredicate predicateWithFormat:@"name == ''"];
Я видел несколько способов заставить основной NSPredicateEditor появляться (по крайней мере, с составной линией), но мне кажется, что должен быть элегантный способ сделать это, так, как это было задумано. Хотя не могу найти это, кто-нибудь может мне помочь с этим?
3 ответа
Некоторые, вероятно, утверждают, что способ, которым это должно быть сделано, находится в Интерфейсном Разработчике Это, вероятно, было бы хорошо, если вы знакомы с тем, как работают шаблоны строк, как они комбинируются и т. Д. Лично я предпочитаю создавать программы программно, потому что я могу четко представлять, как работают шаблоны строк.
Давайте рассмотрим пример того, как все это работает вместе, а затем вы можете решить, какой подход работает лучше для вас:
- У вас есть объект с двумя
NSString
свойства:bar
а такжеbaz
bar
может иметь только определенное количество возможных значений, которые мы будем называть@"Bar1"
,@"Bar2"
, а также@"Bar3"
,baz
может быть любымNSString
значение.
Имея это в виду, вот как вы могли бы создать это программно:
// these defines are for brevity on the web page; please don't actually do this
#define NSPERT NSPredicateEditorRowTemplate
#define KP(_kp) [NSExpression expressionForKeyPath:(_kp)]
#define CV(_cv) [NSExpression expressionForConstantValue:(_cv)]
NSPERT *compound = [[NSPERT alloc] initWithCompoundTypes:@[@(NSAndPredicateType),
@(NSOrPredicateType),
@(NSNotPredicateType)]];
NSPERT *bar = [[NSPERT alloc] initWithLeftExpressions:@[KP(@"bar")]
rightExpressions:@[CV(@"Bar1"), CV(@"Bar2"), CV(@"Bar3")]
modifier:NSDirectPredicateModifier
operators:@[@(NSEqualToPredicateOperatorType),
@(NSNotEqualToPredicateOperatorType)]
options:0];
NSPERT *baz = [[NSPERT alloc] initWithLeftExpressions:@[KP(@"baz")]
rightExpressionAttributeType:NSStringAttributeType
modifier:NSDirectPredicateModifier
operators:@[@(NSEqualToPredicateOperatorType),
@(NSNotEqualToPredicateOperatorType)]
options:0];
NSArray *templates = @[compound, bar, baz];
[self.myPredicateEditor setRowTemplates:templates];
Конечно, это много печатать только для создания шаблонов строк. Я это понимаю. Я просто думаю, что это также очень явно и легко отлаживается. При этом, вы также можете настроить точно такую же вещь в Интерфейсном Разработчике. Для этого положить NSPredicateEditor
в вашей XIB, и дать ему 3 строки шаблонов. Шаблон строки соединения вы бы оставили в покое, а два других вы бы изменили, чтобы настроить так:
Для " bar
"шаблон:
Для " baz
"шаблон:
Если вы сравните эти снимки экрана с приведенным выше кодом, то (надеюсь) будет легко увидеть, как одно сопоставляется с другим. С шаблонами строк, настроенными следующим образом, IB также показывает редактор предикатов в вашем окне:
Если вы хотите сделать более сложные вещи, такие как создание пользовательских шаблонов строк или заполнение списка допустимых постоянных значений чем-то из вашей модели данных, то вам придется перейти к настройке в коде.
Изменить, чтобы ответить на вопрос комментария
modifier
Бит этих селекторов соответствует NSComparisonPredicateModifer
для созданного NSComparisonPredicate
, Существует три модификатора: Direct, Any и All.
@"bar = 'Bar1'";
^ No modifier means this is NSDirectPredicateModifier
@"ANY user.userName = 'markjs'";
^~~ use of "ANY" keyword means this is NSAnyPredicateModifier
@"ALL user.age < 42";
^~~ use of "ALL" keyword means this is NSAllPredicateModifier
В этих трех примерах все они являются предикатами сравнения (потому что у них есть левая сторона, оператор и правая сторона), но использование "ЛЮБОГО" или "ВСЕГО" влияет на интерпретацию левой стороны. Если какой-либо из них присутствует, то предикат ожидает, что левая часть оценивает набор возможностей.
Используя опцию "direct" (или 0
) в основном означает, что вы собираетесь делать сравнение один к одному.
Ключ к заполнению NSPredicateEditor
с составной строкой (NOT, AND, OR), а также одной строкой с простым предикатом, является то, что NSPredicateEditor
должен получить -setRowTemplates:
позвонить с NSArray
который содержит как составной, так и обычный предикат. Затем его предикат должен быть установлен на составной предикат с субпредикатом, используя -andPredicateWithSubpredicates:
,
Это помогло мне:
NSArray *leftexp = @[[NSExpression expressionForKeyPath:@"name"]];
NSArray *rightexp = @[[NSExpression expressionForConstantValue:@"A"],[NSExpression expressionForConstantValue:@"B"],[NSExpression expressionForConstantValue:@"C"]];
NSPredicateEditorRowTemplate *template = [[NSPredicateEditorRowTemplate alloc] \
initWithLeftExpressions:leftexp
rightExpressions:rightexp
modifier:NSDirectPredicateModifier
operators:@[@(NSEqualToPredicateOperatorType)]
options:0];
NSPredicateEditorRowTemplate *compound = [[NSPredicateEditorRowTemplate alloc] initWithCompoundTypes:@[@(NSAndPredicateType),@(NSOrPredicateType),@(NSNotPredicateType)]];
NSMutableArray *templates = [NSMutableArray arrayWithObject:compound];
[templates addObject:template];
[self.predicateEditor setRowTemplates:templates];
NSPredicate *p = [NSPredicate predicateWithFormat:@"name == 'A'"];
self.predicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObject:p]];
Основываясь на ответе markjs, я перевел его в swift 4 и поместил в расширение NSPredicateEditorRowTemplate.
extension NSPredicateEditorRowTemplate {
convenience init( compoundTypes: [NSCompoundPredicate.LogicalType] ) {
var compoundTypesNSNumber: [NSNumber] = []
for c in compoundTypes { compoundTypesNSNumber.append( NSNumber(value: c.rawValue) ) }
self.init( compoundTypes: compoundTypesNSNumber )
}
convenience init( forKeyPath keyPath: String, withValues values: [Any] ) {
let keyPaths: [NSExpression] = [NSExpression(forKeyPath: keyPath)]
var constantValues: [NSExpression] = []
for v in values {
constantValues.append( NSExpression(forConstantValue: v) )
}
let operators: [NSComparisonPredicate.Operator] = [.equalTo, .notEqualTo]
var operatorsNSNumber: [NSNumber] = []
for o in operators { operatorsNSNumber.append( NSNumber(value: o.rawValue) ) }
self.init( leftExpressions: keyPaths,
rightExpressions: constantValues,
modifier: .direct,
operators: operatorsNSNumber,
options: (Int(NSComparisonPredicate.Options.caseInsensitive.rawValue | NSComparisonPredicate.Options.diacriticInsensitive.rawValue)) )
}
convenience init( stringCompareForKeyPaths keyPaths: [String] ) {
var leftExpressions: [NSExpression] = []
for keyPath in keyPaths {
leftExpressions.append( NSExpression(forKeyPath: keyPath) )
}
let operators: [NSComparisonPredicate.Operator] = [.contains, .equalTo, .notEqualTo, .beginsWith, .endsWith]
var operatorsNSNumber: [NSNumber] = []
for o in operators { operatorsNSNumber.append( NSNumber(value: o.rawValue) ) }
self.init( leftExpressions: leftExpressions,
rightExpressionAttributeType: .stringAttributeType,
modifier: .direct,
operators: operatorsNSNumber,
options: (Int(NSComparisonPredicate.Options.caseInsensitive.rawValue | NSComparisonPredicate.Options.diacriticInsensitive.rawValue)) )
}
convenience init( IntCompareForKeyPaths keyPaths: [String] ) {
var leftExpressions: [NSExpression] = []
for keyPath in keyPaths {
leftExpressions.append( NSExpression(forKeyPath: keyPath) )
}
let operators: [NSComparisonPredicate.Operator] = [.equalTo, .notEqualTo, .greaterThan, .greaterThanOrEqualTo, .lessThan, .lessThanOrEqualTo]
var operatorsNSNumber: [NSNumber] = []
for o in operators { operatorsNSNumber.append( NSNumber(value: o.rawValue) ) }
self.init( leftExpressions: leftExpressions,
rightExpressionAttributeType: .integer16AttributeType,
modifier: .direct,
operators: operatorsNSNumber,
options: 0 )
}
}
Использует это следующим образом:
func setMyPetFilter() {
// We assume that myPredicateEditor is create/set in IB
// Remove whatever was configured in IB
myPredicateEditor.rowTemplates.removeAll()
let templateCompoundTypes = NSPredicateEditorRowTemplate( compoundTypes: [.and, .or, .not] )
let template1 = NSPredicateEditorRowTemplate( forKeyPath: "Animals", withValues: ["Dog", "Cat", "Hamster", "Canary"] )
let template2 = NSPredicateEditorRowTemplate( stringCompareForKeyPaths: ["PetName", "OwnerName"] )
let template3 = NSPredicateEditorRowTemplate( forKeyPath: "Legs", withValues: [2,4] )
let template4 = NSPredicateEditorRowTemplate( IntCompareForKeyPaths: ["Age", "AgeOwner"] )
myPredicateEditor.rowTemplates = [templateCompoundTypes, template1, template2, template3, template4]
}