在从Obj-C到Swift的转换中实现“如果对象不存在则执行此操作”

时间:2015-07-05 03:04:31

标签: objective-c swift optional

我正在将一些代码转换为Swift,我遇到了一个问题,似乎与展开一个可选项有关,所以我可以实现这个模式:

如果对象存在,请使用它。如果没有创建它。

我也想知道我对NSPredicate的使用。

这是有效的Obj-C代码:

UILabel *label = [[[cell.contentView subviews] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UIView *subview, NSDictionary *bindings) {
    return ([subview isKindOfClass:[UILabel class]]);
}]] firstObject];

if (!label) {
    label = [[UILabel alloc] initWithFrame:cell.bounds];
    label.font = [UIFont systemFontOfSize:10.f];
    label.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    label.textAlignment = NSTextAlignmentCenter;
    label.backgroundColor = [UIColor clearColor];
    [cell.contentView addSubview:label];
}

这是我在Swift中修改的代码:

    let subViews = cell.contentView.subviews
    let labelPredicate = NSPredicate { (UIView subview, _) -> Bool in
        return subview.isKindOfClass(UILabel) }

    let labels = (subViews as NSArray).filteredArrayUsingPredicate(labelPredicate) as! [UILabel]
    let label = labels[0]

    if (!label) {
        label = UILabel(frame: view.bounds)
        label.font = UIFont.systemFontOfSize( 10)
        label.autoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth
        label.textAlignment = NSTextAlignment.Center
        label.backgroundColor = UIColor.clearColor()
        cell.contentView.addSubview(label)

    }

if(!label)行出现问题,xcode错误为“找不到'!''的重载接受提供的参数。“

如何正确测试标签是否存在?

感谢。

2 个答案:

答案 0 :(得分:2)

有几点想法:

  1. 问题的标题是如何在Swift中使用谓词。坦率地说,我会考虑使用filter方法,它完全取消了NSPredicate

    let labels = subViews.filter { $0 is UILabel }
    
  2. 即使您想使用NSPredicate,我也建议您简化语法:

    let labelPredicate = NSPredicate { (subview, _) -> Bool in subview is UILabel }
    
  3. 关于if (!label)语法,等效的Swift语法将是

    if label == nil { ... }
    

    你不能在Swift中使用任何随机类型的!运算符。明确测试以查看可选项是否为nil

  4. 顺便说一句,构建标签数组以查看是否有标签的过程效率有点低。您可以在不使用reduce构建数组的情况下测试标签是否存在:

    let hasLabel = subViews.reduce(false) { alreadyFoundLabel, subview in alreadyFoundLabel || subview is UILabel }
    if !hasLabel { ... }
    

    或者,从程序上讲,您可以循环浏览子视图:

    var hasLabel = false
    for control in subViews {
        println(control)
        if control is UILabel {
            hasLabel = true
            break;
        }
    }
    if !hasLabel { ... }
    

    显然,如果你需要一系列标签用于其他目的,那么一定要构建它。但是,仅仅为了检查某些东西的存在而构建一个数组效率很低。

答案 1 :(得分:2)

正如您在原始问题中所写,您在Swift中提交了语法错误。条件条款不在括号内。

正如我们的评论所写,Swift抱怨是因为它无法将UILabel与nil与!=运算符进行比较。

在过滤数组后使用as! [UILabel]时,保证Swift labels数组包含UILabel。然后在下一行中,您将labels[0]分配给label。通过询问索引0处的成员,你保证Swift确实会有一个成员在索引0。所以最后,当你试图检查label是否为零时,Swift说"它不能是的,因为你告诉过我。"它知道label不能为零,并且拒绝比较。

有很多方法可以改善这一点,但最简单的方法是改变:

let labels = // ...
let label = labels[0]
if label == nil {
    // ...
}

let labels = // ...
let label: UILabel                // Declare label, but do not assign in yet

if let lbl = labels.first {
    label = lbl                   // Assign the UILabel in index 0
} else {
    label = UILabel(frame: view.bounds)    // Create a new UILabel
    // ...
}

// Do whatever with label

在此示例中,我们声明label将是UILabel,但尚未为其分配任何内容。我们将其声明在if let / else的范围之外,以便之后可用。在我们分配之前,斯威夫特不会让我们做任何事情,但那没关系。

接下来,我们检查labels索引0中是否有标签。如果是,请将其分配给label。如果没有,我们将创建一个新标签并进行设置。

在我们将UILabel分配给标签后,您可以按计划继续自由继续。

我强烈建议您阅读OptionalsOptional Chaining,因为它们是Swift的核心,并且会对您的代码结构产生巨大影响。