【问题标题】:NSPredicateEditorRowTemplate: how to to populate right side popupNSPredicateEditorRowTemplate:如何填充右侧弹出窗口
【发布时间】:2013-08-13 12:30:25
【问题描述】:

我正在尝试生成一个NSPredicateEditorRowTemplate,它的左侧有许多来自实体Foo 的属性名称,其中包括属性bar。当用户选择 'bar' 时,右侧应成为弹出窗口,其中包含 'bar' 的所有值。

如何最好地填充右侧弹出窗口? bar 的所有唯一值都存储在NSMutableArray 中,所以也许我可以在数组更改时使用 KVO 更改行模板。

有没有一种方法可以让我使用代码轻松更改 NSPredicateEditor 行右侧弹出窗口中的值?我可以在 IB 中输入一些静态值,但在这种情况下不行。

编辑

阅读了大量相关问答,包括NSPredicateEditor in 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 出现的方法(至少包含复合行),但在我看来,必须有一种优雅的方式来做到这一点,就像 的意思 完成。但是找不到,有人可以帮我吗?

【问题讨论】:

    标签: cocoa nspredicate nspredicateeditor nsexpression


    【解决方案1】:

    有些人可能会争辩说,意味着的方式是在 Interface Builder 中完成。如果您熟悉行模板的工作方式、它们的组合方式等,那可能会很好。就个人而言,我更喜欢以编程方式创建,因为我可以明确说明行模板的功能。

    让我们通过一个示例来说明这一切如何协同工作,然后您可以决定哪种方法最适合您:

    • 您有一个具有两个NSString 属性的对象:barbaz
    • 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];
    

    当然,这只是为了创建行模板而进行的大量输入。我明白那个。我只是认为它也非常明确且易于调试。话虽如此,您也可以在 Interface Builder 中配置完全相同的东西。为此,请在您的 xib 中添加 NSPredicateEditor,并为其提供 3 行模板。复合行模板您将单独保留,但您将更改为其他两个配置如下:

    对于“bar”模板:

    对于“baz”模板:

    如果您将这些屏幕截图与上面的代码进行比较,应该(希望)很容易看出其中一个是如何映射到另一个的。像这样配置行模板后,IB 还会在您的窗口中显示谓词编辑器:

    如果您想做更高级的事情,例如创建自定义行模板,或使用数据模型中的某些内容填充允许的常量值列表,那么您将不得不放弃在代码中进行配置。


    编辑以回答评论问题

    这些选择器的modifier 位对应于创建的NSComparisonPredicateNSComparisonPredicateModifer。有三个修饰符: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
    

    在这三个示例中,它们都是比较谓词(因为它们有左侧、运算符和右侧),但是“ANY”或“ALL”的使用会影响左侧的解释方式。如果其中任何一个存在,则谓词期望左侧评估为一组可能性。

    使用“直接”选项(或0)基本上意味着您将进行一对一的比较。

    【讨论】:

    • 再次感谢您提供有关 Cocoa 中我非常喜欢但同时又很难理解的其中一个类的好信息。我认为,使用 IB 很难(不可能?)在运行时更改右侧常量。还是有一些我可以使用的绑定?其次,您是否有这些方法中有关modifier: 变量信息的链接?在谷歌甚至 SO 上都找不到任何东西!
    • @markjs 您可以也许找到一种在运行时动态更改 IB 生成的行模板的方法(我想不出一种临时的方法),但是我认为以编程方式制作该行模板会更简单。另外,我在最后添加了一些关于修饰符的内容:)
    • 再次感谢!我现在有一个功能很好的谓词编辑器。我发现的最奇怪的事情之一是确实必须有一个 NSPredicate 与编辑器关联,通过 -setObjectValue:。如果没有,则为空窗口!
    • 如果用户删除最后一个“真实”谓词,复合行也会消失。此时无法添加新行!如果有办法阻止用户使用“删除”按钮,我查看了NSPredicateEditorRowTemplate,但没有运气。我需要自定义模板吗(希望不需要)?
    • @markjs 检查 IB(或setCanRemoveAllRows:)中的“可以删除所有行”复选框。您希望它为“否”,此时如果只剩下一行,编辑器将删除 - 按钮(如果您允许复合,将是复合行)。
    【解决方案2】:

    用复合行(NOT、AND、OR)填充NSPredicateEditor 以及用简单谓词填充一行的关键是NSPredicateEditor 应该收到带有@987654326 的-setRowTemplates: 调用@ 包含复合词和常规谓词。然后,它的谓词应该设置为带有子谓词的复合谓词,使用-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]];
    

    【讨论】:

    【解决方案3】:

    根据 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]
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-08
      • 1970-01-01
      • 1970-01-01
      • 2015-12-31
      • 2017-01-12
      • 1970-01-01
      相关资源
      最近更新 更多